1use crate::{
24 asset::{Resource, ResourceDataRef},
25 core::{
26 algebra::{Matrix4, Point3, Vector2, Vector3, Vector4},
27 arrayvec::ArrayVec,
28 log::Log,
29 math::{aabb::AxisAlignedBoundingBox, ray::Ray, ray_rect_intersection, Rect},
30 parking_lot::Mutex,
31 pool::Handle,
32 reflect::prelude::*,
33 type_traits::prelude::*,
34 uuid::{uuid, Uuid},
35 variable::InheritableVariable,
36 visitor::prelude::*,
37 SafeLock,
38 },
39 graphics::ElementRange,
40 material::MaterialResourceExtension,
41 material::{Material, MaterialProperty, MaterialResource},
42 renderer::{
43 self,
44 bundle::{RenderContext, SurfaceInstanceData},
45 },
46 resource::texture::{
47 Texture, TextureDataRefMut, TextureKind, TextureMagnificationFilter,
48 TextureMinificationFilter, TexturePixelKind, TextureResource, TextureResourceExtension,
49 TextureWrapMode,
50 },
51 scene::node::RdcControlFlow,
52 scene::{
53 base::{Base, BaseBuilder},
54 debug::SceneDrawingContext,
55 graph::Graph,
56 mesh::RenderPath,
57 node::{Node, NodeTrait},
58 terrain::{geometry::TerrainGeometry, quadtree::QuadTree},
59 Scene,
60 },
61};
62use fxhash::FxHashMap;
63use fyrox_core::{uuid_provider, warn};
64use fyrox_graph::SceneGraph;
65use fyrox_resource::untyped::ResourceKind;
66use half::f16;
67use image::{imageops::FilterType, ImageBuffer, Luma};
68use std::{
69 cell::Cell,
70 cmp::Ordering,
71 collections::HashMap,
72 ops::{Deref, DerefMut, Range},
73 sync::LazyLock,
74};
75
76pub mod brushstroke;
77mod geometry;
78mod quadtree;
79
80use crate::scene::node::constructor::NodeConstructor;
81pub use brushstroke::*;
82use fyrox_graph::constructor::ConstructorProvider;
83
84use super::collider::BitMask;
85
86pub const VERSION: u8 = 0;
88
89pub static WHITE_1X1: LazyLock<TextureResource> = LazyLock::new(|| {
91 TextureResource::from_bytes(
92 uuid!("09a71013-ccb2-4a41-a48a-ab6c80f14f0e"),
93 TextureKind::Rectangle {
94 width: 1,
95 height: 1,
96 },
97 TexturePixelKind::R8,
98 vec![255],
99 ResourceKind::External,
100 )
101 .unwrap()
102});
103
104#[derive(Debug, Clone)]
106pub struct TerrainRect {
107 pub grid_position: Vector2<i32>,
109 pub bounds: Rect<f32>,
111}
112
113impl TerrainRect {
114 pub fn from_local(position: Vector2<f32>, cell_size: Vector2<f32>) -> TerrainRect {
117 let cell_pos = Vector2::new(position.x / cell_size.x, position.y / cell_size.y);
118 let cell_pos = cell_pos.map(f32::floor);
119 let min = Vector2::new(cell_pos.x * cell_size.x, cell_pos.y * cell_size.y);
120 TerrainRect {
121 grid_position: cell_pos.map(|x| x as i32),
122 bounds: Rect::new(min.x, min.y, cell_size.x, cell_size.y),
123 }
124 }
125}
126
127pub struct ChunkHeightData<'a>(pub ResourceDataRef<'a, Texture>);
133pub struct ChunkHeightMutData<'a>(pub TextureDataRefMut<'a>);
138
139impl ChunkHeightData<'_> {
140 pub fn size(&self) -> Vector2<u32> {
142 match self.0.kind() {
143 TextureKind::Rectangle { width, height } => Vector2::new(width - 2, height - 2),
144 _ => panic!("Invalid texture kind."),
145 }
146 }
147 pub fn row_size(&self) -> usize {
149 match self.0.kind() {
150 TextureKind::Rectangle { width, .. } => width as usize,
151 _ => panic!("Invalid texture kind."),
152 }
153 }
154 pub fn get(&self, position: Vector2<i32>) -> Option<f32> {
156 if self.is_valid_index(position) {
157 Some(self[position])
158 } else {
159 None
160 }
161 }
162 #[inline]
163 fn is_valid_index(&self, position: Vector2<i32>) -> bool {
164 let s = self.size();
165 (-1..=s.x as i32).contains(&position.x) && (-1..=s.y as i32).contains(&position.y)
166 }
167}
168
169impl ChunkHeightMutData<'_> {
170 pub fn size(&self) -> Vector2<u32> {
172 match self.0.kind() {
173 TextureKind::Rectangle { width, height } => Vector2::new(width - 2, height - 2),
174 _ => panic!("Invalid texture kind."),
175 }
176 }
177 pub fn row_size(&self) -> usize {
179 match self.0.kind() {
180 TextureKind::Rectangle { width, .. } => width as usize,
181 _ => panic!("Invalid texture kind."),
182 }
183 }
184 pub fn get(&self, position: Vector2<i32>) -> Option<f32> {
186 if self.is_valid_index(position) {
187 Some(self[position])
188 } else {
189 None
190 }
191 }
192 pub fn get_mut(&mut self, position: Vector2<i32>) -> Option<&mut f32> {
194 if self.is_valid_index(position) {
195 Some(&mut self[position])
196 } else {
197 None
198 }
199 }
200 #[inline]
201 fn is_valid_index(&self, position: Vector2<i32>) -> bool {
202 let s = self.size();
203 (-1..=s.x as i32).contains(&position.x) && (-1..=s.y as i32).contains(&position.y)
204 }
205}
206
207impl std::ops::Index<Vector2<i32>> for ChunkHeightData<'_> {
208 type Output = f32;
209
210 fn index(&self, position: Vector2<i32>) -> &Self::Output {
211 assert!(self.is_valid_index(position));
212 let row_size = self.row_size();
213 let x = (position.x + 1) as usize;
214 let y = (position.y + 1) as usize;
215 match self.0.data_of_type::<f32>() {
216 Some(d) => &d[y * row_size + x],
217 None => panic!("Height data type error: {:?}", self.0),
218 }
219 }
220}
221impl std::ops::Index<Vector2<i32>> for ChunkHeightMutData<'_> {
222 type Output = f32;
223
224 fn index(&self, position: Vector2<i32>) -> &Self::Output {
225 assert!(self.is_valid_index(position));
226 let row_size = self.row_size();
227 let x = (position.x + 1) as usize;
228 let y = (position.y + 1) as usize;
229 &self.0.data_of_type::<f32>().unwrap()[y * row_size + x]
230 }
231}
232impl std::ops::IndexMut<Vector2<i32>> for ChunkHeightMutData<'_> {
233 fn index_mut(&mut self, position: Vector2<i32>) -> &mut Self::Output {
234 assert!(self.is_valid_index(position));
235 let row_size = self.row_size();
236 let x = (position.x + 1) as usize;
237 let y = (position.y + 1) as usize;
238 &mut self.0.data_mut_of_type::<f32>().unwrap()[y * row_size + x]
239 }
240}
241
242#[derive(Debug, Clone, Visit, Reflect, PartialEq)]
246pub struct Layer {
247 pub material: MaterialResource,
249
250 pub mask_property_name: String,
252
253 #[visit(optional)]
255 pub height_map_property_name: String,
256
257 #[visit(optional)]
259 pub hole_mask_property_name: String,
260
261 #[visit(optional)]
263 pub node_uv_offsets_property_name: String,
264}
265
266uuid_provider!(Layer = "7439d5fd-43a9-45f0-bd7c-76cf4d2ec22e");
267
268impl Default for Layer {
269 fn default() -> Self {
270 Self {
271 material: MaterialResource::new_ok(
272 Uuid::new_v4(),
273 Default::default(),
274 Material::standard_terrain(),
275 ),
276 mask_property_name: "maskTexture".to_string(),
277 height_map_property_name: "heightMapTexture".to_string(),
278 node_uv_offsets_property_name: "nodeUvOffsets".to_string(),
279 hole_mask_property_name: "holeMaskTexture".to_string(),
280 }
281 }
282}
283
284fn make_quad_tree(
286 texture: &Option<TextureResource>,
287 height_map_size: Vector2<u32>,
288 block_size: Vector2<u32>,
289) -> QuadTree {
290 let texture = texture.as_ref().unwrap().data_ref();
291 let height_mod_count = texture.modifications_count();
292 let height_map = texture.data_of_type::<f32>().unwrap();
293 QuadTree::new(height_map, height_map_size, block_size, height_mod_count)
294}
295
296fn make_height_map_texture_internal(
301 height_map: Vec<f32>,
302 size: Vector2<u32>,
303) -> Option<TextureResource> {
304 let mut data = Texture::from_bytes(
305 TextureKind::Rectangle {
306 width: size.x,
307 height: size.y,
308 },
309 TexturePixelKind::R32F,
310 crate::core::transmute_vec_as_bytes(height_map),
311 )?;
312
313 data.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
314 data.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
315
316 Some(Resource::new_ok(Uuid::new_v4(), Default::default(), data))
317}
318
319fn make_blank_hole_texture(size: Vector2<u32>) -> TextureResource {
320 make_hole_texture(vec![255; size.x as usize * size.y as usize], size)
321}
322
323fn make_hole_texture(mask_data: Vec<u8>, size: Vector2<u32>) -> TextureResource {
324 let mut data = Texture::from_bytes(
325 TextureKind::Rectangle {
326 width: size.x,
327 height: size.y,
328 },
329 TexturePixelKind::R8,
330 mask_data,
331 )
332 .unwrap();
333 data.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
334 data.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
335 data.set_magnification_filter(TextureMagnificationFilter::Nearest);
336 data.set_minification_filter(TextureMinificationFilter::Nearest);
337 Resource::new_ok(Uuid::new_v4(), Default::default(), data)
338}
339
340fn make_height_map_texture(height_map: Vec<f32>, size: Vector2<u32>) -> TextureResource {
345 make_height_map_texture_internal(height_map, size).unwrap()
346}
347
348#[derive(Debug, Reflect)]
353pub struct Chunk {
354 #[reflect(hidden)]
355 quad_tree: Mutex<QuadTree>,
356 #[reflect(setter = "set_height_map")]
360 heightmap: Option<TextureResource>,
361 #[reflect(hidden)]
362 hole_mask: Option<TextureResource>,
363 #[reflect(hidden)]
364 position: Vector3<f32>,
365 #[reflect(hidden)]
366 physical_size: Vector2<f32>,
367 #[reflect(hidden)]
368 height_map_size: Vector2<u32>,
369 #[reflect(hidden)]
370 block_size: Vector2<u32>,
371 #[reflect(hidden)]
372 grid_position: Vector2<i32>,
373 #[reflect(hidden)]
375 pub layer_masks: Vec<TextureResource>,
376 #[reflect(hidden)]
377 height_map_modifications_count: u64,
378}
379
380uuid_provider!(Chunk = "ae996754-69c1-49ba-9c17-a7bd4be072a9");
381
382impl PartialEq for Chunk {
383 fn eq(&self, other: &Self) -> bool {
384 self.heightmap == other.heightmap
385 && self.height_map_size == other.height_map_size
386 && self.grid_position == other.grid_position
387 && self.layer_masks == other.layer_masks
388 }
389}
390
391impl Clone for Chunk {
392 fn clone(&self) -> Self {
394 Self {
395 heightmap: Some(self.heightmap.as_ref().unwrap().deep_clone()),
396 hole_mask: self.hole_mask.as_ref().map(Resource::deep_clone),
397 position: self.position,
398 physical_size: self.physical_size,
399 height_map_size: self.height_map_size,
400 block_size: self.block_size,
401 grid_position: self.grid_position,
402 layer_masks: self
403 .layer_masks
404 .iter()
405 .map(|m| m.deep_clone())
406 .collect::<Vec<_>>(),
407 quad_tree: Mutex::new(make_quad_tree(
408 &self.heightmap,
409 self.height_map_size,
410 self.block_size,
411 )),
412 height_map_modifications_count: self.height_map_modifications_count,
413 }
414 }
415}
416
417impl Visit for Chunk {
419 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
420 let mut region = visitor.enter_region(name)?;
421
422 let mut version = VERSION;
423 version.visit("Version", &mut region)?;
424
425 if let VERSION = version {
426 self.heightmap.visit("Heightmap", &mut region)?;
427 self.hole_mask.visit("HoleMask", &mut region)?;
428 self.physical_size.visit("PhysicalSize", &mut region)?;
431 self.height_map_size.visit("HeightMapSize", &mut region)?;
432 self.layer_masks.visit("LayerMasks", &mut region)?;
433 self.grid_position.visit("GridPosition", &mut region)?;
434 if region.is_reading() {
436 self.position = self.position()
437 }
438 self.block_size.visit("BlockSize", &mut region)?;
439 }
440
441 self.quad_tree = Mutex::new(make_quad_tree(
442 &self.heightmap,
443 self.height_map_size,
444 self.block_size,
445 ));
446
447 Ok(())
448 }
449}
450
451impl Default for Chunk {
452 fn default() -> Self {
453 Self {
454 quad_tree: Default::default(),
455 heightmap: Default::default(),
456 hole_mask: Default::default(),
457 position: Default::default(),
458 physical_size: Default::default(),
459 height_map_size: Default::default(),
460 block_size: Vector2::new(32, 32),
461 grid_position: Default::default(),
462 layer_masks: Default::default(),
463 height_map_modifications_count: 0,
464 }
465 }
466}
467
468impl Chunk {
469 pub fn height_data(&self) -> ChunkHeightData {
471 ChunkHeightData(self.heightmap.as_ref().map(|r| r.data_ref()).unwrap())
472 }
473
474 pub fn create_margin(&mut self) {
477 let data = self.heightmap.as_ref().map(|r| r.data_ref()).unwrap();
478 let size = match data.kind() {
479 TextureKind::Rectangle { width, height } => Vector2::new(width, height),
480 _ => panic!("Texture is not rectangle"),
481 };
482 let data_f32 = From::<&[f32]>::from(data.data_of_type().unwrap());
483 let result = create_zero_margin(data_f32, size);
484 drop(data);
485 self.heightmap = Some(make_height_map_texture(result, size.map(|x| x + 2)));
486 self.height_map_size = self.height_map_size.map(|x| x + 2);
487 }
488 pub fn update(&self) {
490 let Some(heightmap) = self.heightmap.as_ref() else {
491 return;
492 };
493 let count = heightmap.data_ref().modifications_count();
494 let mut quad_tree = self.quad_tree.safe_lock();
495 if count != quad_tree.height_mod_count() {
496 *quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
497 }
498 }
499 pub fn local_position(&self) -> Vector2<f32> {
502 map_to_local(self.position())
503 }
504
505 pub fn position(&self) -> Vector3<f32> {
507 Vector3::new(
508 self.grid_position.x as f32 * self.physical_size.x,
509 0.0,
510 self.grid_position.y as f32 * self.physical_size.y,
511 )
512 }
513
514 #[inline]
516 pub fn grid_position(&self) -> Vector2<i32> {
517 self.grid_position
518 }
519
520 pub fn heightmap(&self) -> &TextureResource {
522 self.heightmap.as_ref().unwrap()
523 }
524
525 pub fn set_height_map(
538 &mut self,
539 height_map: Option<TextureResource>,
540 ) -> Option<TextureResource> {
541 if let Some(new_height_map) = height_map {
542 let mut state = new_height_map.state();
543 if let Some(new_height_map_texture) = state.data() {
544 if let TextureKind::Rectangle { width, height } = new_height_map_texture.kind() {
545 if width == self.height_map_size.x && height == self.height_map_size.y {
546 fn convert<T, C>(texture: &Texture, mut mapper: C) -> Option<Vec<f32>>
547 where
548 T: Sized,
549 C: Fn(&T) -> f32,
550 {
551 texture
552 .mip_level_data_of_type::<T>(0)
553 .map(|v| v.iter().map(&mut mapper).collect::<Vec<_>>())
554 }
555
556 let pixels = match new_height_map_texture.pixel_kind() {
558 TexturePixelKind::R8 | TexturePixelKind::Luminance8 => {
559 convert::<u8, _>(new_height_map_texture, |v| {
560 *v as f32 / u8::MAX as f32
561 })
562 }
563 TexturePixelKind::RGB8 => {
564 #[repr(C)]
565 struct Rgb8 {
566 r: u8,
567 g: u8,
568 b: u8,
569 }
570 convert::<Rgb8, _>(new_height_map_texture, |v| {
571 v.r as f32 / u8::MAX as f32
572 })
573 }
574 TexturePixelKind::RGBA8 => {
575 #[repr(C)]
576 struct Rgba8 {
577 r: u8,
578 g: u8,
579 b: u8,
580 a: u8,
581 }
582 convert::<Rgba8, _>(new_height_map_texture, |v| {
583 v.r as f32 / u8::MAX as f32
584 })
585 }
586 TexturePixelKind::RG8 | TexturePixelKind::LuminanceAlpha8 => {
587 #[repr(C)]
588 struct Rg8 {
589 r: u8,
590 g: u8,
591 }
592 convert::<Rg8, _>(new_height_map_texture, |v| {
593 v.r as f32 / u8::MAX as f32
594 })
595 }
596 TexturePixelKind::R16 | TexturePixelKind::Luminance16 => {
597 convert::<u16, _>(new_height_map_texture, |v| {
598 *v as f32 / u16::MAX as f32
599 })
600 }
601 TexturePixelKind::RG16 | TexturePixelKind::LuminanceAlpha16 => {
602 #[repr(C)]
603 struct Rg16 {
604 r: u16,
605 g: u16,
606 }
607 convert::<Rg16, _>(new_height_map_texture, |v| {
608 v.r as f32 / u16::MAX as f32
609 })
610 }
611 TexturePixelKind::BGR8 => {
612 #[repr(C)]
613 struct Bgr8 {
614 b: u8,
615 g: u8,
616 r: u8,
617 }
618 convert::<Bgr8, _>(new_height_map_texture, |v| {
619 v.r as f32 / u8::MAX as f32
620 })
621 }
622 TexturePixelKind::BGRA8 => {
623 #[repr(C)]
624 struct Bgra8 {
625 r: u8,
626 g: u8,
627 b: u8,
628 a: u8,
629 }
630 convert::<Bgra8, _>(new_height_map_texture, |v| {
631 v.r as f32 / u8::MAX as f32
632 })
633 }
634 TexturePixelKind::RGB16 => {
635 #[repr(C)]
636 struct Rgb16 {
637 r: u16,
638 g: u16,
639 b: u16,
640 }
641 convert::<Rgb16, _>(new_height_map_texture, |v| {
642 v.r as f32 / u16::MAX as f32
643 })
644 }
645 TexturePixelKind::RGBA16 => {
646 #[repr(C)]
647 struct Rgba16 {
648 r: u16,
649 g: u16,
650 b: u16,
651 a: u16,
652 }
653 convert::<Rgba16, _>(new_height_map_texture, |v| {
654 v.r as f32 / u16::MAX as f32
655 })
656 }
657 TexturePixelKind::RGB32F => {
658 #[repr(C)]
659 struct Rgb32F {
660 r: f32,
661 g: f32,
662 b: f32,
663 }
664 convert::<Rgb32F, _>(new_height_map_texture, |v| v.r)
665 }
666 TexturePixelKind::RGBA32F => {
667 #[repr(C)]
668 struct Rgba32F {
669 r: f32,
670 g: f32,
671 b: f32,
672 a: f32,
673 }
674 convert::<Rgba32F, _>(new_height_map_texture, |v| v.r)
675 }
676 TexturePixelKind::RGB16F => {
677 #[repr(C)]
678 struct Rgb16F {
679 r: f16,
680 g: f16,
681 b: f16,
682 }
683 convert::<Rgb16F, _>(new_height_map_texture, |v| v.r.to_f32())
684 }
685 TexturePixelKind::R32F => {
686 convert::<f32, _>(new_height_map_texture, |v| *v)
687 }
688 TexturePixelKind::R16F => {
689 convert::<f16, _>(new_height_map_texture, |v| v.to_f32())
690 }
691 _ => None,
692 };
693
694 if let Some(pixels) = pixels {
695 if let Some(texture) =
696 make_height_map_texture_internal(pixels, self.height_map_size)
697 {
698 let prev_texture = self.heightmap.replace(texture);
699 self.update_quad_tree();
700 return prev_texture;
701 }
702 } else {
703 warn!(
704 "Unable to convert input texture into single-channel height map!\
705 Input texture format is {:?}.",
706 new_height_map_texture.pixel_kind()
707 )
708 }
709 } else {
710 warn!(
711 "The size of the texture must match the height map size! \
712 Input texture size: {width}x{height}, but the height map size is \
713 {}x{}",
714 self.height_map_size.x, self.height_map_size.y
715 );
716 }
717 } else {
718 warn!(
719 "Height map can be set only from 2D textures! The input texture is {:?}",
720 new_height_map_texture.kind()
721 )
722 }
723 } else {
724 warn!("The input texture is in invalid state (unloaded)!")
725 }
726 }
727
728 self.heightmap.clone()
730 }
731
732 pub fn heightmap_owned(&self) -> Vec<f32> {
734 self.heightmap
735 .as_ref()
736 .unwrap()
737 .data_ref()
738 .data_of_type::<f32>()
739 .unwrap()
740 .to_vec()
741 }
742
743 pub fn replace_height_map(
745 &mut self,
746 heightmap: TextureResource,
747 ) -> Result<(), TextureResource> {
748 let data = heightmap.data_ref();
749 if let TextureKind::Rectangle { width, height } = data.kind() {
750 if data.pixel_kind() == TexturePixelKind::R32F
751 && self.height_map_size.x == width
752 && self.height_map_size.y == height
753 {
754 drop(data);
755 self.heightmap = Some(heightmap);
756 self.update_quad_tree();
757 return Ok(());
758 }
759 }
760 drop(data);
761 Err(heightmap)
762 }
763
764 pub fn hole_mask(&self) -> Option<&TextureResource> {
766 self.hole_mask.as_ref()
767 }
768
769 pub fn physical_size(&self) -> Vector2<f32> {
771 self.physical_size
772 }
773
774 pub fn height_map_size(&self) -> Vector2<u32> {
776 self.height_map_size
777 }
778
779 pub fn hole_mask_size(&self) -> Vector2<u32> {
781 self.height_map_size.map(|x| x - 3)
782 }
783
784 pub fn debug_draw(&self, transform: &Matrix4<f32>, ctx: &mut SceneDrawingContext) {
786 let transform = *transform * Matrix4::new_translation(&self.position());
787
788 self.quad_tree.safe_lock().debug_draw(
789 &transform,
790 self.height_map_size,
791 self.physical_size,
792 ctx,
793 )
794 }
795
796 fn set_block_size(&mut self, block_size: Vector2<u32>) {
797 self.block_size = block_size;
798 self.update_quad_tree();
799 }
800
801 pub fn update_quad_tree(&self) {
803 if self.heightmap.is_none() {
804 return;
805 }
806 *self.quad_tree.safe_lock() =
807 make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
808 }
809}
810
811fn map_to_local(v: Vector3<f32>) -> Vector2<f32> {
812 Vector2::new(v.x, v.z)
814}
815
816#[derive(Debug)]
818pub struct TerrainRayCastResult {
819 pub position: Vector3<f32>,
821 pub height: f32,
824 pub normal: Vector3<f32>,
826 pub chunk_index: usize,
828 pub toi: f32,
830}
831
832#[derive(Default)]
844pub struct BrushContext {
845 pub value: Option<f32>,
849 pub stroke: BrushStroke,
851}
852
853impl BrushContext {
854 pub fn brush(&self) -> &Brush {
861 self.stroke.brush()
862 }
863 pub fn shape(&mut self) -> &mut BrushShape {
866 self.stroke.shape()
867 }
868 pub fn mode(&mut self) -> &mut BrushMode {
871 self.stroke.mode()
872 }
873 pub fn hardness(&mut self) -> &mut f32 {
876 self.stroke.hardness()
877 }
878 pub fn alpha(&mut self) -> &mut f32 {
881 self.stroke.alpha()
882 }
883 pub fn start_stroke(&mut self, terrain: &Terrain, brush: Brush) {
889 self.value = None;
890 terrain.start_stroke(brush, &mut self.stroke);
891 }
892 pub fn stamp(&mut self, terrain: &Terrain, position: Vector3<f32>) {
899 let value = if matches!(self.stroke.brush().mode, BrushMode::Flatten) {
900 self.interpolate_value(terrain, position)
901 } else {
902 0.0
903 };
904 terrain.stamp(position, value, &mut self.stroke);
905 }
906 pub fn smear(&mut self, terrain: &Terrain, start: Vector3<f32>, end: Vector3<f32>) {
914 let value = if matches!(self.stroke.brush().mode, BrushMode::Flatten) {
915 self.interpolate_value(terrain, start)
916 } else {
917 0.0
918 };
919 terrain.smear(start, end, value, &mut self.stroke);
920 }
921 pub fn flush(&mut self) {
923 self.stroke.flush();
924 }
925 pub fn end_stroke(&mut self) {
928 self.stroke.end_stroke();
929 }
930}
931
932impl BrushContext {
933 fn interpolate_value(&mut self, terrain: &Terrain, position: Vector3<f32>) -> f32 {
934 if let Some(v) = self.value {
935 return v;
936 }
937 let Some(position) = terrain.project(position) else {
938 return 0.0;
939 };
940 let target = self.stroke.brush().target;
941 let v = terrain.interpolate_value(position, target);
942 self.value = Some(v);
943 v
944 }
945}
946
947#[derive(Debug, Reflect, Clone, ComponentProvider)]
1036#[reflect(derived_type = "Node")]
1037pub struct Terrain {
1038 base: Base,
1039
1040 #[reflect(setter = "set_holes_enabled")]
1041 holes_enabled: bool,
1042
1043 #[reflect(setter = "set_layers")]
1044 layers: InheritableVariable<Vec<Layer>>,
1045
1046 #[reflect(min_value = 0.001, setter = "set_chunk_size")]
1049 chunk_size: InheritableVariable<Vector2<f32>>,
1050
1051 #[reflect(step = 1.0, setter = "set_width_chunks")]
1054 width_chunks: InheritableVariable<Range<i32>>,
1055
1056 #[reflect(step = 1.0, setter = "set_length_chunks")]
1059 length_chunks: InheritableVariable<Range<i32>>,
1060
1061 #[reflect(min_value = 2.0, step = 1.0, setter = "set_height_map_size")]
1070 height_map_size: InheritableVariable<Vector2<u32>>,
1071
1072 #[reflect(min_value = 8.0, step = 1.0, setter = "set_block_size")]
1079 block_size: InheritableVariable<Vector2<u32>>,
1080
1081 #[reflect(min_value = 1.0, step = 1.0, setter = "set_mask_size")]
1083 mask_size: InheritableVariable<Vector2<u32>>,
1084
1085 #[reflect(immutable_collection)]
1086 chunks: InheritableVariable<Vec<Chunk>>,
1087
1088 #[reflect(hidden)]
1089 bounding_box_dirty: Cell<bool>,
1090
1091 #[reflect(hidden)]
1092 bounding_box: Cell<AxisAlignedBoundingBox>,
1093
1094 #[reflect(hidden)]
1097 geometry: TerrainGeometry,
1098}
1099
1100impl Default for Terrain {
1101 fn default() -> Self {
1102 Self {
1103 base: Default::default(),
1104 holes_enabled: false,
1105 layers: Default::default(),
1106 chunk_size: Vector2::new(16.0, 16.0).into(),
1107 width_chunks: Default::default(),
1108 length_chunks: Default::default(),
1109 height_map_size: Vector2::new(259, 259).into(),
1110 block_size: Vector2::new(33, 33).into(),
1111 mask_size: Vector2::new(256, 256).into(),
1112 chunks: Default::default(),
1113 bounding_box_dirty: Cell::new(true),
1114 bounding_box: Cell::new(Default::default()),
1115 geometry: Default::default(),
1116 }
1117 }
1118}
1119
1120impl Visit for Terrain {
1121 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
1122 let mut region = visitor.enter_region(name)?;
1123
1124 let mut version = VERSION;
1125 version.visit("Version", &mut region)?;
1126
1127 if let VERSION = version {
1128 self.base.visit("Base", &mut region)?;
1130 self.holes_enabled.visit("HolesEnabled", &mut region)?;
1131 self.layers.visit("Layers", &mut region)?;
1132 self.chunk_size.visit("ChunkSize", &mut region)?;
1133 self.width_chunks.visit("WidthChunks", &mut region)?;
1134 self.length_chunks.visit("LengthChunks", &mut region)?;
1135 self.height_map_size.visit("HeightMapSize", &mut region)?;
1136 self.block_size.visit("BlockSize", &mut region)?;
1137 self.mask_size.visit("MaskSize", &mut region)?;
1138 self.chunks.visit("Chunks", &mut region)?;
1139 }
1140
1141 if region.is_reading() {
1142 self.geometry = TerrainGeometry::new(*self.block_size);
1143 }
1144
1145 Ok(())
1146 }
1147}
1148
1149impl Deref for Terrain {
1150 type Target = Base;
1151
1152 fn deref(&self) -> &Self::Target {
1153 &self.base
1154 }
1155}
1156
1157impl DerefMut for Terrain {
1158 fn deref_mut(&mut self) -> &mut Self::Target {
1159 &mut self.base
1160 }
1161}
1162
1163fn project(global_transform: Matrix4<f32>, p: Vector3<f32>) -> Option<Vector2<f32>> {
1164 if let Some(inv_global_transform) = global_transform.try_inverse() {
1166 let local_p = inv_global_transform
1167 .transform_point(&Point3::from(p))
1168 .coords;
1169 Some(map_to_local(local_p))
1170 } else {
1171 None
1172 }
1173}
1174
1175fn pixel_position_to_grid_position(
1178 position: Vector2<i32>,
1179 chunk_size: Vector2<u32>,
1180) -> Vector2<i32> {
1181 let chunk_size = chunk_size.map(|x| x as i32);
1182 let x = position.x / chunk_size.x;
1183 let y = position.y / chunk_size.y;
1184 let x = if position.x < 0 && position.x % chunk_size.x != 0 {
1186 x - 1
1187 } else {
1188 x
1189 };
1190 let y = if position.y < 0 && position.y % chunk_size.y != 0 {
1191 y - 1
1192 } else {
1193 y
1194 };
1195 Vector2::new(x, y)
1196}
1197
1198fn resize_u8(data: Vec<u8>, data_size: Vector2<u32>, new_size: Vector2<u32>) -> Vec<u8> {
1199 let image = ImageBuffer::<Luma<u8>, Vec<u8>>::from_vec(data_size.x, data_size.y, data).unwrap();
1200
1201 let resampled_image =
1202 image::imageops::resize(&image, new_size.x, new_size.y, FilterType::Lanczos3);
1203
1204 resampled_image.into_raw()
1205}
1206
1207#[allow(clippy::manual_slice_fill)] fn resize_f32(mut data: Vec<f32>, data_size: Vector2<u32>, new_size: Vector2<u32>) -> Vec<f32> {
1209 let max = data.iter().copied().reduce(f32::max).unwrap();
1210 let min = data.iter().copied().reduce(f32::min).unwrap();
1211 let range = max - min;
1212
1213 if range == 0.0 {
1214 let size: usize = (new_size.x * new_size.y) as usize;
1215 data.clear();
1216 data.extend(std::iter::repeat_n(min, size));
1217 return data;
1218 }
1219
1220 for height in &mut data {
1221 *height = (*height - min) / range;
1222 }
1223
1224 let heightmap_image =
1225 ImageBuffer::<Luma<f32>, Vec<f32>>::from_vec(data_size.x, data_size.y, data).unwrap();
1226
1227 let resampled_heightmap_image = image::imageops::resize(
1228 &heightmap_image,
1229 new_size.x,
1230 new_size.y,
1231 FilterType::Lanczos3,
1232 );
1233
1234 let mut resampled_heightmap = resampled_heightmap_image.into_raw();
1235
1236 for height in &mut resampled_heightmap {
1237 *height = (*height * range) + min;
1238 }
1239 resampled_heightmap
1240}
1241
1242fn create_zero_margin(mut data: Vec<f32>, data_size: Vector2<u32>) -> Vec<f32> {
1243 let w0 = data_size.x as usize;
1244 let w1 = w0 + 2;
1245 let h0 = data_size.y as usize;
1246 let h1 = h0 + 2;
1247 let new_area = w1 * h1;
1248 data.extend(std::iter::repeat_n(0.0, new_area - data.len()));
1249 for y in (0..h0).rev() {
1250 let i0 = y * w0;
1251 let i1 = (y + 1) * w1;
1252 data.copy_within(i0..i0 + w0, i1 + 1);
1253 data[i1] = 0.0;
1254 data[i1 + w1 - 1] = 0.0;
1255 }
1256 for v in data.iter_mut().take(w1) {
1257 *v = 0.0;
1258 }
1259 data
1260}
1261
1262impl TypeUuidProvider for Terrain {
1263 fn type_uuid() -> Uuid {
1264 uuid!("4b0a7927-bcd8-41a3-949a-dd10fba8e16a")
1265 }
1266}
1267
1268impl Terrain {
1269 pub fn align_chunk_margins(&mut self, grid_position: Vector2<i32>) {
1278 let Some(chunk) = self.find_chunk(grid_position) else {
1279 return;
1280 };
1281 let size = self.height_map_size();
1282 let x1 = size.x as i32 - 2;
1283 let y1 = size.y as i32 - 2;
1284 let mut data = chunk.heightmap.as_ref().unwrap().data_ref();
1285 let mut mut_data = ChunkHeightMutData(data.modify());
1286 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 0)) {
1287 let data = other_chunk.height_data();
1288 for y in 0..y1 {
1289 mut_data[Vector2::new(-1, y)] = data[Vector2::new(x1 - 2, y)];
1290 }
1291 }
1292 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 0)) {
1293 let data = other_chunk.height_data();
1294 for y in 0..y1 {
1295 mut_data[Vector2::new(x1, y)] = data[Vector2::new(1, y)];
1296 }
1297 }
1298 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, -1)) {
1299 let data = other_chunk.height_data();
1300 for x in 0..x1 {
1301 mut_data[Vector2::new(x, -1)] = data[Vector2::new(x, y1 - 2)];
1302 }
1303 }
1304 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, 1)) {
1305 let data = other_chunk.height_data();
1306 for x in 0..x1 {
1307 mut_data[Vector2::new(x, y1)] = data[Vector2::new(x, 1)];
1308 }
1309 }
1310 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, -1)) {
1311 let data = other_chunk.height_data();
1312 mut_data[Vector2::new(-1, -1)] = data[Vector2::new(x1 - 2, y1 - 2)];
1313 }
1314 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, -1)) {
1315 let data = other_chunk.height_data();
1316 mut_data[Vector2::new(x1, -1)] = data[Vector2::new(1, y1 - 2)];
1317 }
1318 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 1)) {
1319 let data = other_chunk.height_data();
1320 mut_data[Vector2::new(-1, y1)] = data[Vector2::new(x1 - 2, 1)];
1321 }
1322 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 1)) {
1323 let data = other_chunk.height_data();
1324 mut_data[Vector2::new(x1, y1)] = data[Vector2::new(1, 1)];
1325 }
1326 }
1327
1328 pub fn align_chunk_edges(&mut self, grid_position: Vector2<i32>) {
1333 let Some(chunk) = self.find_chunk(grid_position) else {
1334 return;
1335 };
1336 let size = self.height_map_size();
1337 let x1 = size.x as i32 - 3;
1338 let y1 = size.y as i32 - 3;
1339 let source = chunk.height_data();
1340 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 0)) {
1341 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1342 let mut mut_data = ChunkHeightMutData(data.modify());
1343 for y in 0..=y1 {
1344 mut_data[Vector2::new(x1, y)] = source[Vector2::new(0, y)];
1345 }
1346 }
1347 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 0)) {
1348 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1349 let mut mut_data = ChunkHeightMutData(data.modify());
1350 for y in 0..=y1 {
1351 mut_data[Vector2::new(0, y)] = source[Vector2::new(x1, y)];
1352 }
1353 }
1354 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, -1)) {
1355 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1356 let mut mut_data = ChunkHeightMutData(data.modify());
1357 for x in 0..=x1 {
1358 mut_data[Vector2::new(x, y1)] = source[Vector2::new(x, 0)];
1359 }
1360 }
1361 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(0, 1)) {
1362 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1363 let mut mut_data = ChunkHeightMutData(data.modify());
1364 for x in 0..=x1 {
1365 mut_data[Vector2::new(x, 0)] = source[Vector2::new(x, y1)];
1366 }
1367 }
1368 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, -1)) {
1369 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1370 let mut mut_data = ChunkHeightMutData(data.modify());
1371 mut_data[Vector2::new(x1, y1)] = source[Vector2::new(0, 0)];
1372 }
1373 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, -1)) {
1374 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1375 let mut mut_data = ChunkHeightMutData(data.modify());
1376 mut_data[Vector2::new(0, y1)] = source[Vector2::new(x1, 0)];
1377 }
1378 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(-1, 1)) {
1379 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1380 let mut mut_data = ChunkHeightMutData(data.modify());
1381 mut_data[Vector2::new(x1, 0)] = source[Vector2::new(0, y1)];
1382 }
1383 if let Some(other_chunk) = self.find_chunk(grid_position + Vector2::new(1, 1)) {
1384 let mut data = other_chunk.heightmap.as_ref().unwrap().data_ref();
1385 let mut mut_data = ChunkHeightMutData(data.modify());
1386 mut_data[Vector2::new(0, 0)] = source[Vector2::new(x1, y1)];
1387 }
1388 }
1389
1390 pub fn chunk_size(&self) -> Vector2<f32> {
1392 *self.chunk_size
1393 }
1394
1395 pub fn set_chunk_size(&mut self, chunk_size: Vector2<f32>) -> Vector2<f32> {
1398 let old = *self.chunk_size;
1399 self.chunk_size.set_value_and_mark_modified(chunk_size);
1400
1401 for iy in 0..self.length_chunks.len() {
1403 for ix in 0..self.width_chunks.len() {
1404 let chunk = &mut self.chunks[iy * self.width_chunks.len() + ix];
1405 chunk.physical_size = chunk_size;
1406 chunk.position = chunk.position();
1407 }
1408 }
1409
1410 self.bounding_box_dirty.set(true);
1411
1412 old
1413 }
1414
1415 pub fn height_map_size(&self) -> Vector2<u32> {
1419 *self.height_map_size
1420 }
1421
1422 pub fn hole_mask_size(&self) -> Vector2<u32> {
1427 self.height_map_size.map(|x| x - 3)
1428 }
1429
1430 pub fn set_height_map_size(&mut self, height_map_size: Vector2<u32>) -> Vector2<u32> {
1434 let old = *self.height_map_size;
1435 self.resize_height_maps(height_map_size);
1436 old
1437 }
1438
1439 pub fn set_block_size(&mut self, block_size: Vector2<u32>) -> Vector2<u32> {
1444 let old = *self.block_size;
1445 self.block_size.set_value_and_mark_modified(block_size);
1446 self.geometry = TerrainGeometry::new(*self.block_size);
1447 for chunk in self.chunks.iter_mut() {
1448 chunk.set_block_size(*self.block_size);
1449 }
1450 old
1451 }
1452
1453 pub fn block_size(&self) -> Vector2<u32> {
1455 *self.block_size
1456 }
1457
1458 pub fn set_holes_enabled(&mut self, enabled: bool) -> bool {
1460 if self.holes_enabled == enabled {
1461 return enabled;
1462 }
1463 let old = self.holes_enabled;
1464 self.holes_enabled = enabled;
1465 let size = self.hole_mask_size();
1466 for chunk in self.chunks.iter_mut() {
1467 if enabled {
1468 chunk.hole_mask = Some(make_blank_hole_texture(size));
1469 } else {
1470 chunk.hole_mask = None;
1471 }
1472 }
1473 old
1474 }
1475
1476 pub fn holes_enabled(&self) -> bool {
1478 self.holes_enabled
1479 }
1480
1481 pub fn mask_size(&self) -> Vector2<u32> {
1483 *self.mask_size
1484 }
1485
1486 pub fn set_mask_size(&mut self, mask_size: Vector2<u32>) -> Vector2<u32> {
1489 let old = *self.mask_size;
1490 self.resize_masks(mask_size);
1491 old
1492 }
1493
1494 pub fn width_chunks(&self) -> Range<i32> {
1496 (*self.width_chunks).clone()
1497 }
1498
1499 pub fn set_width_chunks(&mut self, chunks: Range<i32>) -> Range<i32> {
1501 let old = (*self.width_chunks).clone();
1502 self.resize(chunks, self.length_chunks());
1503 old
1504 }
1505
1506 pub fn length_chunks(&self) -> Range<i32> {
1508 (*self.length_chunks).clone()
1509 }
1510
1511 pub fn set_length_chunks(&mut self, chunks: Range<i32>) -> Range<i32> {
1513 let old = (*self.length_chunks).clone();
1514 self.resize(self.width_chunks(), chunks);
1515 old
1516 }
1517
1518 pub fn resize(&mut self, width_chunks: Range<i32>, length_chunks: Range<i32>) {
1521 let mut chunks = self
1522 .chunks
1523 .drain(..)
1524 .map(|c| (c.grid_position, c))
1525 .collect::<HashMap<_, _>>();
1526
1527 self.width_chunks.set_value_and_mark_modified(width_chunks);
1528 self.length_chunks
1529 .set_value_and_mark_modified(length_chunks);
1530 let mut created_chunks = Vec::new();
1531 let mut preserved_chunks = Vec::new();
1532
1533 let hole_size = self.hole_mask_size();
1534
1535 for z in (*self.length_chunks).clone() {
1536 for x in (*self.width_chunks).clone() {
1537 let chunk = if let Some(existing_chunk) = chunks.remove(&Vector2::new(x, z)) {
1538 preserved_chunks.push(existing_chunk.grid_position);
1539 existing_chunk
1541 } else {
1542 let heightmap =
1544 vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
1545 let new_chunk = Chunk {
1546 quad_tree: Mutex::new(QuadTree::new(
1547 &heightmap,
1548 *self.height_map_size,
1549 *self.block_size,
1550 0,
1551 )),
1552 heightmap: Some(make_height_map_texture(heightmap, self.height_map_size())),
1553 hole_mask: if self.holes_enabled {
1554 Some(make_blank_hole_texture(hole_size))
1555 } else {
1556 None
1557 },
1558 height_map_modifications_count: 0,
1559 position: Vector3::new(
1560 x as f32 * self.chunk_size.x,
1561 0.0,
1562 z as f32 * self.chunk_size.y,
1563 ),
1564 physical_size: *self.chunk_size,
1565 height_map_size: *self.height_map_size,
1566 block_size: *self.block_size,
1567 grid_position: Vector2::new(x, z),
1568 layer_masks: self
1569 .layers
1570 .iter()
1571 .enumerate()
1572 .map(|(i, _)| {
1573 create_layer_mask(
1574 self.mask_size.x,
1575 self.mask_size.y,
1576 if i == 0 { 255 } else { 0 },
1577 )
1578 })
1579 .collect::<Vec<_>>(),
1580 };
1581 created_chunks.push(new_chunk.grid_position);
1582 new_chunk
1583 };
1584
1585 self.chunks.push(chunk);
1586 }
1587 }
1588
1589 for grid_position in created_chunks {
1590 self.align_chunk_margins(grid_position);
1591 }
1592 for grid_position in preserved_chunks {
1593 self.align_chunk_edges(grid_position);
1594 }
1595
1596 self.bounding_box_dirty.set(true);
1597 }
1598
1599 pub fn chunks_ref(&self) -> &[Chunk] {
1601 &self.chunks
1602 }
1603
1604 pub fn chunks_mut(&mut self) -> &mut [Chunk] {
1606 self.bounding_box_dirty.set(true);
1607 &mut self.chunks
1608 }
1609
1610 pub fn find_chunk(&self, grid_position: Vector2<i32>) -> Option<&Chunk> {
1612 self.chunks
1613 .iter()
1614 .find(|c| c.grid_position == grid_position)
1615 }
1616
1617 pub fn find_chunk_mut(&mut self, grid_position: Vector2<i32>) -> Option<&mut Chunk> {
1619 self.chunks
1620 .iter_mut()
1621 .find(|c| c.grid_position == grid_position)
1622 }
1623
1624 pub fn update_quad_trees(&mut self) {
1626 for c in self.chunks.iter_mut() {
1627 c.update_quad_tree();
1628 }
1629 }
1630
1631 pub fn project(&self, p: Vector3<f32>) -> Option<Vector2<f32>> {
1634 project(self.global_transform(), p)
1635 }
1636
1637 pub fn local_to_height_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1639 let scale = self.height_grid_scale();
1640 Vector2::new(p.x / scale.x, p.y / scale.y)
1641 }
1642
1643 pub fn local_to_mask_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1645 let scale = self.mask_grid_scale();
1646 let half = scale * 0.5;
1647 let p = p - half;
1648 Vector2::new(p.x / scale.x, p.y / scale.y)
1649 }
1650
1651 pub fn local_to_hole_pixel(&self, p: Vector2<f32>) -> Vector2<f32> {
1653 let scale = self.hole_grid_scale();
1654 let half = scale * 0.5;
1655 let p = p - half;
1656 Vector2::new(p.x / scale.x, p.y / scale.y)
1657 }
1658
1659 pub fn height_grid_scale(&self) -> Vector2<f32> {
1661 let cell_width = self.chunk_size.x / (self.height_map_size.x - 3) as f32;
1664 let cell_length = self.chunk_size.y / (self.height_map_size.y - 3) as f32;
1665 Vector2::new(cell_width, cell_length)
1666 }
1667
1668 pub fn hole_grid_scale(&self) -> Vector2<f32> {
1670 let cell_width = self.chunk_size.x / (self.height_map_size.x - 3) as f32;
1673 let cell_length = self.chunk_size.y / (self.height_map_size.y - 3) as f32;
1674 Vector2::new(cell_width, cell_length)
1675 }
1676
1677 pub fn mask_grid_scale(&self) -> Vector2<f32> {
1679 let cell_width = self.chunk_size.x / self.mask_size.x as f32;
1680 let cell_length = self.chunk_size.y / self.mask_size.y as f32;
1681 Vector2::new(cell_width, cell_length)
1682 }
1683
1684 pub fn get_height_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1686 TerrainRect::from_local(position, self.height_grid_scale())
1687 }
1688
1689 pub fn get_mask_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1693 let cell_size = self.mask_grid_scale();
1694 let half_size = cell_size / 2.0;
1695 let position = position - half_size;
1697 let mut rect = TerrainRect::from_local(position, cell_size);
1698 rect.bounds.position += half_size;
1699 rect
1700 }
1701
1702 pub fn get_hole_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1706 let cell_size = self.height_grid_scale();
1707 let half_size = cell_size / 2.0;
1708 let position = position - half_size;
1710 let mut rect = TerrainRect::from_local(position, cell_size);
1711 rect.bounds.position += half_size;
1712 rect
1713 }
1714
1715 pub fn get_layer_mask(&self, position: Vector2<i32>, layer: usize) -> Option<u8> {
1717 let chunk_pos = self.chunk_containing_mask_pos(position);
1718 let chunk = self.find_chunk(chunk_pos)?;
1719 let origin = self.chunk_mask_pos_origin(chunk_pos);
1720 let pos = (position - origin).map(|x| x as usize);
1721 let index = pos.y * self.mask_size.x as usize + pos.x;
1722 let texture_data = chunk.layer_masks[layer].data_ref();
1723 let mask_data = texture_data.data();
1724 Some(mask_data[index])
1725 }
1726
1727 pub fn get_hole_mask(&self, position: Vector2<i32>) -> Option<u8> {
1729 let chunk_pos = self.chunk_containing_hole_pos(position);
1730 let chunk = self.find_chunk(chunk_pos)?;
1731 let origin = self.chunk_hole_pos_origin(chunk_pos);
1732 let pos = (position - origin).map(|x| x as usize);
1733 let index = pos.y * (self.height_map_size.x - 3) as usize + pos.x;
1734 let texture_data = chunk.hole_mask.as_ref().map(|r| r.data_ref())?;
1735 let mask_data = texture_data.data();
1736 Some(mask_data[index])
1737 }
1738
1739 pub fn get_height(&self, position: Vector2<i32>) -> Option<f32> {
1741 let chunk = self.chunks_containing_height_pos_iter(position).next()?;
1742 let p = (position - self.chunk_height_pos_origin(chunk.grid_position))
1743 .map(|x| (x + 1) as usize);
1744 let index = p.y * self.height_map_size.x as usize + p.x;
1745 let texture_data = chunk.heightmap.as_ref().unwrap().data_ref();
1746 let height_map = texture_data.data_of_type::<f32>().unwrap();
1747 Some(height_map[index])
1748 }
1749
1750 pub fn interpolate_value(&self, position: Vector2<f32>, target: BrushTarget) -> f32 {
1755 let grid_square = match target {
1756 BrushTarget::HeightMap => self.get_height_grid_square(position),
1757 BrushTarget::LayerMask { .. } => self.get_mask_grid_square(position),
1758 BrushTarget::HoleMask => self.get_hole_grid_square(position),
1759 };
1760 let p = grid_square.grid_position;
1761 let b = grid_square.bounds;
1762 let x0 = b.position.x;
1763 let y0 = b.position.y;
1764 let x1 = b.position.x + b.size.x;
1765 let y1 = b.position.y + b.size.y;
1766 let dx0 = position.x - x0;
1767 let dx1 = x1 - position.x;
1768 let dy0 = position.y - y0;
1769 let dy1 = y1 - position.y;
1770 let p00 = p;
1771 let p01 = Vector2::new(p.x, p.y + 1);
1772 let p10 = Vector2::new(p.x + 1, p.y);
1773 let p11 = Vector2::new(p.x + 1, p.y + 1);
1774 let (f00, f01, f10, f11) = match target {
1775 BrushTarget::HeightMap => (
1776 self.get_height(p00).unwrap_or(0.0),
1777 self.get_height(p01).unwrap_or(0.0),
1778 self.get_height(p10).unwrap_or(0.0),
1779 self.get_height(p11).unwrap_or(0.0),
1780 ),
1781 BrushTarget::LayerMask { layer } => (
1782 self.get_layer_mask(p00, layer).unwrap_or(0) as f32 / 255.0,
1783 self.get_layer_mask(p01, layer).unwrap_or(0) as f32 / 255.0,
1784 self.get_layer_mask(p10, layer).unwrap_or(0) as f32 / 255.0,
1785 self.get_layer_mask(p11, layer).unwrap_or(0) as f32 / 255.0,
1786 ),
1787 BrushTarget::HoleMask => (
1788 self.get_hole_mask(p00).unwrap_or(0) as f32 / 255.0,
1789 self.get_hole_mask(p01).unwrap_or(0) as f32 / 255.0,
1790 self.get_hole_mask(p10).unwrap_or(0) as f32 / 255.0,
1791 self.get_hole_mask(p11).unwrap_or(0) as f32 / 255.0,
1792 ),
1793 };
1794 let value = f00 * dx1 * dy1 + f10 * dx0 * dy1 + f01 * dx1 * dy0 + f11 * dx0 * dy0;
1795 value / (b.size.x * b.size.y)
1796 }
1797
1798 pub fn height_pos_to_local(&self, position: Vector2<i32>) -> Vector2<f32> {
1800 let pos = position.map(|x| x as f32);
1801 let chunk_size = self.height_map_size.map(|x| (x - 3) as f32);
1802 let physical_size = &self.chunk_size;
1803 Vector2::new(
1804 pos.x / chunk_size.x * physical_size.x,
1805 pos.y / chunk_size.y * physical_size.y,
1806 )
1807 }
1808
1809 pub fn mask_pos_to_local(&self, position: Vector2<i32>) -> Vector2<f32> {
1811 let pos = position.map(|x| x as f32 + 0.5);
1813 let chunk_size = self.mask_size.map(|x| x as f32);
1814 let physical_size = &self.chunk_size;
1815 Vector2::new(
1816 pos.x / chunk_size.x * physical_size.x,
1817 pos.y / chunk_size.y * physical_size.y,
1818 )
1819 }
1820
1821 pub fn chunk_containing_height_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1830 let chunk_size = self.height_map_size.map(|x| x - 3);
1832 pixel_position_to_grid_position(position, chunk_size)
1833 }
1834
1835 pub fn chunk_contains_height_pos(
1838 &self,
1839 chunk_grid_position: Vector2<i32>,
1840 pixel_position: Vector2<i32>,
1841 ) -> bool {
1842 let p = pixel_position - self.chunk_height_pos_origin(chunk_grid_position);
1843 let w = self.height_map_size.x as i32;
1844 let h = self.height_map_size.y as i32;
1845 (-1..w - 1).contains(&p.x) && (-1..h - 1).contains(&p.y)
1846 }
1847
1848 pub fn chunks_containing_height_pos_iter(
1850 &self,
1851 pixel_position: Vector2<i32>,
1852 ) -> impl Iterator<Item = &Chunk> {
1853 let w = self.height_map_size.x as i32;
1854 let h = self.height_map_size.y as i32;
1855 self.chunks.iter().filter(move |c| {
1856 let p = pixel_position - self.chunk_height_pos_origin(c.grid_position);
1857 (-1..w - 1).contains(&p.x) && (-1..h - 1).contains(&p.y)
1858 })
1859 }
1860
1861 pub fn chunk_height_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1864 let chunk_size = *self.height_map_size;
1865 let x = chunk_grid_position.x * (chunk_size.x as i32 - 1);
1867 let y = chunk_grid_position.y * (chunk_size.y as i32 - 1);
1868 Vector2::new(x, y)
1869 }
1870
1871 pub fn chunk_containing_mask_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1875 pixel_position_to_grid_position(position, *self.mask_size)
1876 }
1877 pub fn chunk_containing_hole_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1881 pixel_position_to_grid_position(position, self.hole_mask_size())
1882 }
1883
1884 pub fn chunk_mask_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1887 let chunk_size = *self.mask_size;
1888 let x = chunk_grid_position.x * chunk_size.x as i32;
1889 let y = chunk_grid_position.y * chunk_size.y as i32;
1890 Vector2::new(x, y)
1891 }
1892
1893 pub fn chunk_hole_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
1896 let chunk_size = self.hole_mask_size();
1897 let x = chunk_grid_position.x * chunk_size.x as i32;
1898 let y = chunk_grid_position.y * chunk_size.y as i32;
1899 Vector2::new(x, y)
1900 }
1901
1902 pub fn update_mask_pixel<F>(&mut self, position: Vector2<i32>, layer: usize, func: F)
1906 where
1907 F: FnOnce(u8) -> u8,
1908 {
1909 let chunk_pos = self.chunk_containing_mask_pos(position);
1910 let origin = self.chunk_mask_pos_origin(chunk_pos);
1911 let pos = position - origin;
1912 let index = (pos.y * self.mask_size.x as i32 + pos.x) as usize;
1913 let Some(chunk) = self.find_chunk_mut(chunk_pos) else {
1914 return;
1915 };
1916 let mut texture_data = chunk.layer_masks[layer].data_ref();
1917 let mut texture_modifier = texture_data.modify();
1918 let mask = texture_modifier.data_mut_of_type::<u8>().unwrap();
1919 let value = &mut mask[index];
1920 *value = func(*value);
1921 }
1922
1923 pub fn for_each_height_map_pixel<F>(&mut self, mut func: F)
1925 where
1926 F: FnMut(&mut f32, Vector2<f32>),
1927 {
1928 for chunk in self.chunks.iter_mut() {
1929 let mut texture_data = chunk.heightmap.as_ref().unwrap().data_ref();
1930 let mut texture_modifier = texture_data.modify();
1931 let height_map = texture_modifier.data_mut_of_type::<f32>().unwrap();
1932
1933 for iy in 0..chunk.height_map_size.y {
1934 let kz = (iy as f32 - 1.0) / (chunk.height_map_size.y - 3) as f32;
1935 for ix in 0..chunk.height_map_size.x {
1936 let kx = (ix as f32 - 1.0) / (chunk.height_map_size.x - 3) as f32;
1937
1938 let pixel_position = chunk.local_position()
1939 + Vector2::new(kx * chunk.physical_size.x, kz * chunk.physical_size.y);
1940
1941 let index = (iy * chunk.height_map_size.x + ix) as usize;
1942
1943 func(&mut height_map[index], pixel_position)
1944 }
1945 }
1946
1947 drop(texture_modifier);
1948 drop(texture_data);
1949
1950 *chunk.quad_tree.safe_lock() =
1951 make_quad_tree(&chunk.heightmap, chunk.height_map_size, chunk.block_size);
1952 }
1953
1954 self.bounding_box_dirty.set(true);
1955 }
1956
1957 pub fn raycast<const DIM: usize>(
1964 &self,
1965 ray: Ray,
1966 results: &mut ArrayVec<TerrainRayCastResult, DIM>,
1967 sort_results: bool,
1968 ) -> bool {
1969 if let Some(inv_transform) = self.global_transform().try_inverse() {
1970 let local_ray = ray.transform(inv_transform);
1972
1973 let origin_proj = map_to_local(
1975 inv_transform
1976 .transform_point(&Point3::from(ray.origin))
1977 .coords,
1978 );
1979 let dir_proj = map_to_local(inv_transform.transform_vector(&ray.dir));
1980
1981 'chunk_loop: for (chunk_index, chunk) in self.chunks.iter().enumerate() {
1983 let texture = chunk.heightmap.as_ref().unwrap().data_ref();
1984 let height_map = texture.data_of_type::<f32>().unwrap();
1985
1986 let chunk_width = (chunk.height_map_size.x - 3) as f32;
1992 let chunk_length = (chunk.height_map_size.y - 3) as f32;
1993 let cell_width = chunk.physical_size.x / chunk_width;
1994 let cell_length = chunk.physical_size.y / chunk_length;
1995
1996 for iy in 1..chunk.height_map_size.y - 2 {
1998 let kz = (iy - 1) as f32 / chunk_length;
1999
2000 for ix in 1..chunk.height_map_size.x - 2 {
2002 let kx = (ix - 1) as f32 / chunk_width;
2003
2004 let pixel_position = chunk.local_position()
2005 + Vector2::new(kx * chunk.physical_size.x, kz * chunk.physical_size.y);
2006
2007 let cell_bounds =
2008 Rect::new(pixel_position.x, pixel_position.y, cell_width, cell_length);
2009
2010 if ray_rect_intersection(cell_bounds, origin_proj, dir_proj).is_some() {
2011 let i0 = (iy * chunk.height_map_size.x + ix) as usize;
2014 let i1 = ((iy + 1) * chunk.height_map_size.x + ix) as usize;
2015 let i2 = ((iy + 1) * chunk.height_map_size.x + ix + 1) as usize;
2016 let i3 = (iy * chunk.height_map_size.x + ix + 1) as usize;
2017
2018 let v0 = Vector3::new(
2019 pixel_position.x,
2020 height_map[i0],
2021 pixel_position.y, );
2023 let v1 = Vector3::new(v0.x, height_map[i1], v0.z + cell_length);
2024 let v2 = Vector3::new(v1.x + cell_width, height_map[i2], v1.z);
2025 let v3 = Vector3::new(v0.x + cell_width, height_map[i3], v0.z);
2026
2027 for vertices in &[[v0, v1, v2], [v2, v3, v0]] {
2028 if let Some((toi, intersection)) =
2029 local_ray.triangle_intersection(vertices)
2030 {
2031 let normal = (vertices[2] - vertices[0])
2032 .cross(&(vertices[1] - vertices[0]))
2033 .try_normalize(f32::EPSILON)
2034 .unwrap_or_else(Vector3::y);
2035
2036 let result = TerrainRayCastResult {
2037 position: self
2038 .global_transform()
2039 .transform_point(&Point3::from(intersection))
2040 .coords,
2041 height: intersection.y,
2042 normal,
2043 chunk_index,
2044 toi,
2045 };
2046
2047 if results.try_push(result).is_err() {
2048 break 'chunk_loop;
2049 }
2050 }
2051 }
2052 }
2053 }
2054 }
2055 }
2056 }
2057
2058 if sort_results {
2059 results.sort_unstable_by(|a, b| {
2060 if a.toi > b.toi {
2061 Ordering::Greater
2062 } else if a.toi < b.toi {
2063 Ordering::Less
2064 } else {
2065 Ordering::Equal
2066 }
2067 });
2068 }
2069
2070 !results.is_empty()
2071 }
2072
2073 pub fn set_layers(&mut self, layers: Vec<Layer>) -> Vec<Layer> {
2075 self.layers.set_value_and_mark_modified(layers)
2076 }
2077
2078 pub fn layers(&self) -> &[Layer] {
2080 &self.layers
2081 }
2082
2083 pub fn layers_mut(&mut self) -> &mut [Layer] {
2085 self.layers.get_value_mut_and_mark_modified()
2086 }
2087
2088 pub fn add_layer(&mut self, layer: Layer, masks: Vec<TextureResource>) {
2092 self.insert_layer(layer, masks, self.layers.len())
2093 }
2094
2095 pub fn remove_layer(&mut self, layer_index: usize) -> (Layer, Vec<TextureResource>) {
2097 let layer = self
2098 .layers
2099 .get_value_mut_and_mark_modified()
2100 .remove(layer_index);
2101 let mut layer_masks = Vec::new();
2102 for chunk in self.chunks_mut() {
2103 layer_masks.push(chunk.layer_masks.remove(layer_index));
2104 }
2105 (layer, layer_masks)
2106 }
2107
2108 pub fn pop_layer(&mut self) -> Option<(Layer, Vec<TextureResource>)> {
2110 if self.layers.is_empty() {
2111 None
2112 } else {
2113 Some(self.remove_layer(self.layers.len() - 1))
2114 }
2115 }
2116
2117 pub fn insert_layer(&mut self, layer: Layer, mut masks: Vec<TextureResource>, index: usize) {
2119 self.layers
2120 .get_value_mut_and_mark_modified()
2121 .insert(index, layer);
2122
2123 for chunk in self.chunks.iter_mut().rev() {
2124 if let Some(mask) = masks.pop() {
2125 chunk.layer_masks.insert(index, mask);
2126 } else {
2127 chunk.layer_masks.insert(
2128 index,
2129 create_layer_mask(
2130 self.mask_size.x,
2131 self.mask_size.y,
2132 if index == 0 { 255 } else { 0 },
2133 ),
2134 )
2135 }
2136 }
2137 }
2138
2139 fn resize_masks(&mut self, mut new_size: Vector2<u32>) {
2140 new_size = new_size.sup(&Vector2::repeat(1));
2141
2142 for chunk in self.chunks.iter_mut() {
2143 for mask in chunk.layer_masks.iter_mut() {
2144 let data = mask.data_ref();
2145 let new_mask = resize_u8(data.data().to_vec(), *self.mask_size, new_size);
2146 let new_mask_texture = TextureResource::from_bytes(
2147 Uuid::new_v4(),
2148 TextureKind::Rectangle {
2149 width: new_size.x,
2150 height: new_size.y,
2151 },
2152 data.pixel_kind(),
2153 new_mask,
2154 ResourceKind::Embedded,
2155 )
2156 .unwrap();
2157
2158 drop(data);
2159 *mask = new_mask_texture;
2160 }
2161 }
2162
2163 self.mask_size.set_value_and_mark_modified(new_size);
2164 }
2165
2166 fn resize_height_maps(&mut self, mut new_size: Vector2<u32>) {
2167 new_size = new_size.sup(&Vector2::repeat(5));
2170 let hole_size = self.hole_mask_size();
2171 let new_hole_size = new_size.map(|x| x - 3);
2172
2173 for chunk in self.chunks.iter_mut() {
2174 let texture = chunk.heightmap.as_ref().unwrap().data_ref();
2175 let heightmap = texture.data_of_type::<f32>().unwrap().to_vec();
2176
2177 let resampled_heightmap = resize_f32(heightmap, chunk.height_map_size, new_size);
2178 drop(texture);
2179 chunk.heightmap = Some(make_height_map_texture(resampled_heightmap, new_size));
2180 if self.holes_enabled {
2181 let texture = chunk.hole_mask.as_ref().map(|t| t.data_ref());
2182 let data = texture.and_then(|t| t.data_of_type::<u8>().map(|t| t.to_vec()));
2183 if let Some(data) = data {
2184 let resampled = resize_u8(data, hole_size, new_hole_size);
2185 chunk.hole_mask = Some(make_hole_texture(resampled, new_hole_size));
2186 } else {
2187 chunk.hole_mask = Some(make_blank_hole_texture(new_hole_size));
2188 }
2189 }
2190 chunk.height_map_size = new_size;
2191 }
2192 self.height_map_size.set_value_and_mark_modified(new_size);
2193
2194 for grid_position in self
2196 .chunks
2197 .iter()
2198 .map(|c| c.grid_position)
2199 .collect::<Vec<_>>()
2200 {
2201 self.align_chunk_margins(grid_position);
2202 self.align_chunk_edges(grid_position);
2203 }
2204 self.update_quad_trees();
2205
2206 self.bounding_box_dirty.set(true);
2207 }
2208
2209 pub fn geometry(&self) -> &TerrainGeometry {
2211 &self.geometry
2212 }
2213 pub fn texture_data(&self, target: BrushTarget) -> TerrainTextureData {
2217 let chunk_size = match target {
2218 BrushTarget::HeightMap => self.height_map_size(),
2219 BrushTarget::LayerMask { .. } => self.mask_size(),
2220 BrushTarget::HoleMask => self.hole_mask_size(),
2221 };
2222 let kind = match target {
2223 BrushTarget::HeightMap => TerrainTextureKind::Height,
2224 BrushTarget::LayerMask { .. } => TerrainTextureKind::Mask,
2225 BrushTarget::HoleMask => TerrainTextureKind::Mask,
2226 };
2227 let resources: FxHashMap<Vector2<i32>, TextureResource> = match target {
2228 BrushTarget::HeightMap => self
2229 .chunks_ref()
2230 .iter()
2231 .map(|c| (c.grid_position(), c.heightmap().clone()))
2232 .collect(),
2233 BrushTarget::HoleMask => self
2234 .chunks_ref()
2235 .iter()
2236 .map(|c| {
2237 (
2238 c.grid_position(),
2239 c.hole_mask
2240 .as_ref()
2241 .cloned()
2242 .expect("Missing hole mask texture"),
2243 )
2244 })
2245 .collect(),
2246 BrushTarget::LayerMask { layer } => self
2247 .chunks_ref()
2248 .iter()
2249 .map(|c| (c.grid_position(), c.layer_masks[layer].clone()))
2250 .collect(),
2251 };
2252 TerrainTextureData {
2253 chunk_size,
2254 kind,
2255 resources,
2256 }
2257 }
2258 fn start_stroke(&self, brush: Brush, stroke: &mut BrushStroke) {
2264 let target = brush.target;
2265 if brush.target == BrushTarget::HoleMask && !self.holes_enabled {
2266 Log::err("Invalid brush stroke. Holes are not enabled on terrain.");
2267 return;
2268 }
2269 stroke.start_stroke(brush, self.handle(), self.texture_data(target))
2270 }
2271 fn stamp(&self, position: Vector3<f32>, value: f32, stroke: &mut BrushStroke) {
2279 let Some(position) = self.project(position) else {
2280 return;
2281 };
2282 let position = match stroke.brush().target {
2283 BrushTarget::HeightMap => self.local_to_height_pixel(position),
2284 BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(position),
2285 BrushTarget::HoleMask => self.local_to_hole_pixel(position),
2286 };
2287 let scale = match stroke.brush().target {
2288 BrushTarget::HeightMap => self.height_grid_scale(),
2289 BrushTarget::LayerMask { .. } => self.mask_grid_scale(),
2290 BrushTarget::HoleMask => self.hole_grid_scale(),
2291 };
2292 stroke.stamp(position, scale, value);
2293 }
2294 fn smear(&self, start: Vector3<f32>, end: Vector3<f32>, value: f32, stroke: &mut BrushStroke) {
2303 let Some(start) = self.project(start) else {
2304 return;
2305 };
2306 let Some(end) = self.project(end) else {
2307 return;
2308 };
2309 let start = match stroke.brush().target {
2310 BrushTarget::HeightMap => self.local_to_height_pixel(start),
2311 BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(start),
2312 BrushTarget::HoleMask => self.local_to_hole_pixel(start),
2313 };
2314 let end = match stroke.brush().target {
2315 BrushTarget::HeightMap => self.local_to_height_pixel(end),
2316 BrushTarget::LayerMask { .. } => self.local_to_mask_pixel(end),
2317 BrushTarget::HoleMask => self.local_to_hole_pixel(end),
2318 };
2319 let scale = match stroke.brush().target {
2320 BrushTarget::HeightMap => self.height_grid_scale(),
2321 BrushTarget::LayerMask { .. } => self.mask_grid_scale(),
2322 BrushTarget::HoleMask => self.hole_grid_scale(),
2323 };
2324 stroke.smear(start, end, scale, value);
2325 }
2326}
2327
2328fn is_power_of_two(x: u32) -> bool {
2330 x != 0 && (x & (x - 1)) == 0
2331}
2332
2333fn validate_height_map_size(x: u32, size: Vector2<u32>) -> Result<(), String> {
2334 if is_power_of_two(x - 3) {
2335 return Ok(());
2336 }
2337 let mut suggestion = 2;
2338 while suggestion + 3 < x {
2339 suggestion *= 2;
2340 }
2341 Err(format!(
2342 "Height map size ({}, {}): {} is not 3 plus a power of 2. Consider: {}",
2343 size.x,
2344 size.y,
2345 x,
2346 suggestion + 3
2347 ))
2348}
2349
2350fn validate_block_size(x: u32, size: Vector2<u32>) -> Result<(), String> {
2351 if is_power_of_two(x - 1) {
2352 return Ok(());
2353 }
2354 let mut suggestion = 2;
2355 while suggestion + 1 < x {
2356 suggestion *= 2;
2357 }
2358 Err(format!(
2359 "Block size ({}, {}): {} is not 1 plus a power of 2. Consider: {}",
2360 size.x,
2361 size.y,
2362 x,
2363 suggestion + 1
2364 ))
2365}
2366
2367fn create_terrain_layer_material() -> MaterialResource {
2368 let mut material = Material::standard_terrain();
2369 material.set_property("texCoordScale", Vector2::new(10.0, 10.0));
2370 MaterialResource::new_ok(Uuid::new_v4(), Default::default(), material)
2371}
2372
2373impl ConstructorProvider<Node, Graph> for Terrain {
2374 fn constructor() -> NodeConstructor {
2375 NodeConstructor::new::<Self>().with_variant("Terrain", |_| {
2376 TerrainBuilder::new(BaseBuilder::new().with_name("Terrain"))
2377 .with_layers(vec![Layer {
2378 material: create_terrain_layer_material(),
2379 ..Default::default()
2380 }])
2381 .build_node()
2382 .into()
2383 })
2384 }
2385}
2386
2387impl NodeTrait for Terrain {
2388 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
2391 if self.bounding_box_dirty.get() {
2392 let mut max_height = -f32::MAX;
2393 let mut min_height = f32::MAX;
2394 for chunk in self.chunks.iter() {
2395 let texture = chunk.heightmap.as_ref().unwrap().data_ref();
2396 let height_map = texture.data_of_type::<f32>().unwrap();
2397 for &height in height_map {
2398 if height > max_height {
2399 max_height = height;
2400 }
2401 if height < min_height {
2402 min_height = height;
2403 }
2404 }
2405 }
2406
2407 let bounding_box = AxisAlignedBoundingBox::from_min_max(
2408 Vector3::new(
2409 self.chunk_size.x * self.width_chunks.start as f32,
2410 min_height,
2411 self.chunk_size.y * self.length_chunks.start as f32,
2412 ),
2413 Vector3::new(
2414 self.chunk_size.x * self.width_chunks.end as f32,
2415 max_height,
2416 self.chunk_size.y * self.length_chunks.end as f32,
2417 ),
2418 );
2419 self.bounding_box.set(bounding_box);
2420 self.bounding_box_dirty.set(false);
2421
2422 bounding_box
2423 } else {
2424 self.bounding_box.get()
2425 }
2426 }
2427
2428 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
2430 self.local_bounding_box()
2431 .transform(&self.global_transform())
2432 }
2433
2434 fn id(&self) -> Uuid {
2435 Self::type_uuid()
2436 }
2437
2438 fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
2439 if *self.render_mask & ctx.render_mask == BitMask::none() {
2440 return RdcControlFlow::Continue;
2441 }
2442
2443 if !self.global_visibility()
2444 || !self.is_globally_enabled()
2445 || (self.frustum_culling()
2446 && !ctx
2447 .frustum
2448 .is_none_or(|f| f.is_intersects_aabb(&self.world_bounding_box())))
2449 {
2450 return RdcControlFlow::Continue;
2451 }
2452
2453 if renderer::is_shadow_pass(ctx.render_pass_name) && !self.cast_shadows() {
2454 return RdcControlFlow::Continue;
2455 }
2456
2457 for c in self.chunks.iter() {
2458 c.update();
2459 }
2460
2461 for (layer_index, layer) in self.layers().iter().enumerate() {
2462 for chunk in self.chunks_ref().iter() {
2463 let quad_tree = chunk.quad_tree.safe_lock();
2468 let levels = (0..=quad_tree.max_level)
2469 .map(|n| {
2470 ctx.observer_position.z_far
2471 * ((quad_tree.max_level - n) as f32 / quad_tree.max_level as f32)
2472 .powf(3.0)
2473 })
2474 .collect::<Vec<_>>();
2475
2476 let chunk_transform =
2477 self.global_transform() * Matrix4::new_translation(&chunk.position());
2478
2479 let mut selection = Vec::new();
2484 quad_tree.select(
2485 &chunk_transform,
2486 self.height_map_size(),
2487 self.chunk_size(),
2488 ctx.frustum,
2489 ctx.observer_position.translation,
2490 &levels,
2491 &mut selection,
2492 );
2493
2494 let mut material = layer.material.deep_copy().data_ref().clone();
2495
2496 material.bind(
2497 &layer.mask_property_name,
2498 chunk.layer_masks[layer_index].clone(),
2499 );
2500 material.bind(&layer.height_map_property_name, chunk.heightmap.clone());
2501 material.bind(&layer.hole_mask_property_name, chunk.hole_mask.clone());
2502
2503 let size = self.height_map_size.map(|x| (x - 3) as f32);
2505 for node in selection {
2506 let kx = (node.position.x - 1) as f32 / size.x;
2509 let kz = (node.position.y - 1) as f32 / size.y;
2510
2511 let kw = (node.size.x - 1) as f32 / size.x;
2512 let kh = (node.size.y - 1) as f32 / size.y;
2513
2514 material.set_property(
2515 &layer.node_uv_offsets_property_name,
2516 MaterialProperty::Vector4(Vector4::new(kx, kz, kw, kh)),
2517 );
2518
2519 let material = MaterialResource::new_ok(
2520 Uuid::new_v4(),
2521 Default::default(),
2522 material.clone(),
2523 );
2524
2525 let node_transform = chunk_transform
2526 * Matrix4::new_translation(&Vector3::new(
2527 kx * self.chunk_size.x,
2528 0.0,
2529 kz * self.chunk_size.y,
2530 ))
2531 * Matrix4::new_nonuniform_scaling(&Vector3::new(
2532 kw * self.chunk_size.x,
2533 1.0,
2534 kh * self.chunk_size.y,
2535 ));
2536
2537 if node.is_draw_full() {
2538 ctx.storage.push(
2539 &self.geometry.data,
2540 &material,
2541 RenderPath::Deferred,
2542 layer_index as u64,
2543 SurfaceInstanceData {
2544 world_transform: node_transform,
2545 bone_matrices: Default::default(),
2546 blend_shapes_weights: Default::default(),
2547 element_range: ElementRange::Full,
2548 node_handle: self.handle(),
2549 },
2550 );
2551 } else {
2552 for (i, draw_quadrant) in node.active_quadrants.iter().enumerate() {
2553 if *draw_quadrant {
2554 ctx.storage.push(
2555 &self.geometry.data,
2556 &material,
2557 RenderPath::Deferred,
2558 layer_index as u64,
2559 SurfaceInstanceData {
2560 world_transform: node_transform,
2561 bone_matrices: Default::default(),
2562 blend_shapes_weights: Default::default(),
2563 element_range: self.geometry.quadrants[i],
2564 node_handle: self.handle(),
2565 },
2566 );
2567 }
2568 }
2569 }
2570 }
2571 }
2572 }
2573
2574 RdcControlFlow::Continue
2575 }
2576
2577 fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
2578 for chunk in self.chunks.iter() {
2579 chunk.debug_draw(&self.global_transform(), ctx)
2580 }
2581 }
2582
2583 fn validate(&self, _: &Scene) -> Result<(), String> {
2584 let h_size = self.height_map_size();
2585 validate_height_map_size(h_size.x, h_size)?;
2586 validate_height_map_size(h_size.y, h_size)?;
2587 let b_size = self.block_size();
2588 validate_block_size(b_size.x, b_size)?;
2589 validate_block_size(b_size.y, b_size)?;
2590 if b_size.x - 1 > h_size.x - 3 {
2591 return Err(format!(
2592 "Block size ({}, {}): {} is too large for height map. Consider: {}",
2593 b_size.x,
2594 b_size.y,
2595 b_size.x,
2596 h_size.x - 2
2597 ));
2598 }
2599 if b_size.y - 1 > h_size.y - 3 {
2600 return Err(format!(
2601 "Block size ({}, {}): {} is too large for height map. Consider: {}",
2602 b_size.x,
2603 b_size.y,
2604 b_size.y,
2605 h_size.y - 2
2606 ));
2607 }
2608 Ok(())
2609 }
2610}
2611
2612pub struct TerrainBuilder {
2614 base_builder: BaseBuilder,
2615 holes_enabled: bool,
2616 chunk_size: Vector2<f32>,
2617 mask_size: Vector2<u32>,
2618 width_chunks: Range<i32>,
2619 length_chunks: Range<i32>,
2620 height_map_size: Vector2<u32>,
2621 block_size: Vector2<u32>,
2622 layers: Vec<Layer>,
2623}
2624
2625fn create_layer_mask(width: u32, height: u32, value: u8) -> TextureResource {
2626 let mask = TextureResource::from_bytes(
2627 Uuid::new_v4(),
2628 TextureKind::Rectangle { width, height },
2629 TexturePixelKind::R8,
2630 vec![value; (width * height) as usize],
2631 ResourceKind::Embedded,
2632 )
2633 .unwrap();
2634
2635 let mut data_ref = mask.data_ref();
2636 data_ref.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
2637 data_ref.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
2638 drop(data_ref);
2639
2640 mask
2641}
2642
2643impl TerrainBuilder {
2644 pub fn new(base_builder: BaseBuilder) -> Self {
2646 Self {
2647 base_builder,
2648 holes_enabled: false,
2649 chunk_size: Vector2::new(16.0, 16.0),
2650 width_chunks: 0..2,
2651 length_chunks: 0..2,
2652 mask_size: Vector2::new(256, 256),
2653 height_map_size: Vector2::new(257, 257),
2654 block_size: Vector2::new(33, 33),
2655 layers: Default::default(),
2656 }
2657 }
2658
2659 pub fn with_holes(mut self, holes_enabled: bool) -> Self {
2661 self.holes_enabled = holes_enabled;
2662 self
2663 }
2664
2665 pub fn with_chunk_size(mut self, size: Vector2<f32>) -> Self {
2667 self.chunk_size = size;
2668 self
2669 }
2670
2671 pub fn with_mask_size(mut self, size: Vector2<u32>) -> Self {
2673 self.mask_size = size;
2674 self
2675 }
2676
2677 pub fn with_width_chunks(mut self, width_chunks: Range<i32>) -> Self {
2679 self.width_chunks = width_chunks;
2680 self
2681 }
2682
2683 pub fn with_length_chunks(mut self, length_chunks: Range<i32>) -> Self {
2685 self.length_chunks = length_chunks;
2686 self
2687 }
2688
2689 pub fn with_height_map_size(mut self, size: Vector2<u32>) -> Self {
2691 self.height_map_size = size;
2692 self
2693 }
2694
2695 pub fn with_layers(mut self, layers: Vec<Layer>) -> Self {
2697 self.layers = layers;
2698 self
2699 }
2700
2701 pub fn with_block_size(mut self, block_size: Vector2<u32>) -> Self {
2704 self.block_size = block_size;
2705 self
2706 }
2707
2708 pub fn build_node(self) -> Node {
2710 let mut chunks = Vec::new();
2711 for z in self.length_chunks.clone() {
2712 for x in self.width_chunks.clone() {
2713 let heightmap =
2714 vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
2715 let hole_mask = if self.holes_enabled {
2716 Some(create_layer_mask(
2717 self.height_map_size.x - 3,
2718 self.height_map_size.y - 3,
2719 255,
2720 ))
2721 } else {
2722 None
2723 };
2724 let chunk = Chunk {
2725 quad_tree: Mutex::new(QuadTree::new(
2726 &heightmap,
2727 self.height_map_size,
2728 self.block_size,
2729 0,
2730 )),
2731 height_map_size: self.height_map_size,
2732 heightmap: Some(make_height_map_texture(heightmap, self.height_map_size)),
2733 hole_mask,
2734 height_map_modifications_count: 0,
2735 position: Vector3::new(
2736 x as f32 * self.chunk_size.x,
2737 0.0,
2738 z as f32 * self.chunk_size.y,
2739 ),
2740 physical_size: self.chunk_size,
2741 grid_position: Vector2::new(x, z),
2742 layer_masks: self
2743 .layers
2744 .iter()
2745 .enumerate()
2746 .map(|(i, _)| {
2747 create_layer_mask(
2748 self.mask_size.x,
2749 self.mask_size.y,
2750 if i == 0 { 255 } else { 0 },
2752 )
2753 })
2754 .collect::<Vec<_>>(),
2755 block_size: self.block_size,
2756 };
2757
2758 chunks.push(chunk);
2759 }
2760 }
2761
2762 let terrain = Terrain {
2763 chunk_size: self.chunk_size.into(),
2764 base: self.base_builder.build_base(),
2765 holes_enabled: self.holes_enabled,
2766 layers: self.layers.into(),
2767 chunks: chunks.into(),
2768 bounding_box_dirty: Cell::new(true),
2769 bounding_box: Default::default(),
2770 mask_size: self.mask_size.into(),
2771 height_map_size: self.height_map_size.into(),
2772 width_chunks: self.width_chunks.into(),
2773 length_chunks: self.length_chunks.into(),
2774 geometry: TerrainGeometry::new(self.block_size),
2775 block_size: self.block_size.into(),
2776 };
2777 Node::new(terrain)
2778 }
2779
2780 pub fn build(self, graph: &mut Graph) -> Handle<Terrain> {
2782 graph.add_node(self.build_node()).to_variant()
2783 }
2784}
2785
2786#[cfg(test)]
2787mod tests {
2788 use super::*;
2789
2790 #[test]
2791 fn power_of_two() {
2792 assert!(!is_power_of_two(0));
2793 assert!(is_power_of_two(1));
2794 assert!(is_power_of_two(2));
2795 assert!(!is_power_of_two(3));
2796 assert!(is_power_of_two(4));
2797 assert!(!is_power_of_two(5));
2798 assert!(!is_power_of_two(6));
2799 assert!(!is_power_of_two(7));
2800 assert!(is_power_of_two(8));
2801 assert!(!is_power_of_two(9));
2802 assert!(!is_power_of_two(15));
2803 assert!(is_power_of_two(16));
2804 }
2805 #[test]
2806 fn resize_1x1() {
2807 let r = resize_f32(vec![3.5], Vector2::new(1, 1), Vector2::new(2, 2));
2808 assert_eq!(r, vec![3.5, 3.5, 3.5, 3.5]);
2809 }
2810 #[test]
2811 fn resize_2x1() {
2812 let r = resize_f32(vec![1.0, 2.0], Vector2::new(2, 1), Vector2::new(3, 1));
2813 assert_eq!(r, vec![1.0, 1.5, 2.0]);
2814 }
2815 #[test]
2816 fn zero_margin_0x0() {
2817 let r = create_zero_margin(Vec::new(), Vector2::new(0, 0));
2818 assert_eq!(r, vec![0.0, 0.0, 0.0, 0.0]);
2819 }
2820 #[test]
2821 fn zero_margin_1x1() {
2822 let r = create_zero_margin(vec![3.5], Vector2::new(1, 1));
2823 assert_eq!(r, vec![0.0, 0.0, 0.0, 0.0, 3.5, 0.0, 0.0, 0.0, 0.0]);
2824 }
2825 #[test]
2826 fn zero_margin_2x1() {
2827 let r = create_zero_margin(vec![1.0, 2.0], Vector2::new(2, 1));
2828 assert_eq!(
2829 r,
2830 vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0]
2831 );
2832 }
2833 #[test]
2834 fn zero_margin_1x2() {
2835 let r = create_zero_margin(vec![1.0, 2.0], Vector2::new(1, 2));
2836 assert_eq!(
2837 r,
2838 vec![0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0]
2839 );
2840 }
2841 #[test]
2842 fn zero_margin_3x3() {
2843 let r = create_zero_margin(
2844 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
2845 Vector2::new(3, 3),
2846 );
2847 assert_eq!(
2848 r,
2849 vec![
2850 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 0.0, 0.0, 4.0, 5.0, 6.0, 0.0, 0.0,
2851 7.0, 8.0, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
2852 ]
2853 );
2854 }
2855}