diff --git a/crates/building_blocks_storage/src/chunk_tree.rs b/crates/building_blocks_storage/src/chunk_tree.rs index fb5ed7f..448d150 100644 --- a/crates/building_blocks_storage/src/chunk_tree.rs +++ b/crates/building_blocks_storage/src/chunk_tree.rs @@ -646,6 +646,23 @@ where Bldr: ChunkTreeBuilder, Store: ChunkStorage, { + /// Returns `true` iff any chunks in the Moore-neighborhood of `key` are loading. + pub fn chunk_neighborhood_is_loading(&self, key: ChunkKey) -> bool { + for offset in PointN::moore_offsets().into_iter() { + let neighbor_min = key.minimum + offset * self.chunk_shape(); + let neighbor_key = ChunkKey::new(key.lod, neighbor_min); + if let Some((neighbor_state, _)) = self.get_node_state(neighbor_key) { + if neighbor_state.is_loading() { + return true; + } + } else if self.missing_node_is_loading(neighbor_key) { + return true; + } + } + + false + } + /// Iff there is not already a node for `key`, then the entire subtree at `key` will be marked for loading. This means that /// a traversal from the ancestor root of `key` will be able to discover all nodes that need to be loaded by following the /// `descendant_needs_loading` bits. For an example of this, see [`ChunkTree::clipmap_loading_slots`]. diff --git a/crates/building_blocks_storage/src/chunk_tree/clipmap.rs b/crates/building_blocks_storage/src/chunk_tree/clipmap.rs index cee2e33..7ebd0d1 100644 --- a/crates/building_blocks_storage/src/chunk_tree/clipmap.rs +++ b/crates/building_blocks_storage/src/chunk_tree/clipmap.rs @@ -151,7 +151,8 @@ where } let (node_state, _) = self.get_node_state(node_key).unwrap(); - let is_loading = node_state.is_loading(); + let is_loading = + node_state.is_loading() || self.chunk_neighborhood_is_loading(node_key); let was_active = node_state.is_rendering(); let is_active = @@ -217,6 +218,28 @@ where // This node just became inactive, and none of its ancestors were active, so it must have active // descendants. Split this node into active descendants. + // Start by making sure all children are loaded. + let mut children_loading = false; + for corner_index in 0..PointN::NUM_CORNERS { + let child_key = self.indexer.child_chunk_key(node_key, corner_index); + if let Some((child_state, _)) = self.get_node_state(child_key) { + if child_state.is_loading() + || self.chunk_neighborhood_is_loading(child_key) + { + children_loading = true; + break; + } + } else if self.missing_node_is_loading(child_key) + || self.chunk_neighborhood_is_loading(child_key) + { + children_loading = true; + break; + } + } + if children_loading { + continue; + } + // This node might have already split off from some ancestor. let split_ancestor = if let Some(a) = committed_split_descendants.remove(&node_key) { diff --git a/examples/lod_terrain/chunk_generator.rs b/examples/lod_terrain/chunk_generator.rs index d389b9c..2de6275 100644 --- a/examples/lod_terrain/chunk_generator.rs +++ b/examples/lod_terrain/chunk_generator.rs @@ -51,7 +51,12 @@ pub fn chunk_generator_system( if let Some(chunk) = chunk { map.chunks.write_chunk(key, chunk); } else { - map.chunks.delete_chunk(key); + // TODO: this is a temporary hack to smooth voxels; we can't delete just any "empty" chunks (those without any + // active edges) because there may be active edges between chunks, and the "empty" chunk might be responsible + // for generated the surface that intersects those edges + let extent = map.chunks.indexer.extent_for_chunk_with_min(key.minimum); + map.chunks.write_chunk(key, Array3x1::fill(extent, Voxel::EMPTY)); + // map.chunks.delete_chunk(key); } } }