-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix up margins for newly-generated chunks
- Loading branch information
Showing
4 changed files
with
259 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
use crate::{ | ||
dodeca::Vertex, | ||
math, | ||
node::VoxelData, | ||
voxel_math::{ChunkAxisPermutation, ChunkDirection, CoordAxis, CoordSign, Coords}, | ||
}; | ||
|
||
/// Updates the margins of both `voxels` and `neighbor_voxels` at the side they meet at. | ||
/// It is assumed that `voxels` corresponds to a chunk that lies at `vertex` and that | ||
/// `neighbor_voxels` is at direction `direction` from `voxels`. | ||
pub fn fix_margins( | ||
dimension: u8, | ||
vertex: Vertex, | ||
voxels: &mut VoxelData, | ||
direction: ChunkDirection, | ||
neighbor_voxels: &mut VoxelData, | ||
) { | ||
let neighbor_axis_permutation = neighbor_axis_permutation(vertex, direction); | ||
|
||
let margin_coord = CoordsWithMargins::margin_coord(dimension, direction.sign); | ||
let edge_coord = CoordsWithMargins::edge_coord(dimension, direction.sign); | ||
let voxel_data = voxels.data_mut(dimension); | ||
let neighbor_voxel_data = neighbor_voxels.data_mut(dimension); | ||
for j in 0..dimension { | ||
for i in 0..dimension { | ||
// Determine coordinates of the edge voxel (to read from) and the margin voxel (to write to) | ||
// in voxel_data's perspective. To convert to neighbor_voxel_data's perspective, left-multiply | ||
// by neighbor_axis_permutation. | ||
let coords_of_edge_voxel = CoordsWithMargins(math::tuv_to_xyz( | ||
direction.axis as usize, | ||
[edge_coord, i + 1, j + 1], | ||
)); | ||
let coords_of_margin_voxel = CoordsWithMargins(math::tuv_to_xyz( | ||
direction.axis as usize, | ||
[margin_coord, i + 1, j + 1], | ||
)); | ||
|
||
// Use neighbor_voxel_data to set margins of voxel_data | ||
voxel_data[coords_of_margin_voxel.to_index(dimension)] = neighbor_voxel_data | ||
[(neighbor_axis_permutation * coords_of_edge_voxel).to_index(dimension)]; | ||
|
||
// Use voxel_data to set margins of neighbor_voxel_data | ||
neighbor_voxel_data | ||
[(neighbor_axis_permutation * coords_of_margin_voxel).to_index(dimension)] = | ||
voxel_data[coords_of_edge_voxel.to_index(dimension)]; | ||
} | ||
} | ||
} | ||
|
||
/// Updates the margins of a given VoxelData to match the voxels they're next to. This is a good assumption to start | ||
/// with before taking into account neighboring chunks because it means that no surface will be present on the boundaries | ||
/// of the chunk, resulting in the least rendering. This is also generally accurate when the neighboring chunks are solid. | ||
pub fn initialize_margins(dimension: u8, voxels: &mut VoxelData) { | ||
// If voxels is solid, the margins are already set up the way they should be. | ||
if voxels.is_solid() { | ||
return; | ||
} | ||
|
||
for direction in ChunkDirection::iter() { | ||
let margin_coord = CoordsWithMargins::margin_coord(dimension, direction.sign); | ||
let edge_coord = CoordsWithMargins::edge_coord(dimension, direction.sign); | ||
let chunk_data = voxels.data_mut(dimension); | ||
for j in 0..dimension { | ||
for i in 0..dimension { | ||
// Determine coordinates of the edge voxel (to read from) and the margin voxel (to write to). | ||
let coords_of_edge_voxel = CoordsWithMargins(math::tuv_to_xyz( | ||
direction.axis as usize, | ||
[edge_coord, i + 1, j + 1], | ||
)); | ||
let coords_of_margin_voxel = CoordsWithMargins(math::tuv_to_xyz( | ||
direction.axis as usize, | ||
[margin_coord, i + 1, j + 1], | ||
)); | ||
|
||
chunk_data[coords_of_margin_voxel.to_index(dimension)] = | ||
chunk_data[coords_of_edge_voxel.to_index(dimension)]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn neighbor_axis_permutation(vertex: Vertex, direction: ChunkDirection) -> ChunkAxisPermutation { | ||
match direction.sign { | ||
CoordSign::Plus => vertex.chunk_axis_permutations()[direction.axis as usize], | ||
CoordSign::Minus => ChunkAxisPermutation::IDENTITY, | ||
} | ||
} | ||
|
||
/// Coordinates for a discrete voxel within a chunk, including margins | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
struct CoordsWithMargins(pub [u8; 3]); | ||
|
||
impl CoordsWithMargins { | ||
/// Returns the array index in `VoxelData` corresponding to these coordinates | ||
pub fn to_index(self, chunk_size: u8) -> usize { | ||
let chunk_size_with_margin = chunk_size as usize + 2; | ||
(self.0[0] as usize) | ||
+ (self.0[1] as usize) * chunk_size_with_margin | ||
+ (self.0[2] as usize) * chunk_size_with_margin.pow(2) | ||
} | ||
|
||
/// Returns the x, y, or z coordinate that would correspond to the margin in the direction of `sign` | ||
pub fn margin_coord(chunk_size: u8, sign: CoordSign) -> u8 { | ||
match sign { | ||
CoordSign::Plus => chunk_size + 1, | ||
CoordSign::Minus => 0, | ||
} | ||
} | ||
|
||
/// Returns the x, y, or z coordinate that would correspond to the voxel meeting the chunk boundary in the direction of `sign` | ||
pub fn edge_coord(chunk_size: u8, sign: CoordSign) -> u8 { | ||
match sign { | ||
CoordSign::Plus => chunk_size, | ||
CoordSign::Minus => 1, | ||
} | ||
} | ||
} | ||
|
||
impl From<Coords> for CoordsWithMargins { | ||
#[inline] | ||
fn from(value: Coords) -> Self { | ||
CoordsWithMargins([value.0[0] + 1, value.0[1] + 1, value.0[2] + 1]) | ||
} | ||
} | ||
|
||
impl std::ops::Index<CoordAxis> for CoordsWithMargins { | ||
type Output = u8; | ||
|
||
#[inline] | ||
fn index(&self, coord_axis: CoordAxis) -> &u8 { | ||
self.0.index(coord_axis as usize) | ||
} | ||
} | ||
|
||
impl std::ops::IndexMut<CoordAxis> for CoordsWithMargins { | ||
#[inline] | ||
fn index_mut(&mut self, coord_axis: CoordAxis) -> &mut u8 { | ||
self.0.index_mut(coord_axis as usize) | ||
} | ||
} | ||
|
||
impl std::ops::Mul<CoordsWithMargins> for ChunkAxisPermutation { | ||
type Output = CoordsWithMargins; | ||
|
||
fn mul(self, rhs: CoordsWithMargins) -> Self::Output { | ||
let mut result = CoordsWithMargins([0; 3]); | ||
for axis in CoordAxis::iter() { | ||
result[self[axis]] = rhs[axis]; | ||
} | ||
result | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{dodeca::Vertex, voxel_math::Coords, world::Material}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_fix_margins() { | ||
// This test case can set up empirically by placing blocks and printing their coordinates to confirm which | ||
// coordinates are adjacent to each other. | ||
|
||
// `voxels` lives at vertex F | ||
let mut voxels = VoxelData::Solid(Material::Void); | ||
voxels.data_mut(12)[Coords([11, 2, 10]).to_index(12)] = Material::WoodPlanks; | ||
|
||
// `neighbor_voxels` lives at vertex J | ||
let mut neighbor_voxels = VoxelData::Solid(Material::Void); | ||
neighbor_voxels.data_mut(12)[Coords([2, 10, 11]).to_index(12)] = Material::Grass; | ||
|
||
// Sanity check that voxel adjacencies are as expected. If the test fails here, it's likely that "dodeca.rs" was | ||
// redesigned, and the test itself will have to be fixed, rather than the code being tested. | ||
assert_eq!(Vertex::F.adjacent_vertices()[0], Vertex::J); | ||
assert_eq!(Vertex::J.adjacent_vertices()[2], Vertex::F); | ||
|
||
// Sanity check that voxels are populated as expected, using `CoordsWithMargins` for consistency with the actual | ||
// test case. | ||
assert_eq!( | ||
voxels.get(CoordsWithMargins([12, 3, 11]).to_index(12)), | ||
Material::WoodPlanks | ||
); | ||
assert_eq!( | ||
neighbor_voxels.get(CoordsWithMargins([3, 11, 12]).to_index(12)), | ||
Material::Grass | ||
); | ||
|
||
fix_margins( | ||
12, | ||
Vertex::F, | ||
&mut voxels, | ||
ChunkDirection::PLUS_X, | ||
&mut neighbor_voxels, | ||
); | ||
|
||
// Actual verification: Check that the margins were set correctly | ||
assert_eq!( | ||
voxels.get(CoordsWithMargins([13, 3, 11]).to_index(12)), | ||
Material::Grass | ||
); | ||
assert_eq!( | ||
neighbor_voxels.get(CoordsWithMargins([3, 11, 13]).to_index(12)), | ||
Material::WoodPlanks | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_initialize_margins() { | ||
let mut voxels = VoxelData::Solid(Material::Void); | ||
voxels.data_mut(12)[Coords([11, 2, 10]).to_index(12)] = Material::WoodPlanks; | ||
assert_eq!( | ||
voxels.get(CoordsWithMargins([12, 3, 11]).to_index(12)), | ||
Material::WoodPlanks | ||
); | ||
|
||
initialize_margins(12, &mut voxels); | ||
|
||
assert_eq!( | ||
voxels.get(CoordsWithMargins([13, 3, 11]).to_index(12)), | ||
Material::WoodPlanks | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters