veloren_world/
canvas.rs

1use crate::{
2    block::{ZCache, block_from_structure},
3    column::{ColumnGen, ColumnSample},
4    index::IndexRef,
5    land::Land,
6    sim::{SimChunk, WorldSim},
7    util::{Grid, Sampler, seed_expan},
8};
9use common::{
10    calendar::Calendar,
11    generation::EntityInfo,
12    spot::Spot,
13    terrain::{
14        Block, BlockKind, SpriteCfg, Structure, TerrainChunk, TerrainChunkSize,
15        structure::StructureBlock,
16    },
17    vol::{ReadVol, RectVolSize, WriteVol},
18};
19use rand::prelude::*;
20use rand_chacha::ChaChaRng;
21use std::{borrow::Cow, ops::Deref};
22use vek::*;
23
24#[derive(Copy, Clone)]
25pub struct CanvasInfo<'a> {
26    pub(crate) chunk_pos: Vec2<i32>,
27    pub(crate) wpos: Vec2<i32>,
28    pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
29    pub(crate) column_grid_border: i32,
30    pub(crate) chunks: &'a WorldSim,
31    pub(crate) index: IndexRef<'a>,
32    pub(crate) chunk: &'a SimChunk,
33    pub(crate) calendar: Option<&'a Calendar>,
34}
35
36impl<'a> CanvasInfo<'a> {
37    pub fn calendar(&self) -> Option<&'a Calendar> { self.calendar }
38
39    pub fn wpos(&self) -> Vec2<i32> { self.wpos }
40
41    pub fn area(&self) -> Aabr<i32> {
42        Rect::from((
43            self.wpos(),
44            Extent2::from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32)),
45        ))
46        .into()
47    }
48
49    pub fn col(&self, wpos: Vec2<i32>) -> Option<&'a ColumnSample> {
50        self.column_grid
51            .get(self.column_grid_border + wpos - self.wpos())
52            .and_then(Option::as_ref)
53            .map(|zc| &zc.sample)
54    }
55
56    /// Attempt to get the data for the given column, generating it if we don't
57    /// have it.
58    ///
59    /// This function does not (currently) cache generated columns.
60    pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
61        self.col(wpos).map(Cow::Borrowed).or_else(|| {
62            Some(Cow::Owned(ColumnGen::new(self.chunks()).get((
63                wpos,
64                self.index(),
65                self.calendar,
66            ))?))
67        })
68    }
69
70    /// Find all spots within range of this canvas's chunk. Returns `(wpos,
71    /// spot, seed)`.
72    pub fn nearby_spots(&self) -> impl Iterator<Item = (Vec2<i32>, Spot, u32)> + '_ {
73        (-1..2)
74            .flat_map(|x| (-1..2).map(move |y| Vec2::new(x, y)))
75            .filter_map(move |pos| {
76                let pos = self.chunk_pos + pos;
77                self.chunks.get(pos).and_then(|c| c.spot).map(|spot| {
78                    let wpos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| {
79                        e * sz as i32 + sz as i32 / 2
80                    });
81                    // TODO: Very dumb, not this.
82                    let seed = pos.x as u32 | (pos.y as u32).wrapping_shl(16);
83
84                    (wpos, spot, seed ^ 0xA801D82E)
85                })
86            })
87    }
88
89    pub fn index(&self) -> IndexRef<'a> { self.index }
90
91    pub fn chunk(&self) -> &'a SimChunk { self.chunk }
92
93    pub fn chunks(&self) -> &'a WorldSim { self.chunks }
94
95    pub fn land(&self) -> Land<'_> { Land::from_sim(self.chunks) }
96
97    pub fn with_mock_canvas_info<A, F: for<'b> FnOnce(&CanvasInfo<'b>) -> A>(
98        index: IndexRef<'a>,
99        sim: &'a WorldSim,
100        f: F,
101    ) -> A {
102        let zcache_grid = Grid::populate_from(Vec2::broadcast(1), |_| None);
103        let sim_chunk = SimChunk {
104            chaos: 0.0,
105            alt: 0.0,
106            basement: 0.0,
107            water_alt: 0.0,
108            downhill: None,
109            flux: 0.0,
110            temp: 0.0,
111            humidity: 0.0,
112            rockiness: 0.0,
113            tree_density: 0.0,
114            forest_kind: crate::all::ForestKind::Palm,
115            spawn_rate: 0.0,
116            river: Default::default(),
117            surface_veg: 0.0,
118            sites: Vec::new(),
119            place: None,
120            poi: None,
121            path: Default::default(),
122            cliff_height: 0.0,
123            contains_waypoint: false,
124            spot: None,
125        };
126        f(&CanvasInfo {
127            chunk_pos: Vec2::zero(),
128            wpos: Vec2::zero(),
129            column_grid: &zcache_grid,
130            column_grid_border: 0,
131            chunks: sim,
132            index,
133            chunk: &sim_chunk,
134            calendar: None,
135        })
136    }
137}
138
139pub struct Canvas<'a> {
140    pub(crate) info: CanvasInfo<'a>,
141    pub(crate) chunk: &'a mut TerrainChunk,
142    pub(crate) entities: Vec<EntityInfo>,
143    pub(crate) rtsim_resource_blocks: Vec<Vec3<i32>>,
144}
145
146impl<'a> Canvas<'a> {
147    /// The borrow checker complains at immutable features of canvas (column
148    /// sampling, etc.) being used at the same time as mutable features
149    /// (writing blocks). To avoid this, this method extracts the
150    /// inner `CanvasInfo` such that it may be used independently.
151    pub fn info(&mut self) -> CanvasInfo<'a> { self.info }
152
153    pub fn get(&self, pos: Vec3<i32>) -> Block {
154        self.chunk
155            .get(pos - self.wpos())
156            .ok()
157            .copied()
158            .unwrap_or_else(Block::empty)
159    }
160
161    /// Set a block in the canvas chunk. Note that if the block is a resource
162    /// (see [`Block::get_rtsim_resource`]) then [`Canvas::map_resource`] should
163    /// be used instead.
164    pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
165        if block.get_rtsim_resource().is_some() {
166            self.rtsim_resource_blocks.push(pos);
167        }
168        let _ = self.chunk.set(pos - self.wpos(), block);
169    }
170
171    /// Map a block in the canvas chunk. Note that if the block is a resource
172    /// (see [`Block::get_rtsim_resource`]) then [`Canvas::map_resource`] should
173    /// be used instead.
174    pub fn map(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
175        let _ = self.chunk.map(pos - self.wpos(), f);
176    }
177
178    /// Like [`Canvas::map`], except allows marking resource-containing blocks
179    /// appropriately.
180    pub fn map_resource(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
181        let _ = self.chunk.map(pos - self.wpos(), |b| {
182            let new_block = f(b);
183            if new_block.get_rtsim_resource().is_some() {
184                self.rtsim_resource_blocks.push(pos);
185            }
186            new_block
187        });
188    }
189
190    /// Like [`Canvas::map`], except allows marking resource-containing blocks
191    /// appropriately and allows setting sprite_cfg.
192    pub fn map_resource_with_cfg(
193        &mut self,
194        pos: Vec3<i32>,
195        f: impl FnOnce(Block, &mut Option<SpriteCfg>) -> Block,
196    ) {
197        let mut sprite_cfg = None;
198        self.map_resource(pos, |block| f(block, &mut sprite_cfg));
199        if let Some(sprite_cfg) = sprite_cfg {
200            self.set_sprite_cfg(pos, sprite_cfg);
201        }
202    }
203
204    pub fn set_sprite_cfg(&mut self, pos: Vec3<i32>, sprite_cfg: SpriteCfg) {
205        let rpos = pos - self.wpos();
206        self.chunk.meta_mut().set_sprite_cfg_at(rpos, sprite_cfg);
207    }
208
209    pub fn foreach_col_area(
210        &mut self,
211        aabr: Aabr<i32>,
212        mut f: impl FnMut(&mut Self, Vec2<i32>, &ColumnSample),
213    ) {
214        let chunk_aabr = Aabr {
215            min: self.wpos(),
216            max: self.wpos() + Vec2::from(self.area().size()),
217        };
218
219        for y in chunk_aabr.min.y.max(aabr.min.y)..chunk_aabr.max.y.min(aabr.max.y) {
220            for x in chunk_aabr.min.x.max(aabr.min.x)..chunk_aabr.max.x.min(aabr.max.x) {
221                let wpos2d = Vec2::new(x, y);
222                let info = self.info;
223                let col = if let Some(col) = info.col(wpos2d) {
224                    col
225                } else {
226                    return;
227                };
228                f(self, wpos2d, col);
229            }
230        }
231    }
232
233    /// Execute an operation upon each column in this canvas.
234    pub fn foreach_col(&mut self, f: impl FnMut(&mut Self, Vec2<i32>, &ColumnSample)) {
235        self.foreach_col_area(
236            Aabr {
237                min: Vec2::broadcast(i32::MIN),
238                max: Vec2::broadcast(i32::MAX),
239            },
240            f,
241        );
242    }
243
244    /// Blit a structure on to the canvas at the given position.
245    ///
246    /// Note that this function should be called with identical parameters by
247    /// all chunks within the bounds of the structure to avoid cut-offs
248    /// occurring at chunk borders. Deterministic RNG is advised!
249    pub fn blit_structure(
250        &mut self,
251        origin: Vec3<i32>,
252        structure: &Structure,
253        seed: u32,
254        units: Vec2<Vec2<i32>>,
255        with_snow: bool,
256    ) {
257        let mut entities: Vec<(Vec3<f32>, String)> = Vec::new();
258        let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
259        let info = self.info();
260        self.foreach_col(|canvas, wpos2d, col| {
261            let rpos2d = wpos2d - origin.xy();
262            let rpos2d = units.x * rpos2d.x + units.y * rpos2d.y;
263
264            let mut above = true;
265            for z in (structure.get_bounds().min.z..structure.get_bounds().max.z).rev() {
266                if let Ok(sblock) = structure.get(rpos2d.with_z(z)) {
267                    let mut add_snow = false;
268                    let mut new_sprite_cfg = None;
269                    let wpos = wpos2d.with_z(origin.z + z);
270                    canvas.map(wpos, |block| {
271                        if let Some((new_block, sprite_cfg)) = block_from_structure(
272                            info.index,
273                            sblock,
274                            wpos2d.with_z(origin.z + z),
275                            origin.xy(),
276                            seed,
277                            col,
278                            |sprite| block.with_sprite(sprite),
279                            info.calendar,
280                            &units,
281                        ) {
282                            new_sprite_cfg = sprite_cfg;
283                            if !new_block.is_air() {
284                                if with_snow && col.snow_cover && above {
285                                    add_snow = true;
286                                }
287                                above = false;
288                            }
289                            new_block
290                        } else {
291                            block
292                        }
293                    });
294
295                    // Add sprite configuration for those that need it
296                    if let Some(sprite_cfg) = new_sprite_cfg {
297                        canvas.set_sprite_cfg(wpos, sprite_cfg);
298                    }
299
300                    // Spawn NPCs at the pos of this block
301                    if let StructureBlock::EntitySpawner(spec, spawn_chance) = sblock {
302                        if rng.gen::<f32>() < *spawn_chance {
303                            entities.push((
304                                wpos2d.with_z(origin.z + z).map(|e| e as f32)
305                                    + Vec3::new(0.5, 0.5, 0.0),
306                                spec.clone(),
307                            ));
308                        }
309                    }
310
311                    if add_snow {
312                        canvas.set(
313                            wpos2d.with_z(origin.z + z + 1),
314                            Block::new(BlockKind::Snow, Rgb::new(210, 210, 255)),
315                        );
316                    }
317                }
318            }
319        });
320        for (pos, spec) in entities.drain(..) {
321            self.spawn(EntityInfo::at(pos).with_asset_expect(&spec, &mut rng, None));
322        }
323    }
324
325    pub fn find_spawn_pos(&self, wpos: Vec3<i32>) -> Option<Vec3<i32>> {
326        let height = 2;
327        let search_dist: i32 = 8;
328
329        (1..search_dist * 2 + 1)
330            .rev()
331            .map(|z| wpos.z + if z % 2 != 0 { z / 2 } else { -(z / 2) })
332            .find(|&z| {
333                self.get(wpos.xy().with_z(z - 1)).is_solid()
334                    && (0..height).all(|z_offs| self.get(wpos.xy().with_z(z + z_offs)).is_fluid())
335            })
336            .map(|z| wpos.xy().with_z(z))
337    }
338
339    pub fn spawn(&mut self, entity: EntityInfo) { self.entities.push(entity); }
340}
341
342impl<'a> Deref for Canvas<'a> {
343    type Target = CanvasInfo<'a>;
344
345    fn deref(&self) -> &Self::Target { &self.info }
346}