diff --git a/src/bin/minedmap/tile_renderer.rs b/src/bin/minedmap/tile_renderer.rs index e9a4a13..def56b4 100644 --- a/src/bin/minedmap/tile_renderer.rs +++ b/src/bin/minedmap/tile_renderer.rs @@ -8,39 +8,16 @@ use std::{ use anyhow::{Context, Result}; use glam::Vec3; use lru::LruCache; -use num_integer::div_mod_floor; use minedmap::{ io::{fs, storage}, resource::{block_color, needs_biome}, types::*, + util::coord_offset, }; use super::{common::*, region_group::RegionGroup}; -/// Offsets a chunk and block coordinate pair by a number of blocks -/// -/// As the new coordinate may end up in a different region, a region offset -/// is returned together with the new chunk and block coordinates. -fn coord_offset( - chunk: ChunkCoord, - block: BlockCoord, - offset: i32, -) -> (i8, ChunkCoord, BlockCoord) { - const CHUNKS: i32 = CHUNKS_PER_REGION as i32; - const BLOCKS: i32 = BLOCKS_PER_CHUNK as i32; - let coord = chunk.0 as i32 * BLOCKS + block.0 as i32 + offset; - let (region_chunk, block) = div_mod_floor(coord, BLOCKS); - let (region, chunk) = div_mod_floor(region_chunk, CHUNKS); - ( - region - .try_into() - .expect("the region coordinate should be in the valid range"), - ChunkCoord::new(chunk), - BlockCoord::new(block), - ) -} - fn biome_at( region_group: &RegionGroup>, chunk: ChunkCoords, @@ -283,37 +260,3 @@ impl<'a> TileRenderer<'a> { Ok(()) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_coord_offset() { - const CHUNKS: i32 = CHUNKS_PER_REGION as i32; - const BLOCKS: i32 = BLOCKS_PER_CHUNK as i32; - - for chunk in ChunkX::iter() { - for block in BlockX::iter() { - assert_eq!(coord_offset(chunk, block, 0), (0, chunk, block)); - assert_eq!( - coord_offset(chunk, block, -(CHUNKS * BLOCKS)), - (-1, chunk, block) - ); - assert_eq!( - coord_offset(chunk, block, CHUNKS * BLOCKS), - (1, chunk, block) - ); - - for offset in -(CHUNKS * BLOCKS)..(CHUNKS * BLOCKS) { - let (region2, chunk2, block2) = coord_offset(chunk, block, offset); - assert!((-1..=1).contains(®ion2)); - let coord = chunk.0 as i32 * BLOCKS + block.0 as i32 + offset; - let coord2 = - ((region2 as i32 * CHUNKS) + chunk2.0 as i32) * BLOCKS + block2.0 as i32; - assert_eq!(coord2, coord); - } - } - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 8342ba8..f46a9d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod io; pub mod resource; pub mod types; +pub mod util; pub mod world; diff --git a/src/types.rs b/src/types.rs index 465dd2a..a28583d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -47,7 +47,8 @@ macro_rules! coord_type { }; } -pub const BLOCKS_PER_CHUNK: usize = 16; +pub const BLOCK_BITS: u8 = 4; +pub const BLOCKS_PER_CHUNK: usize = 1 << BLOCK_BITS; coord_type!(BlockCoord, BLOCKS_PER_CHUNK); /// A block X coordinate relative to a chunk @@ -136,7 +137,8 @@ impl Debug for SectionBlockCoords { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct SectionY(pub i32); -pub const CHUNKS_PER_REGION: usize = 32; +pub const CHUNK_BITS: u8 = 5; +pub const CHUNKS_PER_REGION: usize = 1 << CHUNK_BITS; coord_type!(ChunkCoord, CHUNKS_PER_REGION); /// A chunk X coordinate relative to a region diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..436c070 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,84 @@ +use crate::types::*; + +pub trait ShiftMask: Sized { + type MaskedOutput; + + /// Apply a right shift to a value, and return both the result and the + /// bytes that were shifted out + fn shift_mask(self, shift: u8) -> (Self, Self::MaskedOutput); +} + +impl ShiftMask for u32 { + type MaskedOutput = u32; + + fn shift_mask(self, shift: u8) -> (u32, u32) { + let mask = (1 << shift) - 1; + (self >> shift, self & mask) + } +} + +impl ShiftMask for i32 { + type MaskedOutput = u32; + + #[inline] + fn shift_mask(self, shift: u8) -> (i32, u32) { + let mask = (1 << shift) - 1; + (self >> shift, (self as u32) & mask) + } +} + +/// Offsets a chunk and block coordinate pair by a number of blocks +/// +/// As the new coordinate may end up in a different region, a region offset +/// is returned together with the new chunk and block coordinates. +#[inline] +pub fn coord_offset( + chunk: ChunkCoord, + block: BlockCoord, + offset: i32, +) -> (i8, ChunkCoord, BlockCoord) { + let coord = ((chunk.0 as i32) << BLOCK_BITS | block.0 as i32) + offset; + let (region_chunk, block) = coord.shift_mask(BLOCK_BITS); + let (region, chunk) = region_chunk.shift_mask(CHUNK_BITS); + ( + region + .try_into() + .expect("the region coordinate should be in the valid range"), + ChunkCoord::new(chunk), + BlockCoord::new(block), + ) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_coord_offset() { + const CHUNKS: i32 = CHUNKS_PER_REGION as i32; + const BLOCKS: i32 = BLOCKS_PER_CHUNK as i32; + + for chunk in ChunkX::iter() { + for block in BlockX::iter() { + assert_eq!(coord_offset(chunk, block, 0), (0, chunk, block)); + assert_eq!( + coord_offset(chunk, block, -(CHUNKS * BLOCKS)), + (-1, chunk, block) + ); + assert_eq!( + coord_offset(chunk, block, CHUNKS * BLOCKS), + (1, chunk, block) + ); + + for offset in -(CHUNKS * BLOCKS)..(CHUNKS * BLOCKS) { + let (region2, chunk2, block2) = coord_offset(chunk, block, offset); + assert!((-1..=1).contains(®ion2)); + let coord = chunk.0 as i32 * BLOCKS + block.0 as i32 + offset; + let coord2 = + ((region2 as i32 * CHUNKS) + chunk2.0 as i32) * BLOCKS + block2.0 as i32; + assert_eq!(coord2, coord); + } + } + } + } +}