use crate::make_case_elim; use serde::{Deserialize, Serialize}; make_case_elim!( body, #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] pub enum Body { DefaultAirship = 0, } ); impl From for super::Body { fn from(body: Body) -> Self { super::Body::Ship(body) } } impl Body { pub fn manifest_entry(&self) -> &'static str { match self { Body::DefaultAirship => "Human_Airship", } } } /// Terrain is 11.0 scale relative to small-scale voxels, and all figures get /// multiplied by 0.8 in rendering. For now, have a constant in `comp::Scale` /// that compensates for both of these, but there might be a more elegant way /// (e.g. using `Scale(0.8)` for everything else and not having a magic number /// in figure rendering, and multiplying terrain models by 11.0 in animation). pub const AIRSHIP_SCALE: f32 = 11.0 / 0.8; /// Duplicate of some of the things defined in `voxygen::scene::figure::load` to /// avoid having to refactor all of that to `common` for using voxels as /// collider geometry pub mod figuredata { use crate::{ assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron}, figure::cell::Cell, terrain::{ block::{Block, BlockKind}, sprite::SpriteKind, }, volumes::dyna::{ColumnAccess, Dyna}, }; use hashbrown::HashMap; use lazy_static::lazy_static; use serde::Deserialize; use vek::Vec3; #[derive(Deserialize)] pub struct VoxSimple(pub String); #[derive(Deserialize)] pub struct ShipCentralSpec(pub HashMap); #[derive(Deserialize)] pub struct SidedShipCentralVoxSpec { pub bone0: ShipCentralSubSpec, pub bone1: ShipCentralSubSpec, pub bone2: ShipCentralSubSpec, } #[derive(Deserialize)] pub struct ShipCentralSubSpec { pub offset: [f32; 3], pub phys_offset: [f32; 3], pub central: VoxSimple, } /// manual instead of through `make_vox_spec!` so that it can be in `common` #[derive(Clone)] pub struct ShipSpec { pub central: AssetHandle>, pub colliders: HashMap, } #[derive(Clone)] pub struct VoxelCollider { pub dyna: Dyna, pub translation: Vec3, } impl assets::Compound for ShipSpec { fn load( cache: &assets::AssetCache, _: &str, ) -> Result { let manifest: AssetHandle> = AssetExt::load("server.manifests.ship_manifest")?; let mut colliders = HashMap::new(); for (_, spec) in (manifest.read().0).0.iter() { for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() { // TODO: Currently both client and server load models and manifests from // "server.voxel.". In order to support CSG procedural airships, we probably // need to load them in the server and sync them as an ECS resource. let vox = cache.load::(&["server.voxel.", &bone.central.0].concat())?; let dyna = Dyna::::from_vox(&vox.read().0, false); let dyna = dyna.map_into(|cell| { if let Some(rgb) = cell.get_color() { Block::new(BlockKind::Misc, rgb) } else { Block::air(SpriteKind::Empty) } }); let collider = VoxelCollider { dyna, translation: Vec3::from(bone.offset) + Vec3::from(bone.phys_offset), }; colliders.insert(bone.central.0.clone(), collider); } } Ok(ShipSpec { central: manifest, colliders, }) } } lazy_static! { // TODO: Load this from the ECS as a resource, and maybe make it more general than ships // (although figuring out how to keep the figure bones in sync with the terrain offsets seems // like a hard problem if they're not the same manifest) pub static ref VOXEL_COLLIDER_MANIFEST: AssetHandle = AssetExt::load_expect("server.manifests.ship_manifest"); } }