Skip to content

Commit

Permalink
Merge pull request #668 from b-guild/terrain
Browse files Browse the repository at this point in the history
Improving update of terrain quad tree
  • Loading branch information
mrDIMAS authored Jul 12, 2024
2 parents d8a3c51 + a0ebd12 commit 04446f0
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 18 deletions.
88 changes: 72 additions & 16 deletions fyrox-impl/src/scene/terrain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
arrayvec::ArrayVec,
log::Log,
math::{aabb::AxisAlignedBoundingBox, ray::Ray, ray_rect_intersection, Rect},
parking_lot::Mutex,
pool::Handle,
reflect::prelude::*,
sstorage::ImmutableString,
Expand Down Expand Up @@ -122,8 +123,9 @@ fn make_quad_tree(
block_size: Vector2<u32>,
) -> QuadTree {
let texture = texture.as_ref().unwrap().data_ref();
let height_mod_count = texture.modifications_count();
let height_map = texture.data_of_type::<f32>().unwrap();
QuadTree::new(height_map, height_map_size, block_size)
QuadTree::new(height_map, height_map_size, block_size, height_mod_count)
}

/// Create an Ok texture resource of the given size from the given height values.
Expand Down Expand Up @@ -161,10 +163,10 @@ fn make_height_map_texture(height_map: Vec<f32>, size: Vector2<u32>) -> TextureR
/// grid. You can add chunks from any side of a terrain. Chunks could be considered as a "sub-terrain", which could
/// use its own set of materials for layers. This could be useful for different biomes, to prevent high amount of
/// layers which could harm the performance.
#[derive(Debug, Reflect, PartialEq)]
#[derive(Debug, Reflect)]
pub struct Chunk {
#[reflect(hidden)]
quad_tree: QuadTree,
quad_tree: Mutex<QuadTree>,
#[reflect(hidden)]
version: u8,
#[reflect(
Expand All @@ -187,10 +189,22 @@ pub struct Chunk {
/// Layer blending masks of the chunk.
#[reflect(hidden)]
pub layer_masks: Vec<TextureResource>,
#[reflect(hidden)]
height_map_modifications_count: u64,
}

uuid_provider!(Chunk = "ae996754-69c1-49ba-9c17-a7bd4be072a9");

impl PartialEq for Chunk {
fn eq(&self, other: &Self) -> bool {
self.version == other.version
&& self.heightmap == other.heightmap
&& self.height_map_size == other.height_map_size
&& self.grid_position == other.grid_position
&& self.layer_masks == other.layer_masks
}
}

impl Clone for Chunk {
// Deep cloning.
fn clone(&self) -> Self {
Expand All @@ -207,7 +221,12 @@ impl Clone for Chunk {
.iter()
.map(|m| m.deep_clone())
.collect::<Vec<_>>(),
quad_tree: make_quad_tree(&self.heightmap, self.height_map_size, self.block_size),
quad_tree: Mutex::new(make_quad_tree(
&self.heightmap,
self.height_map_size,
self.block_size,
)),
height_map_modifications_count: self.height_map_modifications_count,
}
}
}
Expand Down Expand Up @@ -271,7 +290,11 @@ impl Visit for Chunk {
_ => (),
}

self.quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
self.quad_tree = Mutex::new(make_quad_tree(
&self.heightmap,
self.height_map_size,
self.block_size,
));

Ok(())
}
Expand All @@ -289,11 +312,23 @@ impl Default for Chunk {
block_size: Vector2::new(32, 32),
grid_position: Default::default(),
layer_masks: Default::default(),
height_map_modifications_count: 0,
}
}
}

