use std::collections::BTreeMap;
use crate::formats::world_stream::WorldSource;
use crate::world_segment::ids::TileId;
use crate::world_segment::source::{region_tile_bounds, Access, TileError, TileSource};
use crate::world_segment::tile::VoxelTile;
pub fn chunk_region(cx: i32, cz: i32) -> (i32, i32) {
(cx.div_euclid(32), cz.div_euclid(32))
}
pub struct WorldSourceTiles {
source: WorldSource,
min_y: i32,
max_y: i32,
}
impl WorldSourceTiles {
pub fn new(source: WorldSource, min_y: i32, max_y: i32) -> Self {
WorldSourceTiles { source, min_y, max_y }
}
fn collect_tile(&self, region_x: i32, region_z: i32) -> Result<Option<VoxelTile>, TileError> {
let (tile_id, bounds) = region_tile_bounds(region_x, region_z, self.min_y, self.max_y);
let iter = self
.source
.chunks_bounded(bounds.min, bounds.max)
.map_err(|e| TileError::Io(e.to_string()))?;
let mut blocks: BTreeMap<(i32, i32, i32), crate::BlockState> = BTreeMap::new();
for view in iter {
let view = view.map_err(|e| TileError::Malformed(e.to_string()))?;
for (x, y, z, state) in view.blocks() {
if y < self.min_y || y > self.max_y {
continue;
}
if chunk_region(view.cx(), view.cz()) != (region_x, region_z) {
continue;
}
blocks.insert((x, y, z), state.clone());
}
}
if blocks.is_empty() {
return Ok(None);
}
Ok(Some(VoxelTile::from_blocks(
tile_id,
bounds,
blocks.into_iter(),
)))
}
}
impl TileSource for WorldSourceTiles {
fn access(&self) -> Access {
Access::Random
}
fn tile_ids(&self) -> Result<Vec<TileId>, TileError> {
let mut ids: Vec<TileId> = self
.source
.region_positions()
.map_err(|e| TileError::Io(e.to_string()))?
.into_iter()
.map(|(x, z)| TileId { x, z })
.collect();
ids.sort();
ids.dedup();
Ok(ids)
}
fn tile(&self, id: TileId) -> Result<Option<VoxelTile>, TileError> {
self.collect_tile(id.x, id.z)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunk_maps_to_its_region_tile() {
assert_eq!(chunk_region(0, 0), (0, 0));
assert_eq!(chunk_region(31, 31), (0, 0));
assert_eq!(chunk_region(32, 0), (1, 0));
assert_eq!(chunk_region(-1, 0), (-1, 0));
assert_eq!(chunk_region(-32, 0), (-1, 0));
assert_eq!(chunk_region(-33, 0), (-2, 0));
}
}