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::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
87pub const VERSION: u8 = 2;
89
90lazy_static! {
91 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#[derive(Debug, Clone)]
104pub struct TerrainRect {
105 pub grid_position: Vector2<i32>,
107 pub bounds: Rect<f32>,
109}
110
111impl TerrainRect {
112 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
125pub struct ChunkHeightData<'a>(pub ResourceDataRef<'a, Texture>);
131pub struct ChunkHeightMutData<'a>(pub TextureDataRefMut<'a>);
136
137impl ChunkHeightData<'_> {
138 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 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 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 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 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 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 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#[derive(Debug, Clone, Visit, Reflect, PartialEq)]
244pub struct Layer {
245 pub material: MaterialResource,
247
248 pub mask_property_name: String,
250
251 #[visit(optional)]
253 pub height_map_property_name: String,
254
255 #[visit(optional)]
257 pub hole_mask_property_name: String,
258
259 #[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
282fn 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
294fn 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
338fn 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#[derive(Debug, Reflect)]
351pub struct Chunk {
352 #[reflect(hidden)]
353 quad_tree: Mutex<QuadTree>,
354 #[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 #[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 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
415impl 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 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 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 pub fn height_data(&self) -> ChunkHeightData {
505 ChunkHeightData(self.heightmap.as_ref().map(|r| r.data_ref()).unwrap())
506 }
507
508 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 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 pub fn local_position(&self) -> Vector2<f32> {
536 map_to_local(self.position())
537 }
538
539 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 #[inline]
550 pub fn grid_position(&self) -> Vector2<i32> {
551 self.grid_position
552 }
553
554 pub fn heightmap(&self) -> &TextureResource {
556 self.heightmap.as_ref().unwrap()
557 }
558
559 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 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 self.heightmap.clone()
764 }
765
766 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 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 pub fn hole_mask(&self) -> Option<&TextureResource> {
800 self.hole_mask.as_ref()
801 }
802
803 pub fn physical_size(&self) -> Vector2<f32> {
805 self.physical_size
806 }
807
808 pub fn height_map_size(&self) -> Vector2<u32> {
810 self.height_map_size
811 }
812
813 pub fn hole_mask_size(&self) -> Vector2<u32> {
815 self.height_map_size.map(|x| x - 3)
816 }
817
818 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 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 Vector2::new(v.x, v.z)
848}
849
850#[derive(Debug)]
852pub struct TerrainRayCastResult {
853 pub position: Vector3<f32>,
855 pub height: f32,
858 pub normal: Vector3<f32>,
860 pub chunk_index: usize,
862 pub toi: f32,
864}
865
866#[derive(Default)]
878pub struct BrushContext {
879 pub value: Option<f32>,
883 pub stroke: BrushStroke,
885}
886
887impl BrushContext {
888 pub fn brush(&self) -> &Brush {
895 self.stroke.brush()
896 }
897 pub fn shape(&mut self) -> &mut BrushShape {
900 self.stroke.shape()
901 }
902 pub fn mode(&mut self) -> &mut BrushMode {
905 self.stroke.mode()
906 }
907 pub fn hardness(&mut self) -> &mut f32 {
910 self.stroke.hardness()
911 }
912 pub fn alpha(&mut self) -> &mut f32 {
915 self.stroke.alpha()
916 }
917 pub fn start_stroke(&mut self, terrain: &Terrain, brush: Brush) {
923 self.value = None;
924 terrain.start_stroke(brush, &mut self.stroke);
925 }
926 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 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 pub fn flush(&mut self) {
957 self.stroke.flush();
958 }
959 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#[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 #[reflect(min_value = 0.001, setter = "set_chunk_size")]
1083 chunk_size: InheritableVariable<Vector2<f32>>,
1084
1085 #[reflect(step = 1.0, setter = "set_width_chunks")]
1088 width_chunks: InheritableVariable<Range<i32>>,
1089
1090 #[reflect(step = 1.0, setter = "set_length_chunks")]
1093 length_chunks: InheritableVariable<Range<i32>>,
1094
1095 #[reflect(min_value = 2.0, step = 1.0, setter = "set_height_map_size")]
1104 height_map_size: InheritableVariable<Vector2<u32>>,
1105
1106 #[reflect(min_value = 8.0, step = 1.0, setter = "set_block_size")]
1113 block_size: InheritableVariable<Vector2<u32>>,
1114
1115 #[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 #[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 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 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 let new_material = Material::standard_terrain();
1237
1238 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 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 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
1331fn 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 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)] fn 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 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 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 pub fn chunk_size(&self) -> Vector2<f32> {
1548 *self.chunk_size
1549 }
1550
1551 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 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 pub fn height_map_size(&self) -> Vector2<u32> {
1575 *self.height_map_size
1576 }
1577
1578 pub fn hole_mask_size(&self) -> Vector2<u32> {
1583 self.height_map_size.map(|x| x - 3)
1584 }
1585
1586 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 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 pub fn block_size(&self) -> Vector2<u32> {
1611 *self.block_size
1612 }
1613
1614 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 pub fn holes_enabled(&self) -> bool {
1634 self.holes_enabled
1635 }
1636
1637 pub fn mask_size(&self) -> Vector2<u32> {
1639 *self.mask_size
1640 }
1641
1642 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 pub fn width_chunks(&self) -> Range<i32> {
1652 (*self.width_chunks).clone()
1653 }
1654
1655 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 pub fn length_chunks(&self) -> Range<i32> {
1664 (*self.length_chunks).clone()
1665 }
1666
1667 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 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 existing_chunk
1697 } else {
1698 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 pub fn chunks_ref(&self) -> &[Chunk] {
1757 &self.chunks
1758 }
1759
1760 pub fn chunks_mut(&mut self) -> &mut [Chunk] {
1762 self.bounding_box_dirty.set(true);
1763 &mut self.chunks
1764 }
1765
1766 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 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 pub fn update_quad_trees(&mut self) {
1782 for c in self.chunks.iter_mut() {
1783 c.update_quad_tree();
1784 }
1785 }
1786
1787 pub fn project(&self, p: Vector3<f32>) -> Option<Vector2<f32>> {
1790 project(self.global_transform(), p)
1791 }
1792
1793 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 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 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 pub fn height_grid_scale(&self) -> Vector2<f32> {
1817 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 pub fn hole_grid_scale(&self) -> Vector2<f32> {
1826 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 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 pub fn get_height_grid_square(&self, position: Vector2<f32>) -> TerrainRect {
1842 TerrainRect::from_local(position, self.height_grid_scale())
1843 }
1844
1845 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 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 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 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 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 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 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 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 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 pub fn mask_pos_to_local(&self, position: Vector2<i32>) -> Vector2<f32> {
1967 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 pub fn chunk_containing_height_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
1986 let chunk_size = self.height_map_size.map(|x| x - 3);
1988 pixel_position_to_grid_position(position, chunk_size)
1989 }
1990
1991 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 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 pub fn chunk_height_pos_origin(&self, chunk_grid_position: Vector2<i32>) -> Vector2<i32> {
2020 let chunk_size = *self.height_map_size;
2021 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 pub fn chunk_containing_mask_pos(&self, position: Vector2<i32>) -> Vector2<i32> {
2031 pixel_position_to_grid_position(position, *self.mask_size)
2032 }
2033 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 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 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 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 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 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 let local_ray = ray.transform(inv_transform);
2128
2129 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 '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 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 for iy in 1..chunk.height_map_size.y - 2 {
2154 let kz = (iy - 1) as f32 / chunk_length;
2155
2156 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 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, );
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 pub fn set_layers(&mut self, layers: Vec<Layer>) -> Vec<Layer> {
2231 self.layers.set_value_and_mark_modified(layers)
2232 }
2233
2234 pub fn layers(&self) -> &[Layer] {
2236 &self.layers
2237 }
2238
2239 pub fn layers_mut(&mut self) -> &mut [Layer] {
2241 self.layers.get_value_mut_and_mark_modified()
2242 }
2243
2244 pub fn add_layer(&mut self, layer: Layer, masks: Vec<TextureResource>) {
2248 self.insert_layer(layer, masks, self.layers.len())
2249 }
2250
2251 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 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 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 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 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 pub fn geometry(&self) -> &TerrainGeometry {
2367 &self.geometry
2368 }
2369 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 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 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 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
2484fn 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 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 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 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 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 let size = self.height_map_size.map(|x| (x - 3) as f32);
2661 for node in selection {
2662 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
2768pub 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 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 pub fn with_holes(mut self, holes_enabled: bool) -> Self {
2817 self.holes_enabled = holes_enabled;
2818 self
2819 }
2820
2821 pub fn with_chunk_size(mut self, size: Vector2<f32>) -> Self {
2823 self.chunk_size = size;
2824 self
2825 }
2826
2827 pub fn with_mask_size(mut self, size: Vector2<u32>) -> Self {
2829 self.mask_size = size;
2830 self
2831 }
2832
2833 pub fn with_width_chunks(mut self, width_chunks: Range<i32>) -> Self {
2835 self.width_chunks = width_chunks;
2836 self
2837 }
2838
2839 pub fn with_length_chunks(mut self, length_chunks: Range<i32>) -> Self {
2841 self.length_chunks = length_chunks;
2842 self
2843 }
2844
2845 pub fn with_height_map_size(mut self, size: Vector2<u32>) -> Self {
2847 self.height_map_size = size;
2848 self
2849 }
2850
2851 pub fn with_layers(mut self, layers: Vec<Layer>) -> Self {
2853 self.layers = layers;
2854 self
2855 }
2856
2857 pub fn with_block_size(mut self, block_size: Vector2<u32>) -> Self {
2860 self.block_size = block_size;
2861 self
2862 }
2863
2864 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 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 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}