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