Skip to content

Commit db96d89

Browse files
committed
Fix up margins after block updates
1 parent a3203bb commit db96d89

File tree

2 files changed

+123
-63
lines changed

2 files changed

+123
-63
lines changed

common/src/margins.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::{
22
dodeca::Vertex,
3+
graph::Graph,
34
math,
4-
node::VoxelData,
5+
node::{Chunk, ChunkId, VoxelData},
56
voxel_math::{ChunkAxisPermutation, ChunkDirection, CoordAxis, CoordSign, Coords},
7+
world::Material,
68
};
79

810
/// Updates the margins of both `voxels` and `neighbor_voxels` at the side they meet at.
@@ -79,6 +81,52 @@ pub fn initialize_margins(dimension: u8, voxels: &mut VoxelData) {
7981
}
8082
}
8183

84+
/// Assuming that the voxel at `chunk` and `coords` is set to `material`, updates the cooresponding
85+
/// margin in the chunk at direction `direction` from `chunk` if such a margin exists. Unpopulated chunks
86+
/// are ignored.
87+
pub fn update_margin_voxel(
88+
graph: &mut Graph,
89+
chunk: ChunkId,
90+
coords: Coords,
91+
direction: ChunkDirection,
92+
material: Material,
93+
) {
94+
let coords: CoordsWithMargins = coords.into();
95+
let dimension = graph.layout().dimension();
96+
let edge_coord = match direction.sign {
97+
CoordSign::Plus => dimension,
98+
CoordSign::Minus => 1,
99+
};
100+
if coords[direction.axis] != edge_coord {
101+
// There is nothing to do if we're not on an edge voxel.
102+
return;
103+
}
104+
let Some(Chunk::Populated {
105+
voxels: neighbor_voxels,
106+
surface: neighbor_surface,
107+
old_surface: neighbor_old_surface,
108+
..
109+
}) = graph
110+
.get_chunk_neighbor(chunk, direction.axis, direction.sign)
111+
.map(|chunk_id| &mut graph[chunk_id])
112+
else {
113+
// If the neighboring chunk to check is not populated, there is nothing to do.
114+
return;
115+
};
116+
117+
let margin_coord = match direction.sign {
118+
CoordSign::Plus => dimension + 1,
119+
CoordSign::Minus => 0,
120+
};
121+
let neighbor_axis_permutation = neighbor_axis_permutation(chunk.vertex, direction);
122+
let mut neighbor_coords = coords;
123+
neighbor_coords[direction.axis] = margin_coord;
124+
neighbor_coords = neighbor_axis_permutation * neighbor_coords;
125+
126+
neighbor_voxels.data_mut(dimension)[neighbor_coords.to_index(dimension)] = material;
127+
*neighbor_old_surface = neighbor_surface.take().or(*neighbor_old_surface);
128+
}
129+
82130
fn neighbor_axis_permutation(vertex: Vertex, direction: ChunkDirection) -> ChunkAxisPermutation {
83131
match direction.sign {
84132
CoordSign::Plus => vertex.chunk_axis_permutations()[direction.axis as usize],
@@ -153,7 +201,7 @@ impl std::ops::Mul<CoordsWithMargins> for ChunkAxisPermutation {
153201

154202
#[cfg(test)]
155203
mod tests {
156-
use crate::{dodeca::Vertex, voxel_math::Coords, world::Material};
204+
use crate::{dodeca::Vertex, graph::NodeId, node, voxel_math::Coords, world::Material};
157205

158206
use super::*;
159207

@@ -221,4 +269,69 @@ mod tests {
221269
Material::WoodPlanks
222270
);
223271
}
272+
273+
#[test]
274+
fn test_update_margin_voxel() {
275+
let mut graph = Graph::new(12);
276+
let current_vertex = Vertex::A;
277+
let neighbor_vertex = current_vertex.adjacent_vertices()[1];
278+
let neighbor_node =
279+
graph.ensure_neighbor(NodeId::ROOT, current_vertex.canonical_sides()[0]);
280+
node::populate_fresh_nodes(&mut graph);
281+
282+
// These are the chunks this test will work with.
283+
let current_chunk = ChunkId::new(NodeId::ROOT, current_vertex);
284+
let node_neighbor_chunk = ChunkId::new(neighbor_node, current_vertex);
285+
let vertex_neighbor_chunk = ChunkId::new(NodeId::ROOT, neighbor_vertex);
286+
287+
// Populate relevant chunks with void
288+
for chunk in [current_chunk, node_neighbor_chunk, vertex_neighbor_chunk] {
289+
*graph.get_chunk_mut(chunk).unwrap() = Chunk::Populated {
290+
voxels: VoxelData::Solid(Material::Void),
291+
modified: false,
292+
surface: None,
293+
old_surface: None,
294+
};
295+
}
296+
297+
// Update and check the margins of node_neighbor_chunk
298+
update_margin_voxel(
299+
&mut graph,
300+
current_chunk,
301+
Coords([0, 7, 9]),
302+
ChunkDirection::MINUS_X,
303+
Material::WoodPlanks,
304+
);
305+
let Chunk::Populated {
306+
voxels: node_neighbor_voxels,
307+
..
308+
} = graph.get_chunk_mut(node_neighbor_chunk).unwrap()
309+
else {
310+
panic!("node_neighbor_chunk should have just been populated by this test");
311+
};
312+
assert_eq!(
313+
node_neighbor_voxels.get(CoordsWithMargins([0, 8, 10]).to_index(12)),
314+
Material::WoodPlanks
315+
);
316+
317+
// Update and check the margins of vertex_neighbor_chunk
318+
update_margin_voxel(
319+
&mut graph,
320+
current_chunk,
321+
Coords([5, 11, 9]),
322+
ChunkDirection::PLUS_Y,
323+
Material::Grass,
324+
);
325+
let Chunk::Populated {
326+
voxels: vertex_neighbor_voxels,
327+
..
328+
} = graph.get_chunk_mut(vertex_neighbor_chunk).unwrap()
329+
else {
330+
panic!("vertex_neighbor_chunk should have just been populated by this test");
331+
};
332+
assert_eq!(
333+
vertex_neighbor_voxels.get(CoordsWithMargins([6, 10, 13]).to_index(12)),
334+
Material::Grass
335+
);
336+
}
224337
}

common/src/node.rs

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,6 @@ impl Graph {
150150
else {
151151
return false;
152152
};
153-
if voxels.is_solid() {
154-
voxels.clear_margin(dimension);
155-
}
156153
let voxel = voxels
157154
.data_mut(dimension)
158155
.get_mut(block_update.coords.to_index(dimension))
@@ -162,43 +159,14 @@ impl Graph {
162159
*modified = true;
163160
*old_surface = surface.take().or(*old_surface);
164161

165-
self.clear_adjacent_solid_chunk_margins(block_update.chunk_id);
166-
true
167-
}
168-
169-
/// Clears margins from any populated and solid adjacent chunks. When a chunk is modified, this function should
170-
/// be called on that chunk to ensure that adjacent chunks are rendered, since they can no longer be assumed to be
171-
/// hidden by world generation.
172-
fn clear_adjacent_solid_chunk_margins(&mut self, chunk: ChunkId) {
173-
for coord_axis in CoordAxis::iter() {
174-
for coord_sign in CoordSign::iter() {
175-
if let Some(chunk_id) = self.get_chunk_neighbor(chunk, coord_axis, coord_sign) {
176-
// We only need to clear margins from populated chunks.
177-
let _ = self.clear_solid_chunk_margin(chunk_id);
178-
}
179-
}
180-
}
181-
}
182-
183-
/// Tries to clear the margins of the given chunk. Fails and returns false if the
184-
/// chunk is not populated yet. Succeeds and returns true if the chunk is not Solid, as the
185-
/// chunk is assumed to have empty margins already.
186-
#[must_use]
187-
fn clear_solid_chunk_margin(&mut self, chunk: ChunkId) -> bool {
188-
let dimension = self.layout().dimension;
189-
let Some(Chunk::Populated {
190-
voxels,
191-
surface,
192-
old_surface,
193-
..
194-
}) = self.get_chunk_mut(chunk)
195-
else {
196-
return false;
197-
};
198-
199-
if voxels.is_solid() {
200-
voxels.clear_margin(dimension);
201-
*old_surface = surface.take().or(*old_surface);
162+
for chunk_direction in ChunkDirection::iter() {
163+
margins::update_margin_voxel(
164+
self,
165+
block_update.chunk_id,
166+
block_update.coords,
167+
chunk_direction,
168+
block_update.new_material,
169+
)
202170
}
203171
true
204172
}
@@ -261,27 +229,6 @@ impl VoxelData {
261229
}
262230
}
263231

264-
/// Replaces all voxels in the margin of this chunk with the "Void" material. This function is a coarse
265-
/// way to ensure that chunks are fully rendered when they need to be, avoiding a rendering bug caused
266-
/// by a voxel's surface failing to render because of a margin being solid.
267-
/// Until margins are fully implemented, any solid chunk produced by world generation should have its
268-
/// margins cleared if it, or any chunk adjacent to it, is edited, since otherwise, the margins could
269-
/// be inaccurate.
270-
pub fn clear_margin(&mut self, dimension: u8) {
271-
let data = self.data_mut(dimension);
272-
let lwm = usize::from(dimension) + 2;
273-
for z in 0..lwm {
274-
for y in 0..lwm {
275-
for x in 0..lwm {
276-
if x == 0 || x == lwm - 1 || y == 0 || y == lwm - 1 || z == 0 || z == lwm - 1 {
277-
// The current coordinates correspond to a margin point. Set it to void.
278-
data[x + y * lwm + z * lwm.pow(2)] = Material::Void;
279-
}
280-
}
281-
}
282-
}
283-
}
284-
285232
pub fn is_solid(&self) -> bool {
286233
match *self {
287234
VoxelData::Dense(_) => false,

0 commit comments

Comments
 (0)