Skip to main content

fyrox_impl/scene/terrain/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Everything related to terrains. See [`Terrain`] docs for more info.
22
23use crate::{
24    asset::{Resource, ResourceDataRef},
25    core::{
26        algebra::{Matrix4, Point3, Vector2, Vector3, Vector4},
27        arrayvec::ArrayVec,
28        log::Log,
29        math::{aabb::AxisAlignedBoundingBox, ray::Ray, ray_rect_intersection, Rect},
30        parking_lot::Mutex,
31        pool::Handle,
32        reflect::prelude::*,
33        type_traits::prelude::*,
34        uuid::{uuid, Uuid},
35        variable::InheritableVariable,
36        visitor::prelude::*,
37        SafeLock,
38    },
39    graphics::ElementRange,
40    material::MaterialResourceExtension,
41    material::{Material, MaterialProperty, MaterialResource},
42    renderer::{
43        self,
44        bundle::{RenderContext, SurfaceInstanceData},
45    },
46    resource::texture::{
47        Texture, TextureDataRefMut, TextureKind, TextureMagnificationFilter,
48        TextureMinificationFilter, TexturePixelKind, TextureResource, TextureResourceExtension,
49        TextureWrapMode,
50    },
51    scene::node::RdcControlFlow,
52    scene::{
53        base::{Base, BaseBuilder},
54        debug::SceneDrawingContext,
55        graph::Graph,
56        mesh::RenderPath,
57        node::{Node, NodeTrait},
58        terrain::{geometry::TerrainGeometry, quadtree::QuadTree},
59        Scene,
60    },
61};
62use fxhash::FxHashMap;
63use fyrox_core::{uuid_provider, warn};
64use fyrox_graph::SceneGraph;
65use fyrox_resource::untyped::ResourceKind;
66use half::f16;
67use image::{imageops::FilterType, ImageBuffer, Luma};
68use std::{
69    cell::Cell,
70    cmp::Ordering,
71    collections::HashMap,
72    ops::{Deref, DerefMut, Range},
73    sync::LazyLock,
74};
75
76pub mod brushstroke;
77mod geometry;
78mod quadtree;
79
80use crate::scene::node::constructor::NodeConstructor;
81pub use brushstroke::*;
82use fyrox_graph::constructor::ConstructorProvider;
83
84use super::collider::BitMask;
85
86/// Current implementation version marker.
87pub const VERSION: u8 = 0;
88
89/// WHITE_1X1 TextureResource.
90pub static WHITE_1X1: LazyLock<TextureResource> = LazyLock::new(|| {
91    TextureResource::from_bytes(
92        uuid!("09a71013-ccb2-4a41-a48a-ab6c80f14f0e"),
93        TextureKind::Rectangle {
94            width: 1,
95            height: 1,
96        },
97        TexturePixelKind::R8,
98        vec![255],
99        ResourceKind::External,
100    )
101    .unwrap()
102});
103
104/// Position of a single cell within terrain data.
105#[derive(Debug, Clone)]
106pub struct TerrainRect {
107    /// The pixel coordinates of the cell.
108    pub grid_position: Vector2<i32>,
109    /// The local 2D bounds of the cell.
110    pub bounds: Rect<f32>,
111}
112
113impl TerrainRect {
114    /// Calculate the cell which contains the given local 2D coordinates when cells have the given size.
115    /// It is assumed that the (0,0) cell has its origin at local 2D point (0.0, 0.0).
116    pub fn from_local(position: Vector2<f32>, cell_size: Vector2<f32>) -> TerrainRect {
117        let cell_pos = Vector2::new(position.x / cell_size.x, position.y / cell_size.y);
118        let cell_pos = cell_pos.map(f32::floor);
119        let min = Vector2::new(cell_pos.x * cell_size.x, cell_pos.y * cell_size.y);
120        TerrainRect {
121            grid_position: cell_pos.map(|x| x as i32),
122            bounds: Rect::new(min.x, min.y, cell_size.x, cell_size.y),
123        }
124    }
125}
126
127/// A 2D-array interface to the height map data of a chunk.
128/// This interface is aware of the one-pixel margin around the edges
129/// of the height map data, so valid x-coordinates are in the range -1..=width
130/// and y-coordinates are in the range -1..=height.
131/// (0,0) is the actual origin of the chunk, while (-1,-1) is the in the margin of the chunk.
132pub struct ChunkHeightData<'a>(pub ResourceDataRef<'a, Texture>);
133/// A mutable 2D-array interface to the height map data of a chunk.
134/// This interface is aware of the one-pixel margin around the edges
135/// of the height map data, so valid x-coordinates are in the range -1..=width.
136/// (0,0) is the actual origin of the chunk, while (-1,-1) is the in the margin of the chunk.
137pub struct ChunkHeightMutData<'a>(pub TextureDataRefMut<'a>);
138
139impl ChunkHeightData<'_> {
140    /// The size of the hight map, excluding the margins
141    pub fn size(&self) -> Vector2<u32> {
142        match self.0.kind() {
143            TextureKind::Rectangle { width, height } => Vector2::new(width - 2, height - 2),
144            _ => panic!("Invalid texture kind."),
145        }
146    }
147    /// The length of each horizontal row in the underlying texture.
148    pub fn row_size(&self) -> usize {
149        match self.0.kind() {
150            TextureKind::Rectangle { width, .. } => width as usize,
151            _ => panic!("Invalid texture kind."),
152        }
153    }
154    /// Get the value at the given position, if possible.
155    pub fn get(&self, position: Vector2<i32>) -> Option<f32> {
156        if self.is_valid_index(position) {
157            Some(self[position])
158        } else {
159            None
160        }
161    }
162    #[inline]
163    fn is_valid_index(&self, position: Vector2<i32>) -> bool {
164        let s = self.size();
165        (-1..=s.x as i32).contains(&position.x) && (-1..=s.y as i32).contains(&position.y)
166    }
167}
168
169impl ChunkHeightMutData<'_> {
170    /// The size of the hight map, excluding the margins
171    pub fn size(&self) -> Vector2<u32> {
172        match self.0.kind() {
173            TextureKind::Rectangle { width, height } => Vector2::new(width - 2, height - 2),
174            _ => panic!("Invalid texture kind."),
175        }
176    }
177    /// The length of each horizontal row in the underlying texture.
178    pub fn row_size(&self) -> usize {
179        match self.0.kind() {
180            TextureKind::Rectangle { width, .. } => width as usize,
181            _ => panic!("Invalid texture kind."),
182        }
183    }
184    /// Get the value at the given position, if possible.
185    pub fn get(&self, position: Vector2<i32>) -> Option<f32> {
186        if self.is_valid_index(position) {
187            Some(self[position])
188        } else {
189            None
190        }
191    }
192    /// Get the value at the given position, if possible.
193    pub fn get_mut(&mut self, position: Vector2<i32>) -> Option<&mut f32> {
194        if self.is_valid_index(position) {
195            Some(&mut self[position])
196        } else {
197            None
198        }
199    }
200    #[inline]
201    fn is_valid_index(&self, position: Vector2<i32>) -> bool {
202        let s = self.size();
203        (-1..=s.x as i32).contains(&position.x) && (-1..=s.y as i32).contains(&position.y)
204    }
205}
206
207impl std::ops::Index<Vector2<i32>> for ChunkHeightData<'_> {
208    type Output = f32;
209
210    fn index(&self, position: Vector2<i32>) -> &Self::Output {
211        assert!(self.is_valid_index(position));
212        let row_size = self.row_size();
213        let x = (position.x + 1) as usize;
214        let y = (position.y + 1) as usize;
215        match self.0.data_of_type::<f32>() {
216            Some(d) => &d[y * row_size + x],
217            None => panic!("Height data type error: {:?}", self.0),
218        }
219    }
220}
221impl std::ops::Index<Vector2<i32>> for ChunkHeightMutData<'_> {
222    type Output = f32;
223
224    fn index(&self, position: Vector2<i32>) -> &Self::Output {
225        assert!(self.is_valid_index(position));
226        let row_size = self.row_size();
227        let x = (position.x + 1) as usize;
228        let y = (position.y + 1) as usize;
229        &self.0.data_of_type::<f32>().unwrap()[y * row_size + x]
230    }
231}
232impl std::ops::IndexMut<Vector2<i32>> for ChunkHeightMutData<'_> {
233    fn index_mut(&mut self, position: Vector2<i32>) -> &mut Self::Output {
234        assert!(self.is_valid_index(position));
235        let row_size = self.row_size();
236        let x = (position.x + 1) as usize;
237        let y = (position.y + 1) as usize;
238        &mut self.0.data_mut_of_type::<f32>().unwrap()[y * row_size + x]
239    }
240}
241
242/// Layers is a material Terrain can have as many layers as you want, but each layer slightly decreases
243/// performance, so keep amount of layers on reasonable level (1 - 5 should be enough for most
244/// cases).
245#[derive(Debug, Clone, Visit, Reflect, PartialEq)]
246pub struct Layer {
247    /// Material of the layer.
248    pub material: MaterialResource,
249
250    /// Name of the mask sampler property in the material.
251    pub mask_property_name: String,
252
253    /// Name of the height map sampler property in the material.
254    #[visit(optional)]
255    pub height_map_property_name: String,
256
257    /// Name of the hole mask sampler property in the material.
258    #[visit(optional)]
259    pub hole_mask_property_name: String,
260
261    /// Name of the node uv offsets property in the material.
262    #[visit(optional)]
263    pub node_uv_offsets_property_name: String,
264}
265
266uuid_provider!(Layer = "7439d5fd-43a9-45f0-bd7c-76cf4d2ec22e");
267
268impl Default for Layer {
269    fn default() -> Self {
270        Self {
271            material: MaterialResource::new_ok(
272                Uuid::new_v4(),
273                Default::default(),
274                Material::standard_terrain(),
275            ),
276            mask_property_name: "maskTexture".to_string(),
277            height_map_property_name: "heightMapTexture".to_string(),
278            node_uv_offsets_property_name: "nodeUvOffsets".to_string(),
279            hole_mask_property_name: "holeMaskTexture".to_string(),
280        }
281    }
282}
283
284/// Extract the &[f32] from a TextureResource to create a QuadTree, or panic.
285fn make_quad_tree(
286    texture: &Option<TextureResource>,
287    height_map_size: Vector2<u32>,
288    block_size: Vector2<u32>,
289) -> QuadTree {
290    let texture = texture.as_ref().unwrap().data_ref();
291    let height_mod_count = texture.modifications_count();
292    let height_map = texture.data_of_type::<f32>().unwrap();
293    QuadTree::new(height_map, height_map_size, block_size, height_mod_count)
294}
295
296/// Create an Ok texture resource of the given size from the given height values.
297/// `height_map` should have exactly `size.x * size.y` elements.
298/// Returns None if the wrong number of height values are given to fill a height map
299/// of the given size.
300fn make_height_map_texture_internal(
301    height_map: Vec<f32>,
302    size: Vector2<u32>,
303) -> Option<TextureResource> {
304    let mut data = Texture::from_bytes(
305        TextureKind::Rectangle {
306            width: size.x,
307            height: size.y,
308        },
309        TexturePixelKind::R32F,
310        crate::core::transmute_vec_as_bytes(height_map),
311    )?;
312
313    data.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
314    data.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
315
316    Some(Resource::new_ok(Uuid::new_v4(), Default::default(), data))
317}
318
319fn make_blank_hole_texture(size: Vector2<u32>) -> TextureResource {
320    make_hole_texture(vec![255; size.x as usize * size.y as usize], size)
321}
322
323fn make_hole_texture(mask_data: Vec<u8>, size: Vector2<u32>) -> TextureResource {
324    let mut data = Texture::from_bytes(
325        TextureKind::Rectangle {
326            width: size.x,
327            height: size.y,
328        },
329        TexturePixelKind::R8,
330        mask_data,
331    )
332    .unwrap();
333    data.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
334    data.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
335    data.set_magnification_filter(TextureMagnificationFilter::Nearest);
336    data.set_minification_filter(TextureMinificationFilter::Nearest);
337    Resource::new_ok(Uuid::new_v4(), Default::default(), data)
338}
339
340/// Create an Ok texture resource of the given size from the given height values.
341/// `height_map` should have exactly `size.x * size.y` elements.
342/// **Panics** if the wrong number of height values are given to fill a height map
343/// of the given size.
344fn make_height_map_texture(height_map: Vec<f32>, size: Vector2<u32>) -> TextureResource {
345    make_height_map_texture_internal(height_map, size).unwrap()
346}
347
348/// Chunk is smaller block of a terrain. Terrain can have as many chunks as you need, which always arranged in a
349/// grid. You can add chunks from any side of a terrain. Chunks could be considered as a "sub-terrain", which could
350/// use its own set of materials for layers. This could be useful for different biomes, to prevent high amount of
351/// layers which could harm the performance.
352#[derive(Debug, Reflect)]
353pub struct Chunk {
354    #[reflect(hidden)]
355    quad_tree: Mutex<QuadTree>,
356    /// Height map of the chunk. You can assign a custom height map image here. Keep in mind, that
357    /// only Red channel will be used! The assigned texture will be automatically converted to internal
358    /// format suitable for terrain needs.
359    #[reflect(setter = "set_height_map")]
360    heightmap: Option<TextureResource>,
361    #[reflect(hidden)]
362    hole_mask: Option<TextureResource>,
363    #[reflect(hidden)]
364    position: Vector3<f32>,
365    #[reflect(hidden)]
366    physical_size: Vector2<f32>,
367    #[reflect(hidden)]
368    height_map_size: Vector2<u32>,
369    #[reflect(hidden)]
370    block_size: Vector2<u32>,
371    #[reflect(hidden)]
372    grid_position: Vector2<i32>,
373    /// Layer blending masks of the chunk.
374    #[reflect(hidden)]
375    pub layer_masks: Vec<TextureResource>,
376    #[reflect(hidden)]
377    height_map_modifications_count: u64,
378}
379
380uuid_provider!(Chunk = "ae996754-69c1-49ba-9c17-a7bd4be072a9");
381
382impl PartialEq for Chunk {
383    fn eq(&self, other: &Self) -> bool {
384        self.heightmap == other.heightmap
385            && self.height_map_size == other.height_map_size
386            && self.grid_position == other.grid_position
387            && self.layer_masks == other.layer_masks
388    }
389}
390
391impl Clone for Chunk {
392    // Deep cloning.
393    fn clone(&self) -> Self {
394        Self {
395            heightmap: Some(self.heightmap.as_ref().unwrap().deep_clone()),
396            hole_mask: self.hole_mask.as_ref().map(Resource::deep_clone),
397            position: self.position,
398            physical_size: self.physical_size,
399            height_map_size: self.height_map_size,
400            block_size: self.block_size,
401            grid_position: self.grid_position,
402            layer_masks: self
403                .layer_masks
404                .iter()
405                .map(|m| m.deep_clone())
406                .collect::<Vec<_>>(),
407            quad_tree: Mutex::new(make_quad_tree(
408                &self.heightmap,
409                self.height_map_size,
410                self.block_size,
411            )),
412            height_map_modifications_count: self.height_map_modifications_count,
413        }
414    }
415}
416
417// Manual implementation of the trait because we need to serialize heightmap differently.
418impl Visit for Chunk {
419    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
420        let mut region = visitor.enter_region(name)?;
421
422        let mut version = VERSION;
423        version.visit("Version", &mut region)?;
424
425        if let VERSION = version {
426            self.heightmap.visit("Heightmap", &mut region)?;
427            self.hole_mask.visit("HoleMask", &mut region)?;
428            // We do not need to visit position, since its value is implied by grid_position.
429            //self.position.visit("Position", &mut region)?;
430            self.physical_size.visit("PhysicalSize", &mut region)?;
431            self.height_map_size.visit("HeightMapSize", &mut region)?;
432            self.layer_masks.visit("LayerMasks", &mut region)?;
433            self.grid_position.visit("GridPosition", &mut region)?;
434            // Set position to have the value implied by grid_position
435            if region.is_reading() {
436                self.position = self.position()
437            }
438            self.block_size.visit("BlockSize", &mut region)?;
439        }
440
441        self.quad_tree = Mutex::new(make_quad_tree(
442            &self.heightmap,
443            self.height_map_size,
444            self.block_size,
445        ));
446
447        Ok(())
448    }
449}
450
451impl Default for Chunk {
452    fn default() -> Self {
453        Self {
454            quad_tree: Default::default(),
455            heightmap: Default::default(),
456            hole_mask: Default::default(),
457            position: Default::default(),
458            physical_size: Default::default(),
459            height_map_size: Default::default(),
460            block_size: Vector2::new(32, 32),
461            grid_position: Default::default(),
462            layer_masks: Default::default(),
463            height_map_modifications_count: 0,
464        }
465    }
466}
467
468impl Chunk {
469    /// Return a view of the height data as a 2D array of f32.
470    pub fn height_data(&self) -> ChunkHeightData {
471        ChunkHeightData(self.heightmap.as_ref().map(|r| r.data_ref()).unwrap())
472    }
473
474    /// Modify the height texture of the chunk to give it a one pixel margin around all four edges.
475    /// The [`Chunk::height_map_size`] is increased to match. The margin is initialized to zero.
476    pub fn create_margin(&mut self) {
477        let data = self.heightmap.as_ref().map(|r| r.data_ref()).unwrap();
478        let size = match data.kind() {
479            TextureKind::Rectangle { width, height } => Vector2::new(width, height),
480            _ => panic!("Texture is not rectangle"),
481        };
482        let data_f32 = From::<&[f32]>::from(data.data_of_type().unwrap());
483        let result = create_zero_margin(data_f32, size);
484        drop(data);
485        self.heightmap = Some(make_height_map_texture(result, size.map(|x| x + 2)));
486        self.height_map_size = self.height_map_size.map(|x| x + 2);
487    }
488    /// Check the heightmap for modifications and update data as necessary.
489    pub fn update(&self) {
490        let Some(heightmap) = self.heightmap.as_ref() else {
491            return;
492        };
493        let count = heightmap.data_ref().modifications_count();
494        let mut quad_tree = self.quad_tree.safe_lock();
495        if count != quad_tree.height_mod_count() {
496            *quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
497        }
498    }
499    /// Returns position of the chunk in local 2D coordinates relative to origin of the
500    /// terrain.
501    pub fn local_position(&self) -> Vector2<f32> {
502        map_to_local(self.position())
503    }
504
505    /// The position of the chunk within the terrain based on its `grid_position` and `physical_size`.
506    pub fn position(&self) -> Vector3<f32> {
507        Vector3::new(
508            self.grid_position.x as f32 * self.physical_size.x,
509            0.0,
510            self.grid_position.y as f32 * self.physical_size.y,
511        )
512    }
513
514    /// The 2D position of the chunk within the chunk array.
515    #[inline]
516    pub fn grid_position(&self) -> Vector2<i32> {
517        self.grid_position
518    }
519
520    /// Returns a reference to height map.
521    pub fn heightmap(&self) -> &TextureResource {
522        self.heightmap.as_ref().unwrap()
523    }
524
525    /// Sets new height map to the chunk.
526    /// Tries to create a copy of the given texture and convert the copy into [R32F](TexturePixelKind::R32F) format.
527    /// If the conversion is successful, the resulting texture becomes the source for height data of this chunk
528    /// and the new texture is returned.
529    /// If the conversion fails, the argument texture is returned in its original format and the chunk is not modified.
530    ///
531    /// Failure can happen if:
532    /// * The given texture is None.
533    /// * The given texture is not in the [Ok state](crate::asset::state::ResourceState::Ok).
534    /// * The given texture is not [TextureKind::Rectangle].
535    /// * The width or height is incorrect due to not matching [height_map_size](Self::height_map_size).
536    /// * The texture's format is not one of the many formats that this method is capable of converting as identified by its [Texture::pixel_kind].
537    pub fn set_height_map(
538        &mut self,
539        height_map: Option<TextureResource>,
540    ) -> Option<TextureResource> {
541        if let Some(new_height_map) = height_map {
542            let mut state = new_height_map.state();
543            if let Some(new_height_map_texture) = state.data() {
544                if let TextureKind::Rectangle { width, height } = new_height_map_texture.kind() {
545                    if width == self.height_map_size.x && height == self.height_map_size.y {
546                        fn convert<T, C>(texture: &Texture, mut mapper: C) -> Option<Vec<f32>>
547                        where
548                            T: Sized,
549                            C: Fn(&T) -> f32,
550                        {
551                            texture
552                                .mip_level_data_of_type::<T>(0)
553                                .map(|v| v.iter().map(&mut mapper).collect::<Vec<_>>())
554                        }
555
556                        // Try to convert Red component of pixels to R32F format.
557                        let pixels = match new_height_map_texture.pixel_kind() {
558                            TexturePixelKind::R8 | TexturePixelKind::Luminance8 => {
559                                convert::<u8, _>(new_height_map_texture, |v| {
560                                    *v as f32 / u8::MAX as f32
561                                })
562                            }
563                            TexturePixelKind::RGB8 => {
564                                #[repr(C)]
565                                struct Rgb8 {
566                                    r: u8,
567                                    g: u8,
568                                    b: u8,
569                                }
570                                convert::<Rgb8, _>(new_height_map_texture, |v| {
571                                    v.r as f32 / u8::MAX as f32
572                                })
573                            }
574                            TexturePixelKind::RGBA8 => {
575                                #[repr(C)]
576                                struct Rgba8 {
577                                    r: u8,
578                                    g: u8,
579                                    b: u8,
580                                    a: u8,
581                                }
582                                convert::<Rgba8, _>(new_height_map_texture, |v| {
583                                    v.r as f32 / u8::MAX as f32
584                                })
585                            }
586                            TexturePixelKind::RG8 | TexturePixelKind::LuminanceAlpha8 => {
587                                #[repr(C)]
588                                struct Rg8 {
589                                    r: u8,
590                                    g: u8,
591                                }
592                                convert::<Rg8, _>(new_height_map_texture, |v| {
593                                    v.r as f32 / u8::MAX as f32
594                                })
595                            }
596                            TexturePixelKind::R16 | TexturePixelKind::Luminance16 => {
597                                convert::<u16, _>(new_height_map_texture, |v| {
598                                    *v as f32 / u16::MAX as f32
599                                })
600                            }
601                            TexturePixelKind::RG16 | TexturePixelKind::LuminanceAlpha16 => {
602                                #[repr(C)]
603                                struct Rg16 {
604                                    r: u16,
605                                    g: u16,
606                                }
607                                convert::<Rg16, _>(new_height_map_texture, |v| {
608                                    v.r as f32 / u16::MAX as f32
609                                })
610                            }
611                            TexturePixelKind::BGR8 => {
612                                #[repr(C)]
613                                struct Bgr8 {
614                                    b: u8,
615                                    g: u8,
616                                    r: u8,
617                                }
618                                convert::<Bgr8, _>(new_height_map_texture, |v| {
619                                    v.r as f32 / u8::MAX as f32
620                                })
621                            }
622                            TexturePixelKind::BGRA8 => {
623                                #[repr(C)]
624                                struct Bgra8 {
625                                    r: u8,
626                                    g: u8,
627                                    b: u8,
628                                    a: u8,
629                                }
630                                convert::<Bgra8, _>(new_height_map_texture, |v| {
631                                    v.r as f32 / u8::MAX as f32
632                                })
633                            }
634                            TexturePixelKind::RGB16 => {
635                                #[repr(C)]
636                                struct Rgb16 {
637                                    r: u16,
638                                    g: u16,
639                                    b: u16,
640                                }
641                                convert::<Rgb16, _>(new_height_map_texture, |v| {
642                                    v.r as f32 / u16::MAX as f32
643                                })
644                            }
645                            TexturePixelKind::RGBA16 => {
646                                #[repr(C)]
647                                struct Rgba16 {
648                                    r: u16,
649                                    g: u16,
650                                    b: u16,
651                                    a: u16,
652                                }
653                                convert::<Rgba16, _>(new_height_map_texture, |v| {
654                                    v.r as f32 / u16::MAX as f32
655                                })
656                            }
657                            TexturePixelKind::RGB32F => {
658                                #[repr(C)]
659                                struct Rgb32F {
660                                    r: f32,
661                                    g: f32,
662                                    b: f32,
663                                }
664                                convert::<Rgb32F, _>(new_height_map_texture, |v| v.r)
665                            }
666                            TexturePixelKind::RGBA32F => {
667                                #[repr(C)]
668                                struct Rgba32F {
669                                    r: f32,
670                                    g: f32,
671                                    b: f32,
672                                    a: f32,
673                                }
674                                convert::<Rgba32F, _>(new_height_map_texture, |v| v.r)
675                            }
676                            TexturePixelKind::RGB16F => {
677                                #[repr(C)]
678                                struct Rgb16F {
679                                    r: f16,
680                                    g: f16,
681                                    b: f16,
682                                }
683                                convert::<Rgb16F, _>(new_height_map_texture, |v| v.r.to_f32())
684                            }
685                            TexturePixelKind::R32F => {
686                                convert::<f32, _>(new_height_map_texture, |v| *v)
687                            }
688                            TexturePixelKind::R16F => {
689                                convert::<f16, _>(new_height_map_texture, |v| v.to_f32())
690                            }
691                            _ => None,
692                        };
693
694                        if let Some(pixels) = pixels {
695                            if let Some(texture) =
696                                make_height_map_texture_internal(pixels, self.height_map_size)
697                            {
698                                let prev_texture = self.heightmap.replace(texture);
699                                self.update_quad_tree();
700                                return prev_texture;
701                            }
702                        } else {
703                            warn!(
704                                "Unable to convert input texture into single-channel height map!\
705                            Input texture format is {:?}.",
706                                new_height_map_texture.pixel_kind()
707                            )
708                        }
709                    } else {
710                        warn!(
711                            "The size of the texture must match the height map size! \
712                        Input texture size: {width}x{height}, but the height map size is \
713                        {}x{}",
714                            self.height_map_size.x, self.height_map_size.y
715                        );
716                    }
717                } else {
718                    warn!(
719                        "Height map can be set only from 2D textures! The input texture is {:?}",
720                        new_height_map_texture.kind()
721                    )
722                }
723            } else {
724                warn!("The input texture is in invalid state (unloaded)!")
725            }
726        }
727
728        // In case of any error, ignore the new value and return current height map.
729        self.heightmap.clone()
730    }
731
732    /// Returns the height map of the terrain as an array of `f32`s.
733    pub fn heightmap_owned(&self) -> Vec<f32> {
734        self.heightmap
735            .as_ref()
736            .unwrap()
737            .data_ref()
738            .data_of_type::<f32>()
739            .unwrap()
740            .to_vec()
741    }
742
743    /// Replaces the current height map with a new one. New height map must be equal with size of current.
744    pub fn replace_height_map(
745        &mut self,
746        heightmap: TextureResource,
747    ) -> Result<(), TextureResource> {
748        let data = heightmap.data_ref();
749        if let TextureKind::Rectangle { width, height } = data.kind() {
750            if data.pixel_kind() == TexturePixelKind::R32F
751                && self.height_map_size.x == width
752                && self.height_map_size.y == height
753            {
754                drop(data);
755                self.heightmap = Some(heightmap);
756                self.update_quad_tree();
757                return Ok(());
758            }
759        }
760        drop(data);
761        Err(heightmap)
762    }
763
764    /// Returns a reference to hole mask texture, if one exists.
765    pub fn hole_mask(&self) -> Option<&TextureResource> {
766        self.hole_mask.as_ref()
767    }
768
769    /// Returns the size of the chunk in meters.
770    pub fn physical_size(&self) -> Vector2<f32> {
771        self.physical_size
772    }
773
774    /// Returns amount of pixels in the height map along each dimension.
775    pub fn height_map_size(&self) -> Vector2<u32> {
776        self.height_map_size
777    }
778
779    /// Returns amount of pixels in the hole mask along each dimension.
780    pub fn hole_mask_size(&self) -> Vector2<u32> {
781        self.height_map_size.map(|x| x - 3)
782    }
783
784    /// Performs debug drawing of the chunk. It draws internal quad-tree structure for debugging purposes.
785    pub fn debug_draw(&self, transform: &Matrix4<f32>, ctx: &mut SceneDrawingContext) {
786        let transform = *transform * Matrix4::new_translation(&self.position());
787
788        self.quad_tree.safe_lock().debug_draw(
789            &transform,
790            self.height_map_size,
791            self.physical_size,
792            ctx,
793        )
794    }
795
796    fn set_block_size(&mut self, block_size: Vector2<u32>) {
797        self.block_size = block_size;
798        self.update_quad_tree();
799    }
800
801    /// Recalculates the quad tree for this chunk.
802    pub fn update_quad_tree(&self) {
803        if self.heightmap.is_none() {
804            return;
805        }
806        *self.quad_tree.safe_lock() =
807            make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
808    }
809}
810
811fn map_to_local(v: Vector3<f32>) -> Vector2<f32> {
812    // Terrain is a XZ oriented surface so we can map X -> X, Z -> Y
813    Vector2::new(v.x, v.z)
814}
815
816/// Ray-terrain intersection result.
817#[derive(Debug)]
818pub struct TerrainRayCastResult {
819    /// World-space position of impact point.
820    pub position: Vector3<f32>,
821    /// Height value at the intersection point (this value could be interpolated between four neighbour pixels
822    /// of a height map).
823    pub height: f32,
824    /// World-space normal of triangle at impact point.
825    pub normal: Vector3<f32>,
826    /// Index of a chunk that was hit.
827    pub chunk_index: usize,
828    /// Time of impact. Usually in [0; 1] range where 0 - origin of a ray, 1 - its end.
829    pub toi: f32,
830}
831
832/// An object representing the state of a terrain brush being used from code.
833/// It has methods for starting, stopping, stamping, and smearing.
834///
835/// Each BrushContext requires some amount of heap allocation, so it may be preferable
836/// to reuse a BrushContext for multiple strokes when possible.
837///
838/// A single brush stroke can include multiple operations across multiple frames, but
839/// the terrain's texture resources should not be replaced during a stroke because
840/// the BrushContext holds references the the texture resources that the terrain
841/// had when the stroke started, and any brush operations will be applied to those
842/// textures regardless of replacing the textures in the terrain.
843#[derive(Default)]
844pub struct BrushContext {
845    /// Parameter value for the brush. For flattening, this is the target height.
846    /// For flattening, it starts as None and then is given a value based on the first
847    /// stamp or smear.
848    pub value: Option<f32>,
849    /// The pixel and brush data of the in-progress stroke.
850    pub stroke: BrushStroke,
851}
852
853impl BrushContext {
854    /// The current brush. This is immutable access only, because
855    /// the brush's target may only be changed through [BrushContext::start_stroke].
856    ///
857    /// Mutable access to the brush's other properties is available through
858    /// [BrushContext::shape], [BrushContext::mode], [BrushContext::hardness],
859    /// and [BrushContext::alpha].
860    pub fn brush(&self) -> &Brush {
861        self.stroke.brush()
862    }
863    /// Mutable access to the brush's shape. This allows the shape of the brush
864    /// to change without starting a new stroke.
865    pub fn shape(&mut self) -> &mut BrushShape {
866        self.stroke.shape()
867    }
868    /// Mutable access to the brush's mode. This allows the mode of the brush
869    /// to change without starting a new stroke.
870    pub fn mode(&mut self) -> &mut BrushMode {
871        self.stroke.mode()
872    }
873    /// Mutable access to the brush's hardness. This allows the hardness of the brush
874    /// to change without starting a new stroke.
875    pub fn hardness(&mut self) -> &mut f32 {
876        self.stroke.hardness()
877    }
878    /// Mutable access to the brush's alpha. This allows the alpha of the brush
879    /// to change without starting a new stroke.
880    pub fn alpha(&mut self) -> &mut f32 {
881        self.stroke.alpha()
882    }
883    /// Modify the given BrushStroke so that it is using the given Brush and it is modifying the given terrain.
884    /// The BrushContext will now hold references to the textures of this terrain for the target of the given brush,
885    /// and so the stroke should not be used with other terrains until the stroke is finished.
886    /// - `terrain`: The terrain that this stroke will edit.
887    /// - `brush`: The Brush containing the brush shape and painting operation to perform.
888    pub fn start_stroke(&mut self, terrain: &Terrain, brush: Brush) {
889        self.value = None;
890        terrain.start_stroke(brush, &mut self.stroke);
891    }
892    /// Modify the brushstroke to include a stamp of the brush at the given position.
893    /// The location of the stamp relative to the textures is determined based on the global position
894    /// of the terrain and the size of each terrain pixel.
895    /// - `terrain`: The terrain that will be used to translate the given world-space coordinates into
896    /// texture-space coordinates. This should be the same terrain as was given to [BrushContext::start_stroke].
897    /// - `position`: The position of the brush in world coordinates.
898    pub fn stamp(&mut self, terrain: &Terrain, position: Vector3<f32>) {
899        let value = if matches!(self.stroke.brush().mode, BrushMode::Flatten) {
900            self.interpolate_value(terrain, position)
901        } else {
902            0.0
903        };
904        terrain.stamp(position, value, &mut self.stroke);
905    }
906    /// Modify the brushstroke to include a smear of the brush from `start` to `end`.
907    /// The location of the smear relative to the textures is determined based on the global position
908    /// of the terrain and the size of each terrain pixel.
909    /// - `terrain`: The terrain that will be used to translate the given world-space coordinates into
910    /// texture-space coordinates. This should be the same terrain as was given to [BrushContext::start_stroke].
911    /// - `start`: The start of the brush in world coordinates.
912    /// - `end`: The end of the brush in world coordinates.
913    pub fn smear(&mut self, terrain: &Terrain, start: Vector3<f32>, end: Vector3<f32>) {
914        let value = if matches!(self.stroke.brush().mode, BrushMode::Flatten) {
915            self.interpolate_value(terrain, start)
916        } else {
917            0.0
918        };
919        terrain.smear(start, end, value, &mut self.stroke);
920    }
921    /// Update the terrain's textures to include the latest pixel data without ending the stroke.
922    pub fn flush(&mut self) {
923        self.stroke.flush();
924    }
925    /// Update the terrain's textures to include the latest data and clear this context of all pixel data
926    /// to prepare for starting another stroke.
927    pub fn end_stroke(&mut self) {
928        self.stroke.end_stroke();
929    }
930}
931
932impl BrushContext {
933    fn interpolate_value(&mut self, terrain: &Terrain, position: Vector3<f32>) -> f32 {
934        if let Some(v) = self.value {
935            return v;
936        }
937        let Some(position) = terrain.project(position) else {
938            return 0.0;
939        };
940        let target = self.stroke.brush().target;
941        let v = terrain.interpolate_value(position, target);
942        self.value = Some(v);
943        v
944    }
945}
946
947/// Terrain is a height field where each point has fixed coordinates in XZ plane, but variable Y coordinate.
948/// It can be used to create landscapes. It supports multiple layers, where each layer has its own material
949/// and mask.
950///
951/// ## Chunking
952///
953/// Terrain itself does not define any geometry or rendering data, instead it uses one or more chunks for that
954/// purpose. Each chunk could be considered as a "sub-terrain". You can "stack" any amount of chunks from any
955/// side of the terrain. To do that, you define a range of chunks along each axes. This is very useful if you
956/// need to extend your terrain in a particular direction. Imagine that you've created a terrain with just one
957/// chunk (`0..1` range on both axes), but suddenly you found that you need to extend the terrain to add some
958/// new game locations. In this case you can change the range of chunks at the desired axis. For instance, if
959/// you want to add a new location to the right from your single chunk, then you should change `width_chunks`
960/// range to `0..2` and leave `length_chunks` as is (`0..1`). This way terrain will be extended and you can
961/// start shaping the new location.
962///
963/// ## Layers
964///
965/// Layer is a material with a blending mask. Layers helps you to build a terrain with wide variety of details.
966/// For example, you can have a terrain with 3 layers: grass, rock, snow. This combination can be used to
967/// create a terrain with grassy plateaus, rocky mountains with snowy tops. Each chunk (see above) can have its
968/// own set of materials for each layer, however the overall layer count is defined by the terrain itself.
969/// An ability to have different set of materials for different chunks is very useful to support various biomes.
970///
971/// ## Level of detail (LOD)
972///
973/// Terrain has automatic LOD system, which means that the closest portions of it will be rendered with highest
974/// possible quality (defined by the resolution of height map and masks), while the furthest portions will be
975/// rendered with lowest quality. This effectively balances GPU load and allows you to render huge terrains with
976/// low overhead.
977///
978/// The main parameter that affects LOD system is `block_size` (`Terrain::set_block_size`), which defines size
979/// of the patch that will be used for rendering. It is used to divide the size of the height map into a fixed
980/// set of blocks using quad-tree algorithm.
981///
982/// Current implementation uses modified version of CDLOD algorithm without patch morphing. Apparently it is not
983/// needed, since bilinear filtration in vertex shader prevents seams to occur.
984///
985/// ## Painting
986///
987/// Painting involves constructing a [BrushStroke] and calling its [BrushStroke::accept_messages] method with
988/// a channel receiver, and sending a series of pixel messages into that channel. The BrushStroke will translate
989/// those messages into modifications to the Terrain's textures.
990///
991/// ## Ray casting
992///
993/// You have two options to perform a ray casting:
994///
995/// 1) By using ray casting feature of the physics engine. In this case you need to create a `Heighfield` collider
996/// and use standard [`crate::scene::graph::physics::PhysicsWorld::cast_ray`] method.
997/// 2) By using [`Terrain::raycast`] - this method could provide you more information about intersection point, than
998/// physics-based.
999///
1000/// ## Physics
1001///
1002/// As usual, to have collisions working you need to create a rigid body and add an appropriate collider to it.
1003/// In case of terrains you need to create a collider with `Heightfield` shape and specify your terrain as a
1004/// geometry source.
1005///
1006/// ## Coordinate Spaces
1007///
1008/// Terrains operate in several systems of coordinates depending upon which aspect of the terrain is being measured.
1009///
1010/// - **Local:** These are the 3D `f32` coordinates of the Terrain node that are transformed to world space by the
1011/// [Base::global_transform]. It is measured in meters.
1012/// - **Local 2D:** These are the 2D `f32` coordinates formed by taking the (x,y,z) of local coordinates and turning them
1013/// into (x,z), with y removed and z becoming the new y.
1014/// The size of chunks in these coordinates is set by [Terrain::chunk_size].
1015/// - **Grid Position:** These are the 2D `i32` coordinates that represent a chunk's position within the regular grid of
1016/// chunks that make up a terrain. The *local 2D* position of a chunk can be calculated from its *grid position* by
1017/// multiplying its x and y coordinates by the x and y of [Terrain::chunk_size].
1018/// - **Height Pixel Position:** These are the 2D coordinates that measure position across the x and z axes of
1019/// the terrain using pixels in the height data of each chunk. (0,0) is the position of the Terrain node.
1020/// The *height pixel position* of a chunk can be calculated from its *grid position* by
1021/// multiplying its x and y coordinates by (x - 3) and (y - 3) of [Terrain::height_map_size].
1022/// Subtracting 1 from each dimension is necessary because the height map data of chunks overlaps by one pixel
1023/// on each edge, so the distance between the origins of two adjacent chunks is one less than height_map_size.
1024/// - **Mask Pixel Position:** These are the 2D coordinates that measure position across the x and z axes of
1025/// the terrain using pixels of the mask data of each chunk. (0,0) is the position of the (0,0) pixel of the
1026/// mask texture of the (0,0) chunk.
1027/// This means that (0,0) is offset from the position of the Terrain node by a half-pixel in the x direction
1028/// and a half-pixel in the z direction.
1029/// The size of each pixel is determined by [Terrain::chunk_size] and [Terrain::mask_size].
1030///
1031/// The size of blocks and the size of quad tree nodes is measured in height pixel coordinates, and these measurements
1032/// count the number of pixels needed to render the vertices of that part of the terrain, which means that they
1033/// overlap with their neighbors just as chunks overlap. Two adjacent blocks share vertices along their edge,
1034/// so they also share pixels in the height map data.
1035#[derive(Debug, Reflect, Clone, ComponentProvider)]
1036#[reflect(derived_type = "Node")]
1037pub struct Terrain {
1038    base: Base,
1039
1040    #[reflect(setter = "set_holes_enabled")]
1041    holes_enabled: bool,
1042
1043    #[reflect(setter = "set_layers")]
1044    layers: InheritableVariable<Vec<Layer>>,
1045
1046    /// Size of the chunk, in meters. This value becomes the [Chunk::physical_size] of newly created
1047    /// chunks.
1048    #[reflect(min_value = 0.001, setter = "set_chunk_size")]
1049    chunk_size: InheritableVariable<Vector2<f32>>,
1050
1051    /// Min and max 'coordinate' of chunks along X axis. Modifying this will create new chunks or
1052    /// destroy existing chunks.
1053    #[reflect(step = 1.0, setter = "set_width_chunks")]
1054    width_chunks: InheritableVariable<Range<i32>>,
1055
1056    /// Min and max 'coordinate' of chunks along Y axis. Modifying this will create new chunks or
1057    /// destroy existing chunks.
1058    #[reflect(step = 1.0, setter = "set_length_chunks")]
1059    length_chunks: InheritableVariable<Range<i32>>,
1060
1061    /// Size of the height map per chunk, in pixels. Warning: any change to this value will result in resampling!
1062    ///
1063    /// Each dimension should be three greater than some power of 2, such as 7 = 4 + 3, 11 = 8 + 3, 19 = 16 + 3, and so on.
1064    /// This is important because when chunks are being split into quadrants for LOD, the splits must always happen
1065    /// along its vertices, and there should be an equal number of vertices on each side of each split.
1066    /// If there cannot be an equal number of vertices on each side of the split, then the split will be made
1067    /// so that the number of vertices is as close to equal as possible, but this may result in vertices not being
1068    /// properly aligned between adjacent blocks.
1069    #[reflect(min_value = 2.0, step = 1.0, setter = "set_height_map_size")]
1070    height_map_size: InheritableVariable<Vector2<u32>>,
1071
1072    /// Size of the mesh block that will be scaled to various sizes to render the terrain at various levels of detail,
1073    /// as measured by counting vertices along each dimension.
1074    ///
1075    /// Each dimension should be one greater than some power of 2, such as 5 = 4 + 1, 9 = 8 + 1, 17 = 16 + 1, and so on.
1076    /// This helps the vertices of the block to align with the pixels of the height data texture.
1077    /// Excluding the one-pixel margin that is not rendered, height data should also be one greater than some power of 2.
1078    #[reflect(min_value = 8.0, step = 1.0, setter = "set_block_size")]
1079    block_size: InheritableVariable<Vector2<u32>>,
1080
1081    /// Size of the blending mask per chunk, in pixels. Warning: any change to this value will result in resampling!
1082    #[reflect(min_value = 1.0, step = 1.0, setter = "set_mask_size")]
1083    mask_size: InheritableVariable<Vector2<u32>>,
1084
1085    #[reflect(immutable_collection)]
1086    chunks: InheritableVariable<Vec<Chunk>>,
1087
1088    #[reflect(hidden)]
1089    bounding_box_dirty: Cell<bool>,
1090
1091    #[reflect(hidden)]
1092    bounding_box: Cell<AxisAlignedBoundingBox>,
1093
1094    /// The [SurfaceSharedData](crate::scene::mesh::surface::SurfaceResource) that will be instanced to render
1095    /// all the chunks of the height map.
1096    #[reflect(hidden)]
1097    geometry: TerrainGeometry,
1098}
1099
1100impl Default for Terrain {
1101    fn default() -> Self {
1102        Self {
1103            base: Default::default(),
1104            holes_enabled: false,
1105            layers: Default::default(),
1106            chunk_size: Vector2::new(16.0, 16.0).into(),
1107            width_chunks: Default::default(),
1108            length_chunks: Default::default(),
1109            height_map_size: Vector2::new(259, 259).into(),
1110            block_size: Vector2::new(33, 33).into(),
1111            mask_size: Vector2::new(256, 256).into(),
1112            chunks: Default::default(),
1113            bounding_box_dirty: Cell::new(true),
1114            bounding_box: Cell::new(Default::default()),
1115            geometry: Default::default(),
1116        }
1117    }
1118}
1119
1120impl Visit for Terrain {
1121    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
1122        let mut region = visitor.enter_region(name)?;
1123
1124        let mut version = VERSION;
1125        version.visit("Version", &mut region)?;
1126
1127        if let VERSION = version {
1128            // Current version
1129            self.base.visit("Base", &mut region)?;
1130            self.holes_enabled.visit("HolesEnabled", &mut region)?;
1131            self.layers.visit("Layers", &mut region)?;
1132            self.chunk_size.visit("ChunkSize", &mut region)?;
1133            self.width_chunks.visit("WidthChunks", &mut region)?;
1134            self.length_chunks.visit("LengthChunks", &mut region)?;
1135            self.height_map_size.visit("HeightMapSize", &mut region)?;
1136            self.block_size.visit("BlockSize", &mut region)?;
1137            self.mask_size.visit("MaskSize", &mut region)?;
1138            self.chunks.visit("Chunks", &mut region)?;
1139        }
1140
1141        if region.is_reading() {
1142            self.geometry = TerrainGeometry::new(*self.block_size);
1143        }
1144
1145        Ok(())
1146    }
1147}
1148
1149impl Deref for Terrain {
1150    type Target = Base;
1151
1152    fn deref(&self) -> &Self::Target {
1153        &self.base
1154    }
1155}
1156
1157impl DerefMut for Terrain {
1158    fn deref_mut(&mut self) -> &mut Self::Target {
1159        &mut self.base
1160    }
1161}
1162
1163fn project(global_transform: Matrix4<f32>, p: Vector3<f32>) -> Option<Vector2<f32>> {
1164    // Transform point in coordinate system of the terrain.
1165    if let Some(inv_global_transform) = global_transform.try_inverse() {
1166        let local_p = inv_global_transform
1167            .transform_point(&Point3::from(p))
1168            .coords;
1169        Some(map_to_local(local_p))
1170    } else {
1171        None
1172    }
1173}
1174
1175/// Calculate the grid position of the chunk that would contain the given pixel position
1176/// assuming chunks have the given size.
1177fn pixel_position_to_grid_position(
1178    position: Vector2<i32>,
1179    chunk_size: Vector2<u32>,
1180) -> Vector2<i32> {
1181    let chunk_size = chunk_size.map(|x| x as i32);
1182    let x = position.x / chunk_size.x;
1183    let y = position.y / chunk_size.y;
1184    // Correct for the possibility of x or y being negative.
1185    let x = if position.x < 0 && position.x % chunk_size.x != 0 {
1186        x - 1
1187    } else {
1188        x
1189    };
1190    let y = if position.y < 0 && position.y % chunk_size.y != 0 {
1191        y - 1
1192    } else {
1193        y
1194    };
1195    Vector2::new(x, y)
1196}
1197
1198fn resize_u8(data: Vec<u8>, data_size: Vector2<u32>, new_size: Vector2<u32>) -> Vec<u8> {
1199    let image = ImageBuffer::<Luma<u8>, Vec<u8>>::from_vec(data_size.x, data_size.y, data).unwrap();
1200
1201    let resampled_image =
1202        image::imageops::resize(&image, new_size.x, new_size.y, FilterType::Lanczos3);
1203
1204    resampled_image.into_raw()
1205}
1206
1207#[allow(clippy::manual_slice_fill)] // False-positive
1208fn resize_f32(mut data: Vec<f32>, data_size: Vector2<u32>, new_size: Vector2<u32>) -> Vec<f32> {
1209    let max = data.iter().copied().reduce(f32::max).unwrap();
1210    let min = data.iter().copied().reduce(f32::min).unwrap();
1211    let range = max - min;
1212
1213    if range == 0.0 {
1214        let size: usize = (new_size.x * new_size.y) as usize;
1215        data.clear();
1216        data.extend(std::iter::repeat_n(min, size));
1217        return data;
1218    }
1219
1220    for height in &mut data {
1221        *height = (*height - min) / range;
1222    }
1223
1224    let heightmap_image =
1225        ImageBuffer::<Luma<f32>, Vec<f32>>::from_vec(data_size.x, data_size.y, data).unwrap();
1226
1227    let resampled_heightmap_image = image::imageops::resize(
1228        &heightmap_image,
1229        new_size.x,
1230        new_size.y,
1231        FilterType::Lanczos3,
1232    );
1233
1234    let mut resampled_heightmap = resampled_heightmap_image.into_raw();
1235
1236    for height in &mut resampled_heightmap {
1237        *height = (*height * range) + min;
1238    }
1239    resampled_heightmap
1240}
1241
1242fn create_zero_margin(mut data: Vec<f32>, data_size: Vector2<u32>) -> Vec<f32> {
1243    let w0 = data_size.x as usize;
1244    let w1 = w0 + 2;
1245    let h0 = data_size.y as usize;
1246    let h1 = h0 + 2;
1247    let new_area = w1 * h1;
1248    data.extend(std::iter::repeat_n(0.0, new_area - data.len()));
1249    for y in (0..h0).rev() {
1250        let i0 = y * w0;
1251        let i1 = (y + 1) * w1;
1252        data.copy_within(i0..i0 + w0, i1 + 1);
1253        data[i1] = 0.0;
1254        data[i1 + w1 - 1] = 0.0;
1255    }
1256    for v in data.iter_mut().take(w1) {
1257        *v = 0.0;
1258    }
1259    data
1260}
1261
1262impl TypeUuidProvider for Terrain {
1263    fn type_uuid() -> Uuid {
1264        uuid!("4b0a7927-bcd8-41a3-949a-dd10fba8e16a")
1265    }
1266}
1267
1268impl Terrain {
1269    /// The height map of a chunk must have one-pixel margins around the edges which do not correspond
1270    /// to vertices in the terrain of that chunk, but are still needed for calculating the normal of
1271    /// the edge vertices.
1272    /// The normal for each vertex is derived from the heights of the four neighbor vertices, which means
1273    /// that every vertex must have four neighbors, even edge vertices. The one-pixel margin guarantees this.
1274    ///
1275    /// This method modifies the margin of the chunk at the given position so that it matches the data in
1276    /// the eight neighboring chunks.
1277    pub fn align_chunk_margins(&mut self, grid_position: Vector2<i32>) {
1278        let Some(chunk) = self.find_chunk(grid_position) else {
1279            return;
1280        };
1281        let size = self.height_map_size();
1282        let x1 = size.x as i32 - 2;
1283        let y1 = size.y as i32 - 2;
1284        let mut data = chunk.heightmap.as_ref().unwrap().data_ref();
1285        let mut mut_data = ChunkHeightMutData(data.modify());
1286        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 0)) {
1287            let data = other_chunk.height_data();
1288            for y in 0..y1 {
1289                mut_data[Vector2::new(-1, y)] = data[Vector2::new(x1 - 2, y)];
1290            }
1291        }
1292        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 0)) {
1293            let data = other_chunk.height_data();
1294            for y in 0..y1 {
1295                mut_data[Vector2::new(x1, y)] = data[Vector2::new(1, y)];
1296            }
1297        }
1298        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, -1)) {
1299            let data = other_chunk.height_data();
1300            for x in 0..x1 {
1301                mut_data[Vector2::new(x, -1)] = data[Vector2::new(x, y1 - 2)];
1302            }
1303        }
1304        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, 1)) {
1305            let data = other_chunk.height_data();
1306            for x in 0..x1 {
1307                mut_data[Vector2::new(x, y1)] = data[Vector2::new(x, 1)];
1308            }
1309        }
1310        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, -1)) {
1311            let data = other_chunk.height_data();
1312            mut_data[Vector2::new(-1, -1)] = data[Vector2::new(x1 - 2, y1 - 2)];
1313        }
1314        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, -1)) {
1315            let data = other_chunk.height_data();
1316            mut_data[Vector2::new(x1, -1)] = data[Vector2::new(1, y1 - 2)];
1317        }
1318        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 1)) {
1319            let data = other_chunk.height_data();
1320            mut_data[Vector2::new(-1, y1)] = data[Vector2::new(x1 - 2, 1)];
1321        }
1322        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 1)) {
1323            let data = other_chunk.height_data();
1324            mut_data[Vector2::new(x1, y1)] = data[Vector2::new(1, 1)];
1325        }
1326    }
1327
1328    /// The height map of a chunk must duplicate the height data of neighboring chunks along each edge.
1329    /// Otherwise the terrain would split apart at chunk boundaries.
1330    /// This method modifies all eight neighboring chunks surrounding the chunk at the given position to
1331    /// force them to align with the edge data of the chunk at the given position.
1332    pub fn align_chunk_edges(&mut self, grid_position: Vector2<i32>) {
1333        let Some(chunk) = self.find_chunk(grid_position) else {
1334            return;
1335        };
1336        let size = self.height_map_size();
1337        let x1 = size.x as i32 - 3;
1338        let y1 = size.y as i32 - 3;
1339        let source = chunk.height_data();
1340        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 0)) {
1341            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1342            let mut mut_data = ChunkHeightMutData(data.modify());
1343            for y in 0..=y1 {
1344                mut_data[Vector2::new(x1, y)] = source[Vector2::new(0, y)];
1345            }
1346        }
1347        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 0)) {
1348            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1349            let mut mut_data = ChunkHeightMutData(data.modify());
1350            for y in 0..=y1 {
1351                mut_data[Vector2::new(0, y)] = source[Vector2::new(x1, y)];
1352            }
1353        }
1354        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, -1)) {
1355            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1356            let mut mut_data = ChunkHeightMutData(data.modify());
1357            for x in 0..=x1 {
1358                mut_data[Vector2::new(x, y1)] = source[Vector2::new(x, 0)];
1359            }
1360        }
1361        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, 1)) {
1362            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1363            let mut mut_data = ChunkHeightMutData(data.modify());
1364            for x in 0..=x1 {
1365                mut_data[Vector2::new(x, 0)] = source[Vector2::new(x, y1)];
1366            }
1367        }
1368        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, -1)) {
1369            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1370            let mut mut_data = ChunkHeightMutData(data.modify());
1371            mut_data[Vector2::new(x1, y1)] = source[Vector2::new(0, 0)];
1372        }
1373        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, -1)) {
1374            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1375            let mut mut_data = ChunkHeightMutData(data.modify());
1376            mut_data[Vector2::new(0, y1)] = source[Vector2::new(x1, 0)];
1377        }
1378        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 1)) {
1379            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1380            let mut mut_data = ChunkHeightMutData(data.modify());
1381            mut_data[Vector2::new(x1, 0)] = source[Vector2::new(0, y1)];
1382        }
1383        if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 1)) {
1384            let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1385            let mut mut_data = ChunkHeightMutData(data.modify());
1386            mut_data[Vector2::new(0, 0)] = source[Vector2::new(x1, y1)];
1387        }
1388    }
1389
1390    /// Returns chunk size in meters. This is equivalent to [Chunk::physical_size].
1391    pub fn chunk_size(&self) -> Vector2<f32> {
1392        *self.chunk_size
1393    }
1394
1395    /// Sets new chunk size of the terrain (in meters). All chunks in the terrain will be repositioned according
1396    /// to their positions on the grid. Return the previous chunk size.
1397    pub fn set_chunk_size(&mut self, chunk_size: Vector2<f32>) -> Vector2<f32> {
1398        let old = *self.chunk_size;
1399        self.chunk_size.set_value_and_mark_modified(chunk_size);
1400
1401        // Re-position each chunk according to its position on the grid.
1402        for iy in 0..self.length_chunks.len() {
1403            for ix in 0..self.width_chunks.len() {
1404                let chunk = &mut self.chunks[iy * self.width_chunks.len() + ix];
1405                chunk.physical_size = chunk_size;
1406                chunk.position = chunk.position();
1407            }
1408        }
1409
1410        self.bounding_box_dirty.set(true);
1411
1412        old
1413    }
1414
1415    /// Returns height map dimensions along each axis.
1416    /// This is measured in *pixels* and gives the size of each chunk,
1417    /// including the 1 pixel overlap that each chunk shares with its neighbors.
1418    pub fn height_map_size(&self) -> Vector2<u32> {
1419        *self.height_map_size
1420    }
1421
1422    /// Returns hole mask dimensions along each axis.
1423    /// This is measured in *pixels* and gives the size of each chunk by
1424    /// counting the faces between height map vertices.
1425    /// Holes are cut into terrain by removing faces, so each pixel represents one face.
1426    pub fn hole_mask_size(&self) -> Vector2<u32> {
1427        self.height_map_size.map(|x| x - 3)
1428    }
1429
1430    /// Sets new size of the height map for every chunk. Heightmaps in every chunk will be resampled which may
1431    /// cause precision loss if the size was decreased. **Warning:** This method is very heavy and should not be
1432    /// used at every frame!
1433    pub fn set_height_map_size(&mut self, height_map_size: Vector2<u32>) -> Vector2<u32> {
1434        let old = *self.height_map_size;
1435        self.resize_height_maps(height_map_size);
1436        old
1437    }
1438
1439    /// Sets the new block size, measured in height map pixels.
1440    /// Block size defines "granularity" of the terrain; the minimal terrain patch that
1441    /// will be used for rendering. It directly affects level-of-detail system of the terrain. **Warning:** This
1442    /// method is very heavy and should not be used at every frame!
1443    pub fn set_block_size(&mut self, block_size: Vector2<u32>) -> Vector2<u32> {
1444        let old = *self.block_size;
1445        self.block_size.set_value_and_mark_modified(block_size);
1446        self.geometry = TerrainGeometry::new(*self.block_size);
1447        for chunk in self.chunks.iter_mut() {
1448            chunk.set_block_size(*self.block_size);
1449        }
1450        old
1451    }
1452
1453    /// Returns current block size of the terrain as measured by counting vertices along each axis of the block mesh.
1454    pub fn block_size(&self) -> Vector2<u32> {
1455        *self.block_size
1456    }
1457
1458    /// Add or remove the hole masks from the chunks of this terrain.
1459    pub fn set_holes_enabled(&mut self, enabled: bool) -> bool {
1460        if self.holes_enabled == enabled {
1461            return enabled;
1462        }
1463        let old = self.holes_enabled;
1464        self.holes_enabled = enabled;
1465        let size = self.hole_mask_size();
1466        for chunk in self.chunks.iter_mut() {
1467            if enabled {
1468                chunk.hole_mask = Some(make_blank_hole_texture(size));
1469            } else {
1470                chunk.hole_mask = None;
1471            }
1472        }
1473        old
1474    }
1475
1476    /// True if hole masks have been added to chunks.
1477    pub fn holes_enabled(&self) -> bool {
1478        self.holes_enabled
1479    }
1480
1481    /// Returns the number of pixels along each axis of the layer blending mask.
1482    pub fn mask_size(&self) -> Vector2<u32> {
1483        *self.mask_size
1484    }
1485
1486    /// Sets new size of the layer blending mask in pixels. Every layer mask will be resampled which may cause
1487    /// precision loss if the size was decreased.
1488    pub fn set_mask_size(&mut self, mask_size: Vector2<u32>) -> Vector2<u32> {
1489        let old = *self.mask_size;
1490        self.resize_masks(mask_size);
1491        old
1492    }
1493
1494    /// Returns a numeric range along width axis which defines start and end chunk indices on a chunks grid.
1495    pub fn width_chunks(&self) -> Range<i32> {
1496        (*self.width_chunks).clone()
1497    }
1498
1499    /// Sets amount of chunks along width axis.
1500    pub fn set_width_chunks(&mut self, chunks: Range<i32>) -> Range<i32> {
1501        let old = (*self.width_chunks).clone();
1502        self.resize(chunks, self.length_chunks());
1503        old
1504    }
1505
1506    /// Returns a numeric range along length axis which defines start and end chunk indices on a chunks grid.
1507    pub fn length_chunks(&self) -> Range<i32> {
1508        (*self.length_chunks).clone()
1509    }
1510
1511    /// Sets amount of chunks along length axis.
1512    pub fn set_length_chunks(&mut self, chunks: Range<i32>) -> Range<i32> {
1513        let old = (*self.length_chunks).clone();
1514        self.resize(self.width_chunks(), chunks);
1515        old
1516    }
1517
1518    /// Sets new chunks ranges for each axis of the terrain. This function automatically adds new chunks if you're
1519    /// increasing size of the terrain and removes existing if you shrink the terrain.
1520    pub fn resize(&mut self, width_chunks: Range<i32>, length_chunks: Range<i32>) {
1521        let mut chunks = self
1522            .chunks
1523            .drain(..)
1524            .map(|c| (c.grid_position, c))
1525            .collect::<HashMap<_, _>>();
1526
1527        self.width_chunks.set_value_and_mark_modified(width_chunks);
1528        self.length_chunks
1529            .set_value_and_mark_modified(length_chunks);
1530        let mut created_chunks = Vec::new();
1531        let mut preserved_chunks = Vec::new();
1532
1533        let hole_size = self.hole_mask_size();
1534
1535        for z in (*self.length_chunks).clone() {
1536            for x in (*self.width_chunks).clone() {
1537                let chunk = if let Some(existing_chunk) = chunks.remove(&Vector2::new(x, z)) {
1538                    preserved_chunks.push(existing_chunk.grid_position);
1539                    // Put existing chunk back at its position.
1540                    existing_chunk
1541                } else {
1542                    // Create new chunk.
1543                    let heightmap =
1544                        vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
1545                    let new_chunk = Chunk {
1546                        quad_tree: Mutex::new(QuadTree::new(
1547                            &heightmap,
1548                            *self.height_map_size,
1549                            *self.block_size,
1550                            0,
1551                        )),
1552                        heightmap: Some(make_height_map_texture(heightmap, self.height_map_size())),
1553                        hole_mask: if self.holes_enabled {
1554                            Some(make_blank_hole_texture(hole_size))
1555                        } else {
1556                            None
1557                        },
1558                        height_map_modifications_count: 0,
1559                        position: Vector3::new(
1560                            x as f32 * self.chunk_size.x,
1561                            0.0,
1562                            z as f32 * self.chunk_size.y,
1563                        ),
1564                        physical_size: *self.chunk_size,
1565                        height_map_size: *self.height_map_size,
1566                        block_size: *self.block_size,
1567                        grid_position: Vector2::new(x, z),
1568                        layer_masks: self
1569                            .layers
1570                            .iter()
1571                            .enumerate()
1572                            .map(|(i, _)| {
1573                                create_layer_mask(
1574                                    self.mask_size.x,
1575                                    self.mask_size.y,
1576                                    if i == 0 { 255 } else { 0 },
1577                                )
1578                            })
1579                            .collect::<Vec<_>>(),
1580                    };
1581                    created_chunks.push(new_chunk.grid_position);
1582                    new_chunk
1583                };
1584
1585                self.chunks.push(chunk);
1586            }
1587        }
1588
1589        for grid_position in created_chunks {
1590            self.align_chunk_margins(grid_position);
1591        }
1592        for grid_position in preserved_chunks {
1593            self.align_chunk_edges(grid_position);
1594        }
1595
1596        self.bounding_box_dirty.set(true);
1597    }
1598
1599    /// Returns a reference to chunks of the terrain.
1600    pub fn chunks_ref(&self) -> &[Chunk] {
1601        &self.chunks
1602    }
1603
1604    /// Returns a mutable reference to chunks of the terrain.
1605    pub fn chunks_mut(&mut self) -> &mut [Chunk] {
1606        self.bounding_box_dirty.set(true);
1607        &mut self.chunks
1608    }
1609
1610    /// Return the chunk with the matching [Chunk::grid_position].
1611    pub fn find_chunk(&self, grid_position: Vector2<i32>) -> Option<&Chunk> {
1612        self.chunks
1613            .iter()
1614            .find(|c| c.grid_position == grid_position)
1615    }
1616
1617    /// Return the chunk with the matching [Chunk::grid_position].
1618    pub fn find_chunk_mut(&mut self, grid_position: Vector2<i32>) -> Option<&mut Chunk> {
1619        self.chunks
1620            .iter_mut()
1621            .find(|c| c.grid_position == grid_position)
1622    }
1623
1624    /// Create new quad trees for every chunk in the terrain.
1625    pub fn update_quad_trees(&mut self) {
1626        for c in self.chunks.iter_mut() {
1627            c.update_quad_tree();
1628        }
1629    }
1630
1631    /// Projects given 3D point on the surface of terrain and returns 2D vector
1632    /// expressed in local 2D coordinate system of terrain.
1633    pub fn project(&self, p: Vector3<f32>) -> Option<Vector2<f32>> {
1634        project(self.global_transform(), p)
1635    }
1636
1637    /// Convert from local 2D to height pixel position.
1638    pub fn local_to_height_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1639        let scale = self.height_grid_scale();
1640        Vector2::new(p.x / scale.x, p.y / scale.y)
1641    }
1642
1643    /// Convert from local 2D to mask pixel position.
1644    pub fn local_to_mask_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1645        let scale = self.mask_grid_scale();
1646        let half = scale * 0.5;
1647        let p = p - half;
1648        Vector2::new(p.x / scale.x, p.y / scale.y)
1649    }
1650
1651    /// Convert from local 2D to hole pixel position.
1652    pub fn local_to_hole_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1653        let scale = self.hole_grid_scale();
1654        let half = scale * 0.5;
1655        let p = p - half;
1656        Vector2::new(p.x / scale.x, p.y / scale.y)
1657    }
1658
1659    /// The size of each cell of the height grid in local 2D units.
1660    pub fn height_grid_scale(&self) -> Vector2<f32> {
1661        // Subtract 2 to exclude the margins which are not rendered.
1662        // Subtract 1 to count the edges between pixels instead of the pixels.
1663        let cell_width = self.chunk_size.x / (self.height_map_size.x - 3) as f32;
1664        let cell_length = self.chunk_size.y / (self.height_map_size.y - 3) as f32;
1665        Vector2::new(cell_width, cell_length)
1666    }
1667
1668    /// The size of each cell of the height grid in local 2D units.
1669    pub fn hole_grid_scale(&self) -> Vector2<f32> {
1670        // Subtract 2 to exclude the margins which are not rendered.
1671        // Subtract 1 to count the edges between pixels instead of the pixels.
1672        let cell_width = self.chunk_size.x / (self.height_map_size.x - 3) as f32;
1673        let cell_length = self.chunk_size.y / (self.height_map_size.y - 3) as f32;
1674        Vector2::new(cell_width, cell_length)
1675    }
1676
1677    /// The size of each cell of the mask grid in local 2D units.
1678    pub fn mask_grid_scale(&self) -> Vector2<f32> {
1679        let cell_width = self.chunk_size.x / self.mask_size.x as f32;
1680        let cell_length = self.chunk_size.y / self.mask_size.y as f32;
1681        Vector2::new(cell_width, cell_length)
1682    }
1683
1684    /// Calculate which cell of the height grid contains the given local 2D position.
1685    pub fn get_height_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1686        TerrainRect::from_local(position, self.height_grid_scale())
1687    }
1688
1689    /// Calculate which cell of the mask grid contains the given local 2D position.
1690    /// Mask grid cells are shifted by a half-pixel in each dimension, so that the
1691    /// origin of the (0,0) cell is at (0.5,0.5) as measured in pixels.
1692    pub fn get_mask_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1693        let cell_size = self.mask_grid_scale();
1694        let half_size = cell_size / 2.0;
1695        // Translate by a half-pixel so that `from_local` will give us the right answer.
1696        let position = position - half_size;
1697        let mut rect = TerrainRect::from_local(position, cell_size);
1698        rect.bounds.position += half_size;
1699        rect
1700    }
1701
1702    /// Calculate which cell of the hole grid contains the given local 2D position.
1703    /// Mask grid cells are shifted by a half-pixel in each dimension, so that the
1704    /// origin of the (0,0) cell is at (0.5,0.5) as measured in pixels.
1705    pub fn get_hole_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1706        let cell_size = self.height_grid_scale();
1707        let half_size = cell_size / 2.0;
1708        // Translate by a half-pixel so that `from_local` will give us the right answer.
1709        let position = position - half_size;
1710        let mut rect = TerrainRect::from_local(position, cell_size);
1711        rect.bounds.position += half_size;
1712        rect
1713    }
1714
1715    /// Return the value of the layer mask at the given mask pixel position.
1716    pub fn get_layer_mask(&self, position: Vector2<i32>, layer: usize) -> Option<u8> {
1717        let chunk_pos = self.chunk_containing_mask_pos(position);
1718        let chunk = self.find_chunk(chunk_pos)?;
1719        let origin = self.chunk_mask_pos_origin(chunk_pos);
1720        let pos = (position - origin).map(|x| x as usize);
1721        let index = pos.y * self.mask_size.x as usize + pos.x;
1722        let texture_data = chunk.layer_masks[layer].data_ref();
1723        let mask_data = texture_data.data();
1724        Some(mask_data[index])
1725    }
1726
1727    /// Return the value of the layer mask at the given mask pixel position.
1728    pub fn get_hole_mask(&self, position: Vector2<i32>) -> Option<u8> {
1729        let chunk_pos = self.chunk_containing_hole_pos(position);
1730        let chunk = self.find_chunk(chunk_pos)?;
1731        let origin = self.chunk_hole_pos_origin(chunk_pos);
1732        let pos = (position - origin).map(|x| x as usize);
1733        let index = pos.y * (self.height_map_size.x - 3) as usize + pos.x;
1734        let texture_data = chunk.hole_mask.as_ref().map(|r| r.data_ref())?;
1735        let mask_data = texture_data.data();
1736        Some(mask_data[index])
1737    }
1738
1739    /// Return the value of the height map at the given height pixel position.
1740    pub fn get_height(&self, position: Vector2<i32>) -> Option<f32> {
1741        let chunk = self.chunks_containing_height_pos_iter(position).next()?;
1742        let p = (position - self.chunk_height_pos_origin(chunk.grid_position))
1743            .map(|x| (x + 1) as usize);
1744        let index = p.y * self.height_map_size.x as usize + p.x;
1745        let texture_data = chunk.heightmap.as_ref().unwrap().data_ref();
1746        let height_map = texture_data.data_of_type::<f32>().unwrap();
1747        Some(height_map[index])
1748    }
1749
1750    /// Return an interpolation of that the value should be for the given brush target
1751    /// at the given local 2D position.
1752    /// For height target, it returns the height.
1753    /// For mask targets, it returns 0.0 for transparent and 1.0 for opaque.
1754    pub fn interpolate_value(&self, position: Vector2<f32>, target: BrushTarget) -> f32 {
1755        let grid_square = match target {
1756            BrushTarget::HeightMap => self.get_height_grid_square(position),
1757            BrushTarget::LayerMask { .. } => self.get_mask_grid_square(position),
1758            BrushTarget::HoleMask => self.get_hole_grid_square(position),
1759        };
1760        let p = grid_square.grid_position;
1761        let b = grid_square.bounds;
1762        let x0 = b.position.x;
1763        let y0 = b.position.y;
1764        let x1 = b.position.x + b.size.x;
1765        let y1 = b.position.y + b.size.y;
1766        let dx0 = position.x - x0;
1767        let dx1 = x1 - position.x;
1768        let dy0 = position.y - y0;
1769        let dy1 = y1 - position.y;
1770        let p00 = p;
1771        let p01 = Vector2::new(p.x, p.y + 1);
1772        let p10 = Vector2::new(p.x + 1, p.y);
1773        let p11 = Vector2::new(p.x + 1, p.y + 1);
1774        let (f00, f01, f10, f11) = match target {
1775            BrushTarget::HeightMap => (
1776                self.get_height(p00).unwrap_or(0.0),
1777                self.get_height(p01).unwrap_or(0.0),
1778                self.get_height(p10).unwrap_or(0.0),
1779                self.get_height(p11).unwrap_or(0.0),
1780            ),
1781            BrushTarget::LayerMask { layer } => (
1782                self.get_layer_mask(p00, layer).unwrap_or(0) as f32 / 255.0,
1783                self.get_layer_mask(p01, layer).unwrap_or(0) as f32 / 255.0,
1784                self.get_layer_mask(p10, layer).unwrap_or(0) as f32 / 255.0,
1785                self.get_layer_mask(p11, layer).unwrap_or(0) as f32 / 255.0,
1786            ),
1787            BrushTarget::HoleMask => (
1788                self.get_hole_mask(p00).unwrap_or(0) as f32 / 255.0,
1789                self.get_hole_mask(p01).unwrap_or(0) as f32 / 255.0,
1790                self.get_hole_mask(p10).unwrap_or(0) as f32 / 255.0,
1791                self.get_hole_mask(p11).unwrap_or(0) as f32 / 255.0,
1792            ),
1793        };
1794        let value = f00 * dx1 * dy1 + f10 * dx0 * dy1 + f01 * dx1 * dy0 + f11 * dx0 * dy0;
1795        value / (b.size.x * b.size.y)
1796    }
1797
1798    /// Convert height pixel position into local 2D position.
1799    pub fn height_pos_to_local(&self, position: Vector2<i32>) -> Vector2<f32> {
1800        let pos = position.map(|x| x as f32);
1801        let chunk_size = self.height_map_size.map(|x| (x - 3) as f32);
1802        let physical_size = &self.chunk_size;
1803        Vector2::new(
1804            pos.x / chunk_size.x * physical_size.x,
1805            pos.y / chunk_size.y * physical_size.y,
1806        )
1807    }
1808
1809    /// Convert mask pixel position into local 2D position.
1810    pub fn mask_pos_to_local(&self, position: Vector2<i32>) -> Vector2<f32> {
1811        // Shift by 0.5 in each dimension to get the center of the pixel.
1812        let pos = position.map(|x| x as f32 + 0.5);
1813        let chunk_size = self.mask_size.map(|x| x as f32);
1814        let physical_size = &self.chunk_size;
1815        Vector2::new(
1816            pos.x / chunk_size.x * physical_size.x,
1817            pos.y / chunk_size.y * physical_size.y,
1818        )
1819    }
1820
1821    /// Determines the chunk containing the given height pixel coordinate.
1822    /// Be aware that the edges of chunks overlap by two pixels because the vertices along each edge of a chunk
1823    /// have the same height as the corresponding vertices of the next chunk in that direction.
1824    /// Due to this, if `position.x` is on the x-axis origin of the chunk returned by this method,
1825    /// then the position is also contained in the chunk at x - 1.
1826    /// Similarly, if `position.y` is on the y-axis origin, then the position is also in the y - 1 chunk.
1827    /// If position is on the origin in both the x and y axes, then the position is actually contained
1828    /// in 4 chunks.
1829    pub fn chunk_containing_height_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1830        // Subtract 3 from x and y to exclude the overlapping pixels along both axes from the chunk size.
1831        let chunk_size = self.height_map_size.map(|x| x - 3);
1832        pixel_position_to_grid_position(position, chunk_size)
1833    }
1834
1835    /// Given the grid position of some chunk and a height pixel position, return true
1836    /// if the chunk at that position would include data for the height at that position.
1837    pub fn chunk_contains_height_pos(
1838        &self,
1839        chunk_grid_position: Vector2<i32>,
1840        pixel_position: Vector2<i32>,
1841    ) -> bool {
1842        let p = pixel_position - self.chunk_height_pos_origin(chunk_grid_position);
1843        let w = self.height_map_size.x as i32;
1844        let h = self.height_map_size.y as i32;
1845        (-1..w - 1).contains(&p.x) && (-1..h - 1).contains(&p.y)
1846    }
1847
1848    /// Iterate through all the chunks that contain the given height pixel position.
1849    pub fn chunks_containing_height_pos_iter(
1850        &self,
1851        pixel_position: Vector2<i32>,
1852    ) -> impl Iterator<Item = &Chunk> {
1853        let w = self.height_map_size.x as i32;
1854        let h = self.height_map_size.y as i32;
1855        self.chunks.iter().filter(move |c| {
1856            let p = pixel_position - self.chunk_height_pos_origin(c.grid_position);
1857            (-1..w - 1).contains(&p.x) && (-1..h - 1).contains(&p.y)
1858        })
1859    }
1860
1861    /// Determines the position of the (0,0) coordinate of the given chunk
1862    /// as measured in height pixel coordinates.
1863    pub fn chunk_height_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1864        let chunk_size = *self.height_map_size;
1865        // Subtract 1 from x and y to exclude the overlapping pixel along both axes from the chunk size.
1866        let x = chunk_grid_position.x * (chunk_size.x as i32 - 1);
1867        let y = chunk_grid_position.y * (chunk_size.y as i32 - 1);
1868        Vector2::new(x, y)
1869    }
1870
1871    /// Determines the chunk containing the given mask pixel coordinate.
1872    /// This method makes no guarantee that there is actually a chunk at the returned coordinates.
1873    /// It returns the grid_position that the chunk would have if it existed.
1874    pub fn chunk_containing_mask_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1875        pixel_position_to_grid_position(position, *self.mask_size)
1876    }
1877    /// Determines the chunk containing the given hole pixel coordinate.
1878    /// This method makes no guarantee that there is actually a chunk at the returned coordinates.
1879    /// It returns the grid_position that the chunk would have if it existed.
1880    pub fn chunk_containing_hole_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1881        pixel_position_to_grid_position(position, self.hole_mask_size())
1882    }
1883
1884    /// Determines the position of the (0,0) coordinate of the given chunk
1885    /// as measured in mask pixel coordinates.
1886    pub fn chunk_mask_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1887        let chunk_size = *self.mask_size;
1888        let x = chunk_grid_position.x * chunk_size.x as i32;
1889        let y = chunk_grid_position.y * chunk_size.y as i32;
1890        Vector2::new(x, y)
1891    }
1892
1893    /// Determines the position of the (0,0) coordinate of the given chunk
1894    /// as measured in hole pixel coordinates.
1895    pub fn chunk_hole_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1896        let chunk_size = self.hole_mask_size();
1897        let x = chunk_grid_position.x * chunk_size.x as i32;
1898        let y = chunk_grid_position.y * chunk_size.y as i32;
1899        Vector2::new(x, y)
1900    }
1901
1902    /// Applies the given function to the value at the given position in mask pixel coordinates.
1903    /// This method calls the given function with the mask value of that pixel.
1904    /// If no chunk contains the given position, then the function is not called.
1905    pub fn update_mask_pixel<F>(&mut self, position: Vector2<i32>, layer: usize, func: F)
1906    where
1907        F: FnOnce(u8) -> u8,
1908    {
1909        let chunk_pos = self.chunk_containing_mask_pos(position);
1910        let origin = self.chunk_mask_pos_origin(chunk_pos);
1911        let pos = position - origin;
1912        let index = (pos.y * self.mask_size.x as i32 + pos.x) as usize;
1913        let Some(chunk) = self.find_chunk_mut(chunk_pos) else {
1914            return;
1915        };
1916        let mut texture_data = chunk.layer_masks[layer].data_ref();
1917        let mut texture_modifier = texture_data.modify();
1918        let mask = texture_modifier.data_mut_of_type::<u8>().unwrap();
1919        let value = &mut mask[index];
1920        *value = func(*value);
1921    }
1922
1923    /// Applies the given function to each pixel of the height map.
1924    pub fn for_each_height_map_pixel<F>(&mut self, mut func: F)
1925    where
1926        F: FnMut(&mut f32, Vector2<f32>),
1927    {
1928        for chunk in self.chunks.iter_mut() {
1929            let mut texture_data = chunk.heightmap.as_ref().unwrap().data_ref();
1930            let mut texture_modifier = texture_data.modify();
1931            let height_map = texture_modifier.data_mut_of_type::<f32>().unwrap();
1932
1933            for iy in 0..chunk.height_map_size.y {
1934                let kz = (iy as f32 - 1.0) / (chunk.height_map_size.y - 3) as f32;
1935                for ix in 0..chunk.height_map_size.x {
1936                    let kx = (ix as f32 - 1.0) / (chunk.height_map_size.x - 3) as f32;
1937
1938                    let pixel_position = chunk.local_position()
1939                        + Vector2::new(kx * chunk.physical_size.x, kz * chunk.physical_size.y);
1940
1941                    let index = (iy * chunk.height_map_size.x + ix) as usize;
1942
1943                    func(&mut height_map[index], pixel_position)
1944                }
1945            }
1946
1947            drop(texture_modifier);
1948            drop(texture_data);
1949
1950            *chunk.quad_tree.safe_lock() =
1951                make_quad_tree(&chunk.heightmap, chunk.height_map_size, chunk.block_size);
1952        }
1953
1954        self.bounding_box_dirty.set(true);
1955    }
1956
1957    /// Casts a ray and looks for intersections with the terrain. This method collects all results in
1958    /// given array with optional sorting by the time-of-impact.
1959    ///
1960    /// # Performance
1961    ///
1962    /// This method isn't well optimized, it could be optimized 2-5x times. This is a TODO for now.
1963    pub fn raycast<const DIM: usize>(
1964        &self,
1965        ray: Ray,
1966        results: &mut ArrayVec<TerrainRayCastResult, DIM>,
1967        sort_results: bool,
1968    ) -> bool {
1969        if let Some(inv_transform) = self.global_transform().try_inverse() {
1970            // Transform ray into local coordinate system of the terrain.
1971            let local_ray = ray.transform(inv_transform);
1972
1973            // Project ray on the terrain's 2D space.
1974            let origin_proj = map_to_local(
1975                inv_transform
1976                    .transform_point(&Point3::from(ray.origin))
1977                    .coords,
1978            );
1979            let dir_proj = map_to_local(inv_transform.transform_vector(&ray.dir));
1980
1981            // Check each cell of each chunk for intersection in 2D.
1982            'chunk_loop: for (chunk_index, chunk) in self.chunks.iter().enumerate() {
1983                let texture = chunk.heightmap.as_ref().unwrap().data_ref();
1984                let height_map = texture.data_of_type::<f32>().unwrap();
1985
1986                // The number of cells along each dimension of the chunk is 3 less then the number of pixels
1987                // along that dimension.
1988                // There are 2 margin pixels which are only used for calculating normals.
1989                // Among the remaining pixels, the cells count the space between the pixels,
1990                // so the number of cells is one less than the number of pixels.
1991                let chunk_width = (chunk.height_map_size.x - 3) as f32;
1992                let chunk_length = (chunk.height_map_size.y - 3) as f32;
1993                let cell_width = chunk.physical_size.x / chunk_width;
1994                let cell_length = chunk.physical_size.y / chunk_length;
1995
1996                // Search everything between the margins, but not including the margins
1997                for iy in 1..chunk.height_map_size.y - 2 {
1998                    let kz = (iy - 1) as f32 / chunk_length;
1999
2000                    // Search everything between the margins, but not including the margins
2001                    for ix in 1..chunk.height_map_size.x - 2 {
2002                        let kx = (ix - 1) as f32 / chunk_width;
2003
2004                        let pixel_position = chunk.local_position()
2005                            + Vector2::new(kx * chunk.physical_size.x, kz * chunk.physical_size.y);
2006
2007                        let cell_bounds =
2008                            Rect::new(pixel_position.x, pixel_position.y, cell_width, cell_length);
2009
2010                        if ray_rect_intersection(cell_bounds, origin_proj, dir_proj).is_some() {
2011                            // If we have 2D intersection, go back in 3D and do precise intersection
2012                            // check.
2013                            let i0 = (iy * chunk.height_map_size.x + ix) as usize;
2014                            let i1 = ((iy + 1) * chunk.height_map_size.x + ix) as usize;
2015                            let i2 = ((iy + 1) * chunk.height_map_size.x + ix + 1) as usize;
2016                            let i3 = (iy * chunk.height_map_size.x + ix + 1) as usize;
2017
2018                            let v0 = Vector3::new(
2019                                pixel_position.x,
2020                                height_map[i0],
2021                                pixel_position.y, // Remember Z -> Y mapping!
2022                            );
2023                            let v1 = Vector3::new(v0.x, height_map[i1], v0.z + cell_length);
2024                            let v2 = Vector3::new(v1.x + cell_width, height_map[i2], v1.z);
2025                            let v3 = Vector3::new(v0.x + cell_width, height_map[i3], v0.z);
2026
2027                            for vertices in &[[v0, v1, v2], [v2, v3, v0]] {
2028                                if let Some((toi, intersection)) =
2029                                    local_ray.triangle_intersection(vertices)
2030                                {
2031                                    let normal = (vertices[2] - vertices[0])
2032                                        .cross(&(vertices[1] - vertices[0]))
2033                                        .try_normalize(f32::EPSILON)
2034                                        .unwrap_or_else(Vector3::y);
2035
2036                                    let result = TerrainRayCastResult {
2037                                        position: self
2038                                            .global_transform()
2039                                            .transform_point(&Point3::from(intersection))
2040                                            .coords,
2041                                        height: intersection.y,
2042                                        normal,
2043                                        chunk_index,
2044                                        toi,
2045                                    };
2046
2047                                    if results.try_push(result).is_err() {
2048                                        break 'chunk_loop;
2049                                    }
2050                                }
2051                            }
2052                        }
2053                    }
2054                }
2055            }
2056        }
2057
2058        if sort_results {
2059            results.sort_unstable_by(|a, b| {
2060                if a.toi > b.toi {
2061                    Ordering::Greater
2062                } else if a.toi < b.toi {
2063                    Ordering::Less
2064                } else {
2065                    Ordering::Equal
2066                }
2067            });
2068        }
2069
2070        !results.is_empty()
2071    }
2072
2073    /// Sets new terrain layers.
2074    pub fn set_layers(&mut self, layers: Vec<Layer>) -> Vec<Layer> {
2075        self.layers.set_value_and_mark_modified(layers)
2076    }
2077
2078    /// Returns a reference to a slice with layers of the terrain.
2079    pub fn layers(&self) -> &[Layer] {
2080        &self.layers
2081    }
2082
2083    /// Returns a mutable reference to a slice with layers of the terrain.
2084    pub fn layers_mut(&mut self) -> &mut [Layer] {
2085        self.layers.get_value_mut_and_mark_modified()
2086    }
2087
2088    /// Adds new layer to the chunk. It is possible to have different layer count per chunk
2089    /// in the same terrain, however it seems to not have practical usage, so try to keep
2090    /// equal layer count per each chunk in your terrains.
2091    pub fn add_layer(&mut self, layer: Layer, masks: Vec<TextureResource>) {
2092        self.insert_layer(layer, masks, self.layers.len())
2093    }
2094
2095    /// Removes a layer at the given index together with its respective blending masks from each chunk.
2096    pub fn remove_layer(&mut self, layer_index: usize) -> (Layer, Vec<TextureResource>) {
2097        let layer = self
2098            .layers
2099            .get_value_mut_and_mark_modified()
2100            .remove(layer_index);
2101        let mut layer_masks = Vec::new();
2102        for chunk in self.chunks_mut() {
2103            layer_masks.push(chunk.layer_masks.remove(layer_index));
2104        }
2105        (layer, layer_masks)
2106    }
2107
2108    /// Removes last terrain layer together with its respective blending masks from each chunk.
2109    pub fn pop_layer(&mut self) -> Option<(Layer, Vec<TextureResource>)> {
2110        if self.layers.is_empty() {
2111            None
2112        } else {
2113            Some(self.remove_layer(self.layers.len() - 1))
2114        }
2115    }
2116
2117    /// Inserts the layer at the given index together with its blending masks for each chunk.
2118    pub fn insert_layer(&mut self, layer: Layer, mut masks: Vec<TextureResource>, index: usize) {
2119        self.layers
2120            .get_value_mut_and_mark_modified()
2121            .insert(index, layer);
2122
2123        for chunk in self.chunks.iter_mut().rev() {
2124            if let Some(mask) = masks.pop() {
2125                chunk.layer_masks.insert(index, mask);
2126            } else {
2127                chunk.layer_masks.insert(
2128                    index,
2129                    create_layer_mask(
2130                        self.mask_size.x,
2131                        self.mask_size.y,
2132                        if index == 0 { 255 } else { 0 },
2133                    ),
2134                )
2135            }
2136        }
2137    }
2138
2139    fn resize_masks(&mut self, mut new_size: Vector2<u32>) {
2140        new_size = new_size.sup(&Vector2::repeat(1));
2141
2142        for chunk in self.chunks.iter_mut() {
2143            for mask in chunk.layer_masks.iter_mut() {
2144                let data = mask.data_ref();
2145                let new_mask = resize_u8(data.data().to_vec(), *self.mask_size, new_size);
2146                let new_mask_texture = TextureResource::from_bytes(
2147                    Uuid::new_v4(),
2148                    TextureKind::Rectangle {
2149                        width: new_size.x,
2150                        height: new_size.y,
2151                    },
2152                    data.pixel_kind(),
2153                    new_mask,
2154                    ResourceKind::Embedded,
2155                )
2156                .unwrap();
2157
2158                drop(data);
2159                *mask = new_mask_texture;
2160            }
2161        }
2162
2163        self.mask_size.set_value_and_mark_modified(new_size);
2164    }
2165
2166    fn resize_height_maps(&mut self, mut new_size: Vector2<u32>) {
2167        // Height map dimensions should be a 3 + a power of 2 and they should be at least 5x5,
2168        // since two pixels along each edge are duplicated from neighboring chunks.
2169        new_size = new_size.sup(&Vector2::repeat(5));
2170        let hole_size = self.hole_mask_size();
2171        let new_hole_size = new_size.map(|x| x - 3);
2172
2173        for chunk in self.chunks.iter_mut() {
2174            let texture = chunk.heightmap.as_ref().unwrap().data_ref();
2175            let heightmap = texture.data_of_type::<f32>().unwrap().to_vec();
2176
2177            let resampled_heightmap = resize_f32(heightmap, chunk.height_map_size, new_size);
2178            drop(texture);
2179            chunk.heightmap = Some(make_height_map_texture(resampled_heightmap, new_size));
2180            if self.holes_enabled {
2181                let texture = chunk.hole_mask.as_ref().map(|t| t.data_ref());
2182                let data = texture.and_then(|t| t.data_of_type::<u8>().map(|t| t.to_vec()));
2183                if let Some(data) = data {
2184                    let resampled = resize_u8(data, hole_size, new_hole_size);
2185                    chunk.hole_mask = Some(make_hole_texture(resampled, new_hole_size));
2186                } else {
2187                    chunk.hole_mask = Some(make_blank_hole_texture(new_hole_size));
2188                }
2189            }
2190            chunk.height_map_size = new_size;
2191        }
2192        self.height_map_size.set_value_and_mark_modified(new_size);
2193
2194        // Re-establish alignment of edges and margins.
2195        for grid_position in self
2196            .chunks
2197            .iter()
2198            .map(|c| c.grid_position)
2199            .collect::<Vec<_>>()
2200        {
2201            self.align_chunk_margins(grid_position);
2202            self.align_chunk_edges(grid_position);
2203        }
2204        self.update_quad_trees();
2205
2206        self.bounding_box_dirty.set(true);
2207    }
2208
2209    /// Returns data for rendering (vertex and index buffers).
2210    pub fn geometry(&self) -> &TerrainGeometry {
2211        &self.geometry
2212    }
2213    /// Create an object that specifies which TextureResources are being used by this terrain
2214    /// to hold the data for the given BrushTarget.
2215    /// Panics if `target` is `HoleMask` and a chunk is missing its hole mask texture.
2216    pub fn texture_data(&self, target: BrushTarget) -> TerrainTextureData {
2217        let chunk_size = match target {
2218            BrushTarget::HeightMap => self.height_map_size(),
2219            BrushTarget::LayerMask { .. } => self.mask_size(),
2220            BrushTarget::HoleMask => self.hole_mask_size(),
2221        };
2222        let kind = match target {
2223            BrushTarget::HeightMap => TerrainTextureKind::Height,
2224            BrushTarget::LayerMask { .. } => TerrainTextureKind::Mask,
2225            BrushTarget::HoleMask => TerrainTextureKind::Mask,
2226        };
2227        let resources: FxHashMap<Vector2<i32>, TextureResource> = match target {
2228            BrushTarget::HeightMap => self
2229                .chunks_ref()
2230                .iter()
2231                .map(|c| (c.grid_position(), c.heightmap().clone()))
2232                .collect(),
2233            BrushTarget::HoleMask => self
2234                .chunks_ref()
2235                .iter()
2236                .map(|c| {
2237                    (
2238                        c.grid_position(),
2239                        c.hole_mask
2240                            .as_ref()
2241                            .cloned()
2242                            .expect("Missing hole mask texture"),
2243                    )
2244                })
2245                .collect(),
2246            BrushTarget::LayerMask { layer } => self
2247                .chunks_ref()
2248                .iter()
2249                .map(|c| (c.grid_position(), c.layer_masks[layer].clone()))
2250                .collect(),
2251        };
2252        TerrainTextureData {
2253            chunk_size,
2254            kind,
2255            resources,
2256        }
2257    }
2258    /// Modify the given BrushStroke so that it is using the given Brush and it is modifying this terrain.
2259    /// The BrushStroke will now hold references to the textures of this terrain for the target of the given brush,
2260    /// and so the stroke should not be used with other terrains until the stroke is finished.
2261    /// - `brush`: The Brush containing the brush shape and painting operation to perform.
2262    /// - `stroke`: The BrushStroke object to be reset to start a new stroke.
2263    fn start_stroke(&self, brush: Brush, stroke: &mut BrushStroke) {
2264        let target = brush.target;
2265        if brush.target == BrushTarget::HoleMask && !self.holes_enabled {
2266            Log::err("Invalid brush stroke. Holes are not enabled on terrain.");
2267            return;
2268        }
2269        stroke.start_stroke(brush, self.handle(), self.texture_data(target))
2270    }
2271    /// Modify the given BrushStroke to include a stamp of its brush at the given position.
2272    /// The location of the stamp relative to the textures is determined based on the global position
2273    /// of the terrain and the size of each terrain pixel.
2274    /// - `position`: The position of the brush in world coordinates.
2275    /// - `value`: The value of the brush stroke, whose meaning depends on the brush operation.
2276    /// For flatten brush operations, this is the target value to flatten toward.
2277    /// - `stroke`: The BrushStroke object to be modified.
2278    fn stamp(&self, position: Vector3<f32>, value: f32, stroke: &mut BrushStroke) {
2279        let Some(position) = self.project(position) else {
2280            return;
2281        };
2282        let position = match stroke.brush().target {
2283            BrushTarget::HeightMap => self.local_to_height_pixel(position),
2284            BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(position),
2285            BrushTarget::HoleMask => self.local_to_hole_pixel(position),
2286        };
2287        let scale = match stroke.brush().target {
2288            BrushTarget::HeightMap => self.height_grid_scale(),
2289            BrushTarget::LayerMask { .. } => self.mask_grid_scale(),
2290            BrushTarget::HoleMask => self.hole_grid_scale(),
2291        };
2292        stroke.stamp(position, scale, value);
2293    }
2294    /// Modify the given BrushStroke to include a stamp of its brush at the given position.
2295    /// The location of the stamp relative to the textures is determined based on the global position
2296    /// of the terrain and the size of each terrain pixel.
2297    /// - `start`: The start of the smear in world coordinates.
2298    /// - `end`: The end of the smear in world coordinates.
2299    /// - `value`: The value of the brush stroke, whose meaning depends on the brush operation.
2300    /// For flatten brush operations, this is the target value to flatten toward.
2301    /// - `stroke`: The BrushStroke object to be modified.
2302    fn smear(&self, start: Vector3<f32>, end: Vector3<f32>, value: f32, stroke: &mut BrushStroke) {
2303        let Some(start) = self.project(start) else {
2304            return;
2305        };
2306        let Some(end) = self.project(end) else {
2307            return;
2308        };
2309        let start = match stroke.brush().target {
2310            BrushTarget::HeightMap => self.local_to_height_pixel(start),
2311            BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(start),
2312            BrushTarget::HoleMask => self.local_to_hole_pixel(start),
2313        };
2314        let end = match stroke.brush().target {
2315            BrushTarget::HeightMap => self.local_to_height_pixel(end),
2316            BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(end),
2317            BrushTarget::HoleMask => self.local_to_hole_pixel(end),
2318        };
2319        let scale = match stroke.brush().target {
2320            BrushTarget::HeightMap => self.height_grid_scale(),
2321            BrushTarget::LayerMask { .. } => self.mask_grid_scale(),
2322            BrushTarget::HoleMask => self.hole_grid_scale(),
2323        };
2324        stroke.smear(start, end, scale, value);
2325    }
2326}
2327
2328/// True if the given number is a power of two.
2329fn is_power_of_two(x: u32) -> bool {
2330    x != 0 && (x & (x - 1)) == 0
2331}
2332
2333fn validate_height_map_size(x: u32, size: Vector2<u32>) -> Result<(), String> {
2334    if is_power_of_two(x - 3) {
2335        return Ok(());
2336    }
2337    let mut suggestion = 2;
2338    while suggestion + 3 < x {
2339        suggestion *= 2;
2340    }
2341    Err(format!(
2342        "Height map size ({}, {}): {} is not 3 plus a power of 2. Consider: {}",
2343        size.x,
2344        size.y,
2345        x,
2346        suggestion + 3
2347    ))
2348}
2349
2350fn validate_block_size(x: u32, size: Vector2<u32>) -> Result<(), String> {
2351    if is_power_of_two(x - 1) {
2352        return Ok(());
2353    }
2354    let mut suggestion = 2;
2355    while suggestion + 1 < x {
2356        suggestion *= 2;
2357    }
2358    Err(format!(
2359        "Block size ({}, {}): {} is not 1 plus a power of 2. Consider: {}",
2360        size.x,
2361        size.y,
2362        x,
2363        suggestion + 1
2364    ))
2365}
2366
2367fn create_terrain_layer_material() -> MaterialResource {
2368    let mut material = Material::standard_terrain();
2369    material.set_property("texCoordScale", Vector2::new(10.0, 10.0));
2370    MaterialResource::new_ok(Uuid::new_v4(), Default::default(), material)
2371}
2372
2373impl ConstructorProvider<Node, Graph> for Terrain {
2374    fn constructor() -> NodeConstructor {
2375        NodeConstructor::new::<Self>().with_variant("Terrain", |_| {
2376            TerrainBuilder::new(BaseBuilder::new().with_name("Terrain"))
2377                .with_layers(vec![Layer {
2378                    material: create_terrain_layer_material(),
2379                    ..Default::default()
2380                }])
2381                .build_node()
2382                .into()
2383        })
2384    }
2385}
2386
2387impl NodeTrait for Terrain {
2388    /// Returns pre-cached bounding axis-aligned bounding box of the terrain. Keep in mind that
2389    /// if you're modified terrain, bounding box will be recalculated and it is not fast.
2390    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
2391        if self.bounding_box_dirty.get() {
2392            let mut max_height = -f32::MAX;
2393            let mut min_height = f32::MAX;
2394            for chunk in self.chunks.iter() {
2395                let texture = chunk.heightmap.as_ref().unwrap().data_ref();
2396                let height_map = texture.data_of_type::<f32>().unwrap();
2397                for &height in height_map {
2398                    if height > max_height {
2399                        max_height = height;
2400                    }
2401                    if height < min_height {
2402                        min_height = height;
2403                    }
2404                }
2405            }
2406
2407            let bounding_box = AxisAlignedBoundingBox::from_min_max(
2408                Vector3::new(
2409                    self.chunk_size.x * self.width_chunks.start as f32,
2410                    min_height,
2411                    self.chunk_size.y * self.length_chunks.start as f32,
2412                ),
2413                Vector3::new(
2414                    self.chunk_size.x * self.width_chunks.end as f32,
2415                    max_height,
2416                    self.chunk_size.y * self.length_chunks.end as f32,
2417                ),
2418            );
2419            self.bounding_box.set(bounding_box);
2420            self.bounding_box_dirty.set(false);
2421
2422            bounding_box
2423        } else {
2424            self.bounding_box.get()
2425        }
2426    }
2427
2428    /// Returns current **world-space** bounding box.
2429    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
2430        self.local_bounding_box()
2431            .transform(&self.global_transform())
2432    }
2433
2434    fn id(&self) -> Uuid {
2435        Self::type_uuid()
2436    }
2437
2438    fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
2439        if *self.render_mask & ctx.render_mask == BitMask::none() {
2440            return RdcControlFlow::Continue;
2441        }
2442
2443        if !self.global_visibility()
2444            || !self.is_globally_enabled()
2445            || (self.frustum_culling()
2446                && !ctx
2447                    .frustum
2448                    .is_none_or(|f| f.is_intersects_aabb(&self.world_bounding_box())))
2449        {
2450            return RdcControlFlow::Continue;
2451        }
2452
2453        if renderer::is_shadow_pass(ctx.render_pass_name) && !self.cast_shadows() {
2454            return RdcControlFlow::Continue;
2455        }
2456
2457        for c in self.chunks.iter() {
2458            c.update();
2459        }
2460
2461        for (layer_index, layer) in self.layers().iter().enumerate() {
2462            for chunk in self.chunks_ref().iter() {
2463                // Generate a list of distances for each LOD that the terrain can render.
2464                // The first element of the list is the furthest distance, where the lowest LOD is used.
2465                // The formula used to produce this list has been chosen arbitrarily based on what seems to produce
2466                // the best results in the render.
2467                let quad_tree = chunk.quad_tree.safe_lock();
2468                let levels = (0..=quad_tree.max_level)
2469                    .map(|n| {
2470                        ctx.observer_position.z_far
2471                            * ((quad_tree.max_level - n) as f32 / quad_tree.max_level as f32)
2472                                .powf(3.0)
2473                    })
2474                    .collect::<Vec<_>>();
2475
2476                let chunk_transform =
2477                    self.global_transform() * Matrix4::new_translation(&chunk.position());
2478
2479                // Use the `levels` list and the camera position to generate a list of all the positions
2480                // and scales where instances of the terrain geometry should appear in the render.
2481                // The instances will be scaled based on the LOD that is needed at the instance's distance
2482                // according to the `levels` list.
2483                let mut selection = Vec::new();
2484                quad_tree.select(
2485                    &chunk_transform,
2486                    self.height_map_size(),
2487                    self.chunk_size(),
2488                    ctx.frustum,
2489                    ctx.observer_position.translation,
2490                    &levels,
2491                    &mut selection,
2492                );
2493
2494                let mut material = layer.material.deep_copy().data_ref().clone();
2495
2496                material.bind(
2497                    &layer.mask_property_name,
2498                    chunk.layer_masks[layer_index].clone(),
2499                );
2500                material.bind(&layer.height_map_property_name, chunk.heightmap.clone());
2501                material.bind(&layer.hole_mask_property_name, chunk.hole_mask.clone());
2502
2503                // The size of the chunk excluding the margins
2504                let size = self.height_map_size.map(|x| (x - 3) as f32);
2505                for node in selection {
2506                    // Exclude margins from node position. The node at (1,1) is actually at the origin
2507                    // of the chunk, because (0,0) is in the margin, and we do not render the margin.
2508                    let kx = (node.position.x - 1) as f32 / size.x;
2509                    let kz = (node.position.y - 1) as f32 / size.y;
2510
2511                    let kw = (node.size.x - 1) as f32 / size.x;
2512                    let kh = (node.size.y - 1) as f32 / size.y;
2513
2514                    material.set_property(
2515                        &layer.node_uv_offsets_property_name,
2516                        MaterialProperty::Vector4(Vector4::new(kx, kz, kw, kh)),
2517                    );
2518
2519                    let material = MaterialResource::new_ok(
2520                        Uuid::new_v4(),
2521                        Default::default(),
2522                        material.clone(),
2523                    );
2524
2525                    let node_transform = chunk_transform
2526                        * Matrix4::new_translation(&Vector3::new(
2527                            kx * self.chunk_size.x,
2528                            0.0,
2529                            kz * self.chunk_size.y,
2530                        ))
2531                        * Matrix4::new_nonuniform_scaling(&Vector3::new(
2532                            kw * self.chunk_size.x,
2533                            1.0,
2534                            kh * self.chunk_size.y,
2535                        ));
2536
2537                    if node.is_draw_full() {
2538                        ctx.storage.push(
2539                            &self.geometry.data,
2540                            &material,
2541                            RenderPath::Deferred,
2542                            layer_index as u64,
2543                            SurfaceInstanceData {
2544                                world_transform: node_transform,
2545                                bone_matrices: Default::default(),
2546                                blend_shapes_weights: Default::default(),
2547                                element_range: ElementRange::Full,
2548                                node_handle: self.handle(),
2549                            },
2550                        );
2551                    } else {
2552                        for (i, draw_quadrant) in node.active_quadrants.iter().enumerate() {
2553                            if *draw_quadrant {
2554                                ctx.storage.push(
2555                                    &self.geometry.data,
2556                                    &material,
2557                                    RenderPath::Deferred,
2558                                    layer_index as u64,
2559                                    SurfaceInstanceData {
2560                                        world_transform: node_transform,
2561                                        bone_matrices: Default::default(),
2562                                        blend_shapes_weights: Default::default(),
2563                                        element_range: self.geometry.quadrants[i],
2564                                        node_handle: self.handle(),
2565                                    },
2566                                );
2567                            }
2568                        }
2569                    }
2570                }
2571            }
2572        }
2573
2574        RdcControlFlow::Continue
2575    }
2576
2577    fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
2578        for chunk in self.chunks.iter() {
2579            chunk.debug_draw(&self.global_transform(), ctx)
2580        }
2581    }
2582
2583    fn validate(&self, _: &Scene) -> Result<(), String> {
2584        let h_size = self.height_map_size();
2585        validate_height_map_size(h_size.x, h_size)?;
2586        validate_height_map_size(h_size.y, h_size)?;
2587        let b_size = self.block_size();
2588        validate_block_size(b_size.x, b_size)?;
2589        validate_block_size(b_size.y, b_size)?;
2590        if b_size.x - 1 > h_size.x - 3 {
2591            return Err(format!(
2592                "Block size ({}, {}): {} is too large for height map. Consider: {}",
2593                b_size.x,
2594                b_size.y,
2595                b_size.x,
2596                h_size.x - 2
2597            ));
2598        }
2599        if b_size.y - 1 > h_size.y - 3 {
2600            return Err(format!(
2601                "Block size ({}, {}): {} is too large for height map. Consider: {}",
2602                b_size.x,
2603                b_size.y,
2604                b_size.y,
2605                h_size.y - 2
2606            ));
2607        }
2608        Ok(())
2609    }
2610}
2611
2612/// Terrain builder allows you to quickly build a terrain with required features.
2613pub struct TerrainBuilder {
2614    base_builder: BaseBuilder,
2615    holes_enabled: bool,
2616    chunk_size: Vector2<f32>,
2617    mask_size: Vector2<u32>,
2618    width_chunks: Range<i32>,
2619    length_chunks: Range<i32>,
2620    height_map_size: Vector2<u32>,
2621    block_size: Vector2<u32>,
2622    layers: Vec<Layer>,
2623}
2624
2625fn create_layer_mask(width: u32, height: u32, value: u8) -> TextureResource {
2626    let mask = TextureResource::from_bytes(
2627        Uuid::new_v4(),
2628        TextureKind::Rectangle { width, height },
2629        TexturePixelKind::R8,
2630        vec![value; (width * height) as usize],
2631        ResourceKind::Embedded,
2632    )
2633    .unwrap();
2634
2635    let mut data_ref = mask.data_ref();
2636    data_ref.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
2637    data_ref.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
2638    drop(data_ref);
2639
2640    mask
2641}
2642
2643impl TerrainBuilder {
2644    /// Creates new builder instance.
2645    pub fn new(base_builder: BaseBuilder) -> Self {
2646        Self {
2647            base_builder,
2648            holes_enabled: false,
2649            chunk_size: Vector2::new(16.0, 16.0),
2650            width_chunks: 0..2,
2651            length_chunks: 0..2,
2652            mask_size: Vector2::new(256, 256),
2653            height_map_size: Vector2::new(257, 257),
2654            block_size: Vector2::new(33, 33),
2655            layers: Default::default(),
2656        }
2657    }
2658
2659    /// Enables or disables holes from the terrain.
2660    pub fn with_holes(mut self, holes_enabled: bool) -> Self {
2661        self.holes_enabled = holes_enabled;
2662        self
2663    }
2664
2665    /// Sets desired chunk size in meters.
2666    pub fn with_chunk_size(mut self, size: Vector2<f32>) -> Self {
2667        self.chunk_size = size;
2668        self
2669    }
2670
2671    /// Sets desired mask size in pixels.
2672    pub fn with_mask_size(mut self, size: Vector2<u32>) -> Self {
2673        self.mask_size = size;
2674        self
2675    }
2676
2677    /// Sets desired chunk amount along width axis.
2678    pub fn with_width_chunks(mut self, width_chunks: Range<i32>) -> Self {
2679        self.width_chunks = width_chunks;
2680        self
2681    }
2682
2683    /// Sets desired chunk amount along length axis.
2684    pub fn with_length_chunks(mut self, length_chunks: Range<i32>) -> Self {
2685        self.length_chunks = length_chunks;
2686        self
2687    }
2688
2689    /// Sets desired height map size in pixels.
2690    pub fn with_height_map_size(mut self, size: Vector2<u32>) -> Self {
2691        self.height_map_size = size;
2692        self
2693    }
2694
2695    /// Sets desired layers that will be used for each chunk in the terrain.
2696    pub fn with_layers(mut self, layers: Vec<Layer>) -> Self {
2697        self.layers = layers;
2698        self
2699    }
2700
2701    /// Sets desired block size. Block - is a smallest renderable piece of terrain which will be used for
2702    /// level-of-detail functionality.
2703    pub fn with_block_size(mut self, block_size: Vector2<u32>) -> Self {
2704        self.block_size = block_size;
2705        self
2706    }
2707
2708    /// Build terrain node.
2709    pub fn build_node(self) -> Node {
2710        let mut chunks = Vec::new();
2711        for z in self.length_chunks.clone() {
2712            for x in self.width_chunks.clone() {
2713                let heightmap =
2714                    vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
2715                let hole_mask = if self.holes_enabled {
2716                    Some(create_layer_mask(
2717                        self.height_map_size.x - 3,
2718                        self.height_map_size.y - 3,
2719                        255,
2720                    ))
2721                } else {
2722                    None
2723                };
2724                let chunk = Chunk {
2725                    quad_tree: Mutex::new(QuadTree::new(
2726                        &heightmap,
2727                        self.height_map_size,
2728                        self.block_size,
2729                        0,
2730                    )),
2731                    height_map_size: self.height_map_size,
2732                    heightmap: Some(make_height_map_texture(heightmap, self.height_map_size)),
2733                    hole_mask,
2734                    height_map_modifications_count: 0,
2735                    position: Vector3::new(
2736                        x as f32 * self.chunk_size.x,
2737                        0.0,
2738                        z as f32 * self.chunk_size.y,
2739                    ),
2740                    physical_size: self.chunk_size,
2741                    grid_position: Vector2::new(x, z),
2742                    layer_masks: self
2743                        .layers
2744                        .iter()
2745                        .enumerate()
2746                        .map(|(i, _)| {
2747                            create_layer_mask(
2748                                self.mask_size.x,
2749                                self.mask_size.y,
2750                                // Base layer is opaque, every other by default - transparent.
2751                                if i == 0 { 255 } else { 0 },
2752                            )
2753                        })
2754                        .collect::<Vec<_>>(),
2755                    block_size: self.block_size,
2756                };
2757
2758                chunks.push(chunk);
2759            }
2760        }
2761
2762        let terrain = Terrain {
2763            chunk_size: self.chunk_size.into(),
2764            base: self.base_builder.build_base(),
2765            holes_enabled: self.holes_enabled,
2766            layers: self.layers.into(),
2767            chunks: chunks.into(),
2768            bounding_box_dirty: Cell::new(true),
2769            bounding_box: Default::default(),
2770            mask_size: self.mask_size.into(),
2771            height_map_size: self.height_map_size.into(),
2772            width_chunks: self.width_chunks.into(),
2773            length_chunks: self.length_chunks.into(),
2774            geometry: TerrainGeometry::new(self.block_size),
2775            block_size: self.block_size.into(),
2776        };
2777        Node::new(terrain)
2778    }
2779
2780    /// Builds terrain node and adds it to given graph.
2781    pub fn build(self, graph: &mut Graph) -> Handle<Terrain> {
2782        graph.add_node(self.build_node()).to_variant()
2783    }
2784}
2785
2786#[cfg(test)]
2787mod tests {
2788    use super::*;
2789
2790    #[test]
2791    fn power_of_two() {
2792        assert!(!is_power_of_two(0));
2793        assert!(is_power_of_two(1));
2794        assert!(is_power_of_two(2));
2795        assert!(!is_power_of_two(3));
2796        assert!(is_power_of_two(4));
2797        assert!(!is_power_of_two(5));
2798        assert!(!is_power_of_two(6));
2799        assert!(!is_power_of_two(7));
2800        assert!(is_power_of_two(8));
2801        assert!(!is_power_of_two(9));
2802        assert!(!is_power_of_two(15));
2803        assert!(is_power_of_two(16));
2804    }
2805    #[test]
2806    fn resize_1x1() {
2807        let r = resize_f32(vec![3.5], Vector2::new(1, 1), Vector2::new(2, 2));
2808        assert_eq!(r, vec![3.5, 3.5, 3.5, 3.5]);
2809    }
2810    #[test]
2811    fn resize_2x1() {
2812        let r = resize_f32(vec![1.0, 2.0], Vector2::new(2, 1), Vector2::new(3, 1));
2813        assert_eq!(r, vec![1.0, 1.5, 2.0]);
2814    }
2815    #[test]
2816    fn zero_margin_0x0() {
2817        let r = create_zero_margin(Vec::new(), Vector2::new(0, 0));
2818        assert_eq!(r, vec![0.0, 0.0, 0.0, 0.0]);
2819    }
2820    #[test]
2821    fn zero_margin_1x1() {
2822        let r = create_zero_margin(vec![3.5], Vector2::new(1, 1));
2823        assert_eq!(r, vec![0.0, 0.0, 0.0, 0.0, 3.5, 0.0, 0.0, 0.0, 0.0]);
2824    }
2825    #[test]
2826    fn zero_margin_2x1() {
2827        let r = create_zero_margin(vec![1.0, 2.0], Vector2::new(2, 1));
2828        assert_eq!(
2829            r,
2830            vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0]
2831        );
2832    }
2833    #[test]
2834    fn zero_margin_1x2() {
2835        let r = create_zero_margin(vec![1.0, 2.0], Vector2::new(1, 2));
2836        assert_eq!(
2837            r,
2838            vec![0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0]
2839        );
2840    }
2841    #[test]
2842    fn zero_margin_3x3() {
2843        let r = create_zero_margin(
2844            vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
2845            Vector2::new(3, 3),
2846        );
2847        assert_eq!(
2848            r,
2849            vec![
2850                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 0.0, 0.0, 4.0, 5.0, 6.0, 0.0, 0.0,
2851                7.0, 8.0, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
2852            ]
2853        );
2854    }
2855}