Skip to content

Commit

Permalink
Fix visual block update glitches caused by margins
Browse files Browse the repository at this point in the history
  • Loading branch information
patowen committed Dec 7, 2023
1 parent 410344b commit fe227b1
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 12 deletions.
9 changes: 3 additions & 6 deletions client/src/graphics/voxels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions common/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
2 changes: 2 additions & 0 deletions common/src/graph_collision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
};
Expand Down
114 changes: 113 additions & 1 deletion common/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ impl Graph {
))
}

pub fn get_chunk_neighbor(
&self,
chunk: ChunkId,
coord_axis: usize,
coord_direction: i8,
) -> Option<ChunkId> {
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,
Expand Down Expand Up @@ -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]
Expand All @@ -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)
Expand All @@ -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
}
}
Expand Down Expand Up @@ -158,6 +251,7 @@ pub enum Chunk {
Generating,
Populated {
voxels: VoxelData,
modified: bool,
surface: Option<SlotId>,
old_surface: Option<SlotId>,
},
Expand All @@ -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)
}
}
Expand All @@ -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
Expand Down
7 changes: 2 additions & 5 deletions server/src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down

0 comments on commit fe227b1

Please sign in to comment.