diff --git a/client/src/graphics/voxels/mod.rs b/client/src/graphics/voxels/mod.rs index d11e3800..5ee944e9 100644 --- a/client/src/graphics/voxels/mod.rs +++ b/client/src/graphics/voxels/mod.rs @@ -100,12 +100,8 @@ impl Voxels { self.states.peek_mut(chunk).refcount -= 1; } while let Some(chunk) = self.worldgen.poll() { - sim.graph.get_mut(chunk.node).as_mut().unwrap().chunks[chunk.chunk] = - Chunk::Populated { - surface: None, - old_surface: None, - voxels: chunk.voxels, - }; + let chunk_id = ChunkId::new(chunk.node, chunk.chunk); + sim.graph.populate_chunk(chunk_id, chunk.voxels, false); } // Determine what to load/render @@ -173,6 +169,7 @@ impl Voxels { ref mut surface, ref mut old_surface, ref voxels, + .. } => { if let Some(slot) = surface.or(*old_surface) { // Render an already-extracted surface diff --git a/common/benches/bench.rs b/common/benches/bench.rs index e768c0c8..884492f7 100644 --- a/common/benches/bench.rs +++ b/common/benches/bench.rs @@ -49,6 +49,7 @@ fn build_graph(c: &mut Criterion) { if let Some(params) = ChunkParams::new(12, &graph, chunk) { graph[chunk] = Chunk::Populated { voxels: params.generate_voxels(), + modified: false, surface: None, old_surface: None, }; diff --git a/common/src/graph_collision.rs b/common/src/graph_collision.rs index bdd95776..65d1ee08 100644 --- a/common/src/graph_collision.rs +++ b/common/src/graph_collision.rs @@ -157,6 +157,7 @@ mod tests { for vertex in dodeca::Vertex::iter() { graph[ChunkId::new(node, vertex)] = Chunk::Populated { voxels: VoxelData::Solid(Material::Void), + modified: false, surface: None, old_surface: None, }; @@ -428,6 +429,7 @@ mod tests { for vertex in dodeca::Vertex::iter() { graph[ChunkId::new(node, vertex)] = Chunk::Populated { voxels: VoxelData::Solid(Material::Void), + modified: false, surface: None, old_surface: None, }; diff --git a/common/src/node.rs b/common/src/node.rs index 5059cf9f..3606f9d2 100644 --- a/common/src/node.rs +++ b/common/src/node.rs @@ -43,6 +43,25 @@ impl Graph { )) } + pub fn get_chunk_neighbor( + &self, + chunk: ChunkId, + coord_axis: usize, + coord_direction: i8, + ) -> Option { + if coord_direction == 1 { + Some(ChunkId::new( + chunk.node, + chunk.vertex.adjacent_vertices()[coord_axis], + )) + } else { + Some(ChunkId::new( + self.neighbor(chunk.node, chunk.vertex.canonical_sides()[coord_axis])?, + chunk.vertex, + )) + } + } + pub fn get_block_neighbor( &self, mut chunk: ChunkId, @@ -77,6 +96,39 @@ impl Graph { Some((chunk, coords)) } + /// Populates a chunk with the given voxel data and ensures that margins are correctly removed + /// if necessary. + pub fn populate_chunk(&mut self, chunk: ChunkId, mut new_data: VoxelData, modified: bool) { + // New chunks generated by world generation should not be solid if they are adjacent to modified chunks. + if !modified && new_data.is_solid() { + for coord_axis in 0..3 { + for coord_direction in [1, -1] { + if let Some(chunk_id) = + self.get_chunk_neighbor(chunk, coord_axis, coord_direction) + { + if let Chunk::Populated { modified: true, .. } = self[chunk_id] { + // Remove margins if next to a modified chunk + new_data.data_mut(self.layout().dimension); + } + } + } + } + } + + // Insert the data into the graph + *self.get_chunk_mut(chunk).unwrap() = Chunk::Populated { + voxels: new_data, + modified, + surface: None, + old_surface: None, + }; + + // Old chunks generated by world generation should not be solid if they are adjacent to modified chunks. + if modified { + self.remove_adjacent_margins(chunk); + } + } + /// Tries to update the block at the given position to the given material. /// Fails and returns false if the chunk is not populated yet. #[must_use] @@ -86,6 +138,7 @@ impl Graph { // Update the block let Some(Chunk::Populated { voxels, + modified, surface, old_surface, }) = self.get_chunk_mut(block_update.chunk_id) @@ -98,7 +151,47 @@ impl Graph { .expect("coords are in-bounds"); *voxel = block_update.new_material; + *modified = true; *old_surface = surface.take().or(*old_surface); + + self.remove_adjacent_margins(block_update.chunk_id); + true + } + + /// Removes margins from any populated adjacent chunks. When a chunk is modified, this function should + /// be called on that chunk to ensure that adjacent chunks are invisible under the potentially false assumption + /// that they are hidden by world generation. + fn remove_adjacent_margins(&mut self, chunk: ChunkId) { + for coord_axis in 0..3 { + for coord_direction in [1, -1] { + if let Some(chunk_id) = self.get_chunk_neighbor(chunk, coord_axis, coord_direction) + { + // Unpopulated chunks are skipped. + let _ = self.remove_margins(chunk_id); + } + } + } + } + + /// Tries to remove the margins of the given chunk. Fails and returns false if the + /// chunk is not populated yet. + #[must_use] + fn remove_margins(&mut self, chunk: ChunkId) -> bool { + let dimension = self.layout().dimension; + let Some(Chunk::Populated { + voxels, + surface, + old_surface, + .. + }) = self.get_chunk_mut(chunk) + else { + return false; + }; + + if voxels.is_solid() { + voxels.data_mut(dimension); + *old_surface = surface.take().or(*old_surface); + } true } } @@ -158,6 +251,7 @@ pub enum Chunk { Generating, Populated { voxels: VoxelData, + modified: bool, surface: Option, old_surface: Option, }, @@ -173,7 +267,18 @@ impl VoxelData { match *self { VoxelData::Dense(ref mut d) => d, VoxelData::Solid(mat) => { - *self = VoxelData::Dense(vec![mat; (usize::from(dimension) + 2).pow(3)].into()); + let lwm = usize::from(dimension) + 2; + let mut data = vec![Material::Void; lwm.pow(3)]; + + // Populate all blocks except the margins, as margins are not fully implemented yet. + for i in 1..(lwm - 1) { + for j in 1..(lwm - 1) { + for k in 1..(lwm - 1) { + data[i + j * lwm + k * lwm.pow(2)] = mat; + } + } + } + *self = VoxelData::Dense(data.into_boxed_slice()); self.data_mut(dimension) } } @@ -185,6 +290,13 @@ impl VoxelData { VoxelData::Solid(mat) => mat, } } + + pub fn is_solid(&self) -> bool { + match *self { + VoxelData::Dense(_) => false, + VoxelData::Solid(_) => true, + } + } } /// Contains the context needed to know the locations of individual cubes within a chunk in the chunk's coordinate diff --git a/server/src/sim.rs b/server/src/sim.rs index 35e5dd22..0b24abae 100644 --- a/server/src/sim.rs +++ b/server/src/sim.rs @@ -289,11 +289,8 @@ impl Sim { if let Some(params) = ChunkParams::new(self.cfg.chunk_size, &self.graph, chunk) { - self.graph[chunk] = Chunk::Populated { - voxels: params.generate_voxels(), - surface: None, - old_surface: None, - }; + self.graph + .populate_chunk(chunk, params.generate_voxels(), false); } } }