impl Chunk {
/// Check the heightmap for modifications and update data as necessary.
pub fn update(&self) {
let Some(heightmap) = self.heightmap.as_ref() else {
return;
};
let count = heightmap.data_ref().modifications_count();
let mut quad_tree = self.quad_tree.lock();
if count != quad_tree.height_mod_count() {
*quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
}
}
/// Returns position of the chunk in local 2D coordinates relative to origin of the
/// terrain.
pub fn local_position(&self) -> Vector2<f32> {
Expand Down Expand Up @@ -493,7 +528,10 @@ impl Chunk {
if let Some(texture) =
make_height_map_texture_internal(pixels, self.height_map_size)
{
return std::mem::replace(&mut self.heightmap, Some(texture));
let prev_texture =
std::mem::replace(&mut self.heightmap, Some(texture));
self.update_quad_tree();
return prev_texture;
}
}
}
Expand Down Expand Up @@ -529,6 +567,7 @@ impl Chunk {
{
drop(data);
self.heightmap = Some(heightmap);
self.update_quad_tree();
return Ok(());
}
}
Expand All @@ -551,6 +590,7 @@ impl Chunk {
let transform = *transform * Matrix4::new_translation(&self.position());

self.quad_tree
.lock()
.debug_draw(&transform, self.height_map_size, self.physical_size, ctx)
}

Expand All @@ -560,8 +600,12 @@ impl Chunk {
}

/// Recalculates the quad tree for this chunk.
pub fn update_quad_tree(&mut self) {
self.quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
pub fn update_quad_tree(&self) {
if self.heightmap.is_none() {
return;
}
*self.quad_tree.lock() =
make_quad_tree(&self.heightmap, self.height_map_size, self.block_size);
}
}

Expand Down Expand Up @@ -1201,12 +1245,14 @@ impl Terrain {
let heightmap =
vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
let new_chunk = Chunk {
quad_tree: QuadTree::new(
quad_tree: Mutex::new(QuadTree::new(
&heightmap,
*self.height_map_size,
*self.block_size,
),
0,
)),
heightmap: Some(make_height_map_texture(heightmap, self.height_map_size())),
height_map_modifications_count: 0,
position: Vector3::new(
x as f32 * self.chunk_size.x,
0.0,
Expand Down Expand Up @@ -1629,7 +1675,7 @@ impl Terrain {
drop(texture_modifier);
drop(texture_data);

chunk.quad_tree =
*chunk.quad_tree.lock() =
make_quad_tree(&chunk.heightmap, chunk.height_map_size, chunk.block_size);
}

Expand Down Expand Up @@ -2071,17 +2117,21 @@ impl NodeTrait for Terrain {
return RdcControlFlow::Continue;
}

for c in self.chunks.iter() {
c.update();
}

for (layer_index, layer) in self.layers().iter().enumerate() {
for chunk in self.chunks_ref().iter() {
// Generate a list of distances for each LOD that the terrain can render.
// The first element of the list is the furthest distance, where the lowest LOD is used.
// The formula used to produce this list has been chosen arbitrarily based on what seems to produce
// the best results in the render.
let levels = (0..chunk.quad_tree.max_level)
let quad_tree = chunk.quad_tree.lock();
let levels = (0..quad_tree.max_level)
.map(|n| {
ctx.z_far
* ((chunk.quad_tree.max_level - n) as f32
/ chunk.quad_tree.max_level as f32)
* ((quad_tree.max_level - n) as f32 / quad_tree.max_level as f32)
.powf(3.0)
})
.collect::<Vec<_>>();
Expand All @@ -2094,7 +2144,7 @@ impl NodeTrait for Terrain {
// The instances will be scaled based on the LOD that is needed at the instance's distance
// according to the `levels` list.
let mut selection = Vec::new();
chunk.quad_tree.select(
quad_tree.select(
&chunk_transform,
self.height_map_size(),
self.chunk_size(),
Expand Down Expand Up @@ -2321,9 +2371,15 @@ impl TerrainBuilder {
let heightmap =
vec![0.0; (self.height_map_size.x * self.height_map_size.y) as usize];
let chunk = Chunk {
quad_tree: QuadTree::new(&heightmap, self.height_map_size, self.block_size),
quad_tree: Mutex::new(QuadTree::new(
&heightmap,
self.height_map_size,
self.block_size,
0,
)),
height_map_size: self.height_map_size,
heightmap: Some(make_height_map_texture(heightmap, self.height_map_size)),
height_map_modifications_count: 0,
position: Vector3::new(
x as f32 * self.chunk_size.x,
0.0,
Expand Down
14 changes: 12 additions & 2 deletions fyrox-impl/src/scene/terrain/quadtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
pub struct QuadTree {
root: QuadTreeNode,
pub max_level: u32,
height_mod_count: u64,
}

/// Each QuadTreeNode is primarily responsible for storing the AABB data for a particular
Expand Down Expand Up @@ -387,6 +388,7 @@ impl QuadTree {
height_map: &[f32],
height_map_size: Vector2<u32>,
block_size: Vector2<u32>,
height_mod_count: u64,
) -> Self {
let mut index = 0;
let root = QuadTreeNode::new(
Expand All @@ -400,7 +402,15 @@ impl QuadTree {
);
let mut max_level = 0;
root.max_level(&mut max_level);
Self { max_level, root }
Self {
max_level,
root,
height_mod_count,
}
}

pub fn height_mod_count(&self) -> u64 {
self.height_mod_count
}

/// Determine the size and position of terrain geometry instances that are needed in order to render the chunk of this QuadTree.
Expand Down Expand Up @@ -468,7 +478,7 @@ mod test {
let heightmap = vec![0.0; (height_map_size.x * height_map_size.y) as usize];
let z_far = 100.0;

let quadtree = QuadTree::new(&heightmap, height_map_size, block_size);
let quadtree = QuadTree::new(&heightmap, height_map_size, block_size, 0);

let levels = (0..quadtree.max_level)
.map(|n| z_far * (n as f32 / quadtree.max_level as f32))
Expand Down

0 comments on commit 04446f0

Please sign in to comment.