use super::{BlockKind, SpriteKind}; use crate::{ assets::{self, AssetExt, AssetHandle, DotVoxAsset, Error}, make_case_elim, vol::{BaseVol, ReadVol, SizedVol, WriteVol}, volumes::dyna::{Dyna, DynaError}, }; use hashbrown::HashMap; use serde::Deserialize; use std::{num::NonZeroU8, sync::Arc}; use vek::*; make_case_elim!( structure_block, #[derive(Copy, Clone, PartialEq, Debug, Deserialize)] #[repr(u32)] pub enum StructureBlock { None = 0, Grass = 1, TemperateLeaves = 2, PineLeaves = 3, Acacia = 4, Mangrove = 5, PalmLeavesInner = 6, PalmLeavesOuter = 7, Water = 8, GreenSludge = 9, Fruit = 10, Coconut = 11, Chest = 12, Hollow = 13, Liana = 14, Normal(color: Rgb) = 15, Log = 16, Filled(kind: BlockKind, color: Rgb) = 17, Sprite(kind: SpriteKind) = 18, } ); #[derive(Debug)] pub enum StructureError { OutOfBounds, } #[derive(Clone, Debug)] pub struct Structure { center: Vec3, base: Arc, custom_indices: [Option; 256], } #[derive(Debug)] struct BaseStructure { vol: Dyna, ()>, palette: [StructureBlock; 256], } pub struct StructuresGroup(Vec); impl std::ops::Deref for StructuresGroup { type Target = [Structure]; fn deref(&self) -> &[Structure] { &self.0 } } impl assets::Compound for StructuresGroup { fn load( cache: &assets::AssetCache, specifier: &str, ) -> Result { let specs = cache.load::(specifier)?.read(); Ok(StructuresGroup( specs .0 .iter() .map(|sp| { let base = cache.load::>(&sp.specifier)?.cloned(); Ok(Structure { center: Vec3::from(sp.center), base, custom_indices: { let mut indices = [None; 256]; for (&idx, &custom) in default_custom_indices() .iter() .chain(sp.custom_indices.iter()) { indices[idx as usize] = Some(custom); } indices }, }) }) .collect::>()?, )) } } impl Structure { pub fn load_group(specifier: &str) -> AssetHandle { StructuresGroup::load_expect(&["world.manifests.", specifier].concat()) } pub fn with_center(mut self, center: Vec3) -> Self { self.center = center; self } pub fn get_bounds(&self) -> Aabb { Aabb { min: -self.center, max: self.base.vol.size().map(|e| e as i32) - self.center, } } } impl BaseVol for Structure { type Error = StructureError; type Vox = StructureBlock; } impl ReadVol for Structure { #[inline(always)] fn get(&self, pos: Vec3) -> Result<&Self::Vox, StructureError> { match self.base.vol.get(pos + self.center) { Ok(None) => Ok(&StructureBlock::None), Ok(Some(index)) => match &self.custom_indices[index.get() as usize] { Some(sb) => Ok(sb), None => Ok(&self.base.palette[index.get() as usize]), }, Err(DynaError::OutOfBounds) => Err(StructureError::OutOfBounds), } } } impl assets::Compound for BaseStructure { fn load( cache: &assets::AssetCache, specifier: &str, ) -> Result { let dot_vox_data = cache.load::(specifier)?.read(); let dot_vox_data = &dot_vox_data.0; if let Some(model) = dot_vox_data.models.get(0) { let mut palette = [StructureBlock::None; 256]; for (i, col) in dot_vox_data .palette .iter() .map(|col| Rgba::from(col.to_ne_bytes()).into()) .enumerate() { palette[(i + 1).min(255)] = StructureBlock::Filled(BlockKind::Misc, col); } let mut vol = Dyna::filled( Vec3::new(model.size.x, model.size.y, model.size.z), None, (), ); for voxel in &model.voxels { let _ = vol.set( Vec3::new(voxel.x, voxel.y, voxel.z).map(i32::from), Some(NonZeroU8::new(voxel.i + 1).unwrap()), ); } Ok(BaseStructure { vol, palette }) } else { Ok(BaseStructure { vol: Dyna::filled(Vec3::zero(), None, ()), palette: [StructureBlock::None; 256], }) } } } #[derive(Deserialize)] struct StructureSpec { specifier: String, center: [i32; 3], #[serde(default)] custom_indices: HashMap, } fn default_custom_indices() -> HashMap { let blocks: [_; 16] = [ /* 1 */ Some(StructureBlock::TemperateLeaves), /* 2 */ Some(StructureBlock::PineLeaves), /* 3 */ None, /* 4 */ Some(StructureBlock::Water), /* 5 */ Some(StructureBlock::Acacia), /* 6 */ Some(StructureBlock::Mangrove), /* 7 */ Some(StructureBlock::GreenSludge), /* 8 */ Some(StructureBlock::Fruit), /* 9 */ Some(StructureBlock::Grass), /* 10 */ Some(StructureBlock::Liana), /* 11 */ Some(StructureBlock::Chest), /* 12 */ Some(StructureBlock::Coconut), /* 13 */ None, /* 14 */ Some(StructureBlock::PalmLeavesOuter), /* 15 */ Some(StructureBlock::PalmLeavesInner), /* 16 */ Some(StructureBlock::Hollow), ]; blocks .iter() .enumerate() .filter_map(|(i, sb)| Some((i as u8 + 1, (*sb)?))) .collect() } #[derive(Deserialize)] struct StructuresGroupSpec(Vec); impl assets::Asset for StructuresGroupSpec { type Loader = assets::RonLoader; const EXTENSION: &'static str = "ron"; }