diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index 1d068ea772..6827f42355 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -881,6 +881,20 @@ ), species: () ), + biped_small: ( + body: ( + keyword: "biped_small", + names: [ + "Harvey" + ] + ), + species: ( + clownfish: ( + keyword: "gremlin", + generic: "Gremlin" + ) + ) + ), fish_small: ( body: ( keyword: "fish_small", diff --git a/assets/voxygen/voxel/biped_small_central_manifest.ron b/assets/voxygen/voxel/biped_small_central_manifest.ron new file mode 100644 index 0000000000..50dd30551c --- /dev/null +++ b/assets/voxygen/voxel/biped_small_central_manifest.ron @@ -0,0 +1,46 @@ +({ + (Gremlin, Male): ( + head: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + chest: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + shorts: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + tail: ( + offset: (-0.5, -5.0, -2.5), + central: ("npc.gremlin.male.tail"), + ), + main: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + ), + (Gremlin, Female): ( + head: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + chest: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + shorts: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + tail: ( + offset: (-0.5, -5.0, -2.5), + central: ("npc.gremlin.male.tail"), + ), + main: ( + offset: (-1.5, -7.5, -5.0), + central: ("npc.gremlin.male.chest"), + ), + ), +}) diff --git a/assets/voxygen/voxel/biped_small_lateral_manifest.ron b/assets/voxygen/voxel/biped_small_lateral_manifest.ron new file mode 100644 index 0000000000..75fa3b3901 --- /dev/null +++ b/assets/voxygen/voxel/biped_small_lateral_manifest.ron @@ -0,0 +1,38 @@ +({ + (Gremlin, Male): ( + hand_l: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.hand_l"), + ), + hand_r: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.hand_r"), + ), + foot_l: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.fooot_l"), + ), + foot_r: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.foot_r"), + ), + ), + (Gremlin, Female): ( + hand_l: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.hand_l"), + ), + hand_r: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.hand_r"), + ), + foot_l: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.fooot_l"), + ), + foot_r: ( + offset: (-0.5, -3.0, -4.0), + lateral: ("npc.gremlin.male.foot_r"), + ), + ), +}) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index b80c4b1c06..2d60b968c0 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -150,6 +150,7 @@ impl<'a> From<&'a Body> for Psyche { quadruped_low::Species::Pangolin => 0.4, _ => 0.6, }, + Body::BipedSmall(_) => 0.5, Body::BirdMedium(_) => 0.5, Body::BirdSmall(_) => 0.4, Body::FishMedium(_) => 0.15, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 742a487adb..a25f9ff89e 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -1,4 +1,5 @@ pub mod biped_large; +pub mod biped_small; pub mod bird_medium; pub mod bird_small; pub mod dragon; @@ -38,10 +39,11 @@ make_case_elim!( BirdSmall(body: bird_small::Body) = 6, FishSmall(body: fish_small::Body) = 7, BipedLarge(body: biped_large::Body)= 8, - Object(body: object::Body) = 9, - Golem(body: golem::Body) = 10, - Theropod(body: theropod::Body) = 11, - QuadrupedLow(body: quadruped_low::Body) = 12, + BipedSmall(body: biped_small::Body)= 9, + Object(body: object::Body) = 10, + Golem(body: golem::Body) = 11, + Theropod(body: theropod::Body) = 12, + QuadrupedLow(body: quadruped_low::Body) = 13, } ); @@ -71,6 +73,7 @@ pub struct AllBodies { pub bird_small: BodyData, pub fish_small: BodyData>, pub biped_large: BodyData>, + pub biped_small: BodyData>, pub object: BodyData, pub golem: BodyData>, pub theropod: BodyData>, @@ -91,6 +94,7 @@ impl core::ops::Index for AllBodies &self.fish_medium.body, NpcKind::Clownfish => &self.fish_small.body, NpcKind::Ogre => &self.biped_large.body, + NpcKind::Gremlin => &self.biped_small.body, NpcKind::StoneGolem => &self.golem.body, NpcKind::Archaeos => &self.theropod.body, NpcKind::Reddragon => &self.dragon.body, @@ -115,6 +119,7 @@ impl<'a, BodyMeta, SpeciesMeta> core::ops::Index<&'a Body> for AllBodies &self.bird_small.body, Body::FishSmall(_) => &self.fish_small.body, Body::BipedLarge(_) => &self.biped_large.body, + Body::BipedSmall(_) => &self.biped_small.body, Body::Object(_) => &self.object.body, Body::Golem(_) => &self.golem.body, Body::Theropod(_) => &self.theropod.body, @@ -200,6 +205,7 @@ impl Body { _ => 4.6, }, Body::Golem(_) => 2.5, + Body::BipedSmall(_) => 0.75, Body::Object(_) => 0.4, } } @@ -262,6 +268,7 @@ impl Body { _ => 4.6, }, Body::Golem(_) => 5.0, + Body::BipedSmall(_) => 1.5, Body::Object(object) => match object { object::Body::Crossbow => 1.7, _ => 1.0, @@ -339,6 +346,7 @@ impl Body { biped_large::Species::Mindflayer => 8000, _ => 1000, }, + Body::BipedSmall(_) => 10000, Body::Object(object) => match object { object::Body::TrainingDummy => 10000, object::Body::Crossbow => 800, @@ -428,6 +436,7 @@ impl Body { biped_large::Species::Mindflayer => 250, _ => 100, }, + Body::BipedSmall(_) => 10, Body::Object(_) => 10, Body::Golem(_) => 260, Body::Theropod(_) => 20, diff --git a/common/src/comp/body/biped_small.rs b/common/src/comp/body/biped_small.rs new file mode 100644 index 0000000000..b6fe13c5ff --- /dev/null +++ b/common/src/comp/body/biped_small.rs @@ -0,0 +1,78 @@ +use crate::{make_case_elim, make_proj_elim}; +use rand::{seq::SliceRandom, thread_rng}; +use serde::{Deserialize, Serialize}; + +make_proj_elim!( + body, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub struct Body { + pub species: Species, + pub body_type: BodyType, + } +); + +impl Body { + pub fn random() -> Self { + let mut rng = thread_rng(); + let species = *(&ALL_SPECIES).choose(&mut rng).unwrap(); + Self::random_with(&mut rng, &species) + } + + #[inline] + pub fn random_with(rng: &mut impl rand::Rng, &species: &Species) -> Self { + let body_type = *(&ALL_BODY_TYPES).choose(rng).unwrap(); + Self { species, body_type } + } +} + +impl From for super::Body { + fn from(body: Body) -> Self { super::Body::BipedSmall(body) } +} + +make_case_elim!( + species, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Species { + Gremlin = 0, + } +); + +/// Data representing per-species generic data. +/// +/// NOTE: Deliberately don't (yet?) implement serialize. +#[derive(Clone, Debug, Deserialize)] +pub struct AllSpecies { + pub gremlin: SpeciesMeta, +} + +impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { + type Output = SpeciesMeta; + + #[inline] + fn index(&self, &index: &'a Species) -> &Self::Output { + match index { + Species::Gremlin => &self.gremlin, + } + } +} + +pub const ALL_SPECIES: [Species; 1] = [Species::Gremlin]; + +impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { + type IntoIter = std::iter::Copied>; + type Item = Species; + + fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() } +} + +make_case_elim!( + body_type, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum BodyType { + Female = 0, + Male = 1, + } +); +pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male]; diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index e893baaa39..e9b10c2cf4 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -46,7 +46,7 @@ pub use self::{ aura::{Aura, AuraChange, AuraKind, Auras}, beam::{Beam, BeamSegment}, body::{ - biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, + biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object, quadruped_low, quadruped_medium, quadruped_small, theropod, AllBodies, Body, BodyData, }, diff --git a/common/src/npc.rs b/common/src/npc.rs index 8d68c507e9..17414adc78 100644 --- a/common/src/npc.rs +++ b/common/src/npc.rs @@ -16,13 +16,14 @@ pub enum NpcKind { Clownfish, Marlin, Ogre, + Gremlin, Archaeos, StoneGolem, Reddragon, Crocodile, } -pub const ALL_NPCS: [NpcKind; 11] = [ +pub const ALL_NPCS: [NpcKind; 12] = [ NpcKind::Humanoid, NpcKind::Wolf, NpcKind::Pig, @@ -30,6 +31,7 @@ pub const ALL_NPCS: [NpcKind; 11] = [ NpcKind::Clownfish, NpcKind::Marlin, NpcKind::Ogre, + NpcKind::Gremlin, NpcKind::Archaeos, NpcKind::StoneGolem, NpcKind::Reddragon, @@ -123,6 +125,7 @@ pub fn kind_to_body(kind: NpcKind) -> Body { NpcKind::Clownfish => comp::fish_small::Body::random().into(), NpcKind::Marlin => comp::fish_medium::Body::random().into(), NpcKind::Ogre => comp::biped_large::Body::random().into(), + NpcKind::Gremlin => comp::biped_small::Body::random().into(), NpcKind::Archaeos => comp::theropod::Body::random().into(), NpcKind::StoneGolem => comp::golem::Body::random().into(), NpcKind::Reddragon => comp::dragon::Body::random().into(), @@ -249,6 +252,14 @@ impl NpcBody { comp::biped_large::Body::random_with, ) }) + .or_else(|| { + parse( + s, + NpcKind::Gremlin, + &npc_names.biped_small, + comp::biped_small::Body::random_with, + ) + }) .or_else(|| { parse( s, diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 334f8ae039..84aa9d1cc9 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -60,6 +60,7 @@ impl Body { quadruped_medium::Species::Kelpie => 180.0, quadruped_medium::Species::Horse => 180.0, }, + Body::BipedSmall(_) => 100.0, Body::BirdMedium(_) => 80.0, Body::FishMedium(_) => 80.0, Body::Dragon(_) => 250.0, @@ -116,6 +117,7 @@ impl Body { Body::BirdSmall(_) => 35.0, Body::FishSmall(_) => 10.0, Body::BipedLarge(_) => 12.0, + Body::BipedSmall(_) => 12.0, Body::Object(_) => 10.0, Body::Golem(_) => 8.0, Body::Theropod(theropod) => match theropod.species { diff --git a/voxygen/anim/src/biped_small/idle.rs b/voxygen/anim/src/biped_small/idle.rs new file mode 100644 index 0000000000..6e90bc485e --- /dev/null +++ b/voxygen/anim/src/biped_small/idle.rs @@ -0,0 +1,44 @@ +use super::{ + super::{vek::*, Animation}, + BipedSmallSkeleton, SkeletonAttr, +}; + +pub struct IdleAnimation; + +type IdleAnimationDependency = (Vec3, Vec3, Vec3, f64, Vec3); + +impl Animation for IdleAnimation { + type Dependency = IdleAnimationDependency; + type Skeleton = BipedSmallSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"biped_small_idle\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "Biped_small_idle")] + + fn update_skeleton_inner( + skeleton: &Self::Skeleton, + (_velocity, _orientation, _last_ori, _global_time, _avg_vel): Self::Dependency, + anim_time: f64, + _rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + next.head.scale = Vec3::one(); + + next.chest.scale = Vec3::one() / 13.0; + + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 13.0; + next.shorts.position = Vec3::new(0.0, s_a.shorts.0, s_a.shorts.1); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.hand_l.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.foot_l.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2); + next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2); + + next + } +} diff --git a/voxygen/anim/src/biped_small/mod.rs b/voxygen/anim/src/biped_small/mod.rs new file mode 100644 index 0000000000..408ab9f139 --- /dev/null +++ b/voxygen/anim/src/biped_small/mod.rs @@ -0,0 +1,114 @@ +pub mod idle; +pub mod run; + +// Reexports +pub use self::{idle::IdleAnimation, run::RunAnimation}; + +use super::{make_bone, vek::*, FigureBoneData, Skeleton}; +use common::comp::{self}; +use core::convert::TryFrom; + +pub type Body = comp::biped_small::Body; + +skeleton_impls!(struct BipedSmallSkeleton { + + head, + + chest, + + shorts, + + tail, + + main, + + hand_l, + + hand_r, + + foot_l, + + foot_r, +}); + +impl Skeleton for BipedSmallSkeleton { + type Attr = SkeletonAttr; + type Body = Body; + + const BONE_COUNT: usize = 9; + #[cfg(feature = "use-dyn-lib")] + const COMPUTE_FN: &'static [u8] = b"biped_small_compute_mats\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_small_compute_mats")] + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); + let shorts_mat = chest_mat * Mat4::::from(self.chest); + + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(shorts_mat), + make_bone(shorts_mat * Mat4::::from(self.tail)), + make_bone(chest_mat * Mat4::::from(self.main)), + make_bone(chest_mat * Mat4::::from(self.hand_l)), + make_bone(chest_mat * Mat4::::from(self.hand_l)), + make_bone(base_mat * Mat4::::from(self.foot_l)), + make_bone(base_mat * Mat4::::from(self.foot_l)), + ]; + Vec3::default() + } +} + +pub struct SkeletonAttr { + head: (f32, f32), + chest: (f32, f32), + shorts: (f32, f32), + tail: (f32, f32), + hand: (f32, f32, f32), + foot: (f32, f32, f32), +} + +impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { + type Error = (); + + fn try_from(body: &'a comp::Body) -> Result { + match body { + comp::Body::BipedSmall(body) => Ok(SkeletonAttr::from(body)), + _ => Err(()), + } + } +} + +impl Default for SkeletonAttr { + fn default() -> Self { + Self { + head: (0.0, 0.0), + chest: (0.0, 0.0), + shorts: (0.0, 0.0), + tail: (0.0, 0.0), + hand: (0.0, 0.0, 0.0), + foot: (0.0, 0.0, 0.0), + } + } +} + +impl<'a> From<&'a Body> for SkeletonAttr { + fn from(body: &'a Body) -> Self { + use comp::biped_small::Species::*; + Self { + head: match (body.species, body.body_type) { + (Gremlin, _) => (0.0, 5.0), + }, + chest: match (body.species, body.body_type) { + (Gremlin, _) => (0.0, 5.0), + }, + shorts: match (body.species, body.body_type) { + (Gremlin, _) => (0.0, 5.0), + }, + tail: match (body.species, body.body_type) { + (Gremlin, _) => (-7.5, -0.5), + }, + hand: match (body.species, body.body_type) { + (Gremlin, _) => (2.0, 0.5, 1.0), + }, + foot: match (body.species, body.body_type) { + (Gremlin, _) => (2.0, 0.5, 1.0), + }, + } + } +} diff --git a/voxygen/anim/src/biped_small/run.rs b/voxygen/anim/src/biped_small/run.rs new file mode 100644 index 0000000000..c1ed6375ae --- /dev/null +++ b/voxygen/anim/src/biped_small/run.rs @@ -0,0 +1,44 @@ +use super::{ + super::{vek::*, Animation}, + BipedSmallSkeleton, SkeletonAttr, +}; + +pub struct RunAnimation; + +type RunAnimationDependency = (Vec3, Vec3, Vec3, f64, Vec3); + +impl Animation for RunAnimation { + type Dependency = RunAnimationDependency; + type Skeleton = BipedSmallSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"biped_small_run\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "Biped_small_run")] + + fn update_skeleton_inner( + skeleton: &Self::Skeleton, + (_velocity, _orientation, _last_ori, _global_time, _avg_vel): Self::Dependency, + anim_time: f64, + _rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + next.head.scale = Vec3::one(); + + next.chest.scale = Vec3::one() / 13.0; + + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 13.0; + next.shorts.position = Vec3::new(0.0, s_a.shorts.0, s_a.shorts.1); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.hand_l.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.foot_l.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2); + next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2); + + next + } +} diff --git a/voxygen/anim/src/lib.rs b/voxygen/anim/src/lib.rs index ffe1380b00..4e6a9cbbc8 100644 --- a/voxygen/anim/src/lib.rs +++ b/voxygen/anim/src/lib.rs @@ -40,6 +40,7 @@ macro_rules! skeleton_impls { } pub mod biped_large; +pub mod biped_small; pub mod bird_medium; pub mod bird_small; pub mod character; diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 0f2bf8a2d8..412d1475e8 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -3,6 +3,7 @@ use common::{ assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron}, comp::{ biped_large::{self, BodyType as BLBodyType, Species as BLSpecies}, + biped_small::{self, BodyType as BSBodyType, Species as BSSpecies}, bird_medium::{self, BodyType as BMBodyType, Species as BMSpecies}, bird_small, dragon::{self, BodyType as DBodyType, Species as DSpecies}, @@ -2468,6 +2469,241 @@ impl FishSmallLateralSpec { //// +#[derive(Deserialize)] +struct BipedSmallCentralSpec(HashMap<(BSSpecies, BSBodyType), SidedBSCentralVoxSpec>); + +#[derive(Deserialize)] +struct SidedBSCentralVoxSpec { + head: BipedSmallCentralSubSpec, + chest: BipedSmallCentralSubSpec, + shorts: BipedSmallCentralSubSpec, + tail: BipedSmallCentralSubSpec, + main: BipedSmallCentralSubSpec, +} +#[derive(Deserialize)] +struct BipedSmallCentralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + central: VoxSimple, +} +#[derive(Deserialize)] +struct BipedSmallLateralSpec(HashMap<(BSSpecies, BSBodyType), SidedBSLateralVoxSpec>); +#[derive(Deserialize)] +struct SidedBSLateralVoxSpec { + hand_l: BipedSmallLateralSubSpec, + hand_r: BipedSmallLateralSubSpec, + foot_l: BipedSmallLateralSubSpec, + foot_r: BipedSmallLateralSubSpec, +} +#[derive(Deserialize)] +struct BipedSmallLateralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + lateral: VoxSimple, +} + +make_vox_spec!( + biped_small::Body, + struct BipedSmallSpec { + central: BipedSmallCentralSpec = "voxygen.voxel.biped_small_central_manifest", + lateral: BipedSmallLateralSpec = "voxygen.voxel.biped_small_lateral_manifest", + }, + |FigureKey { body, .. }, spec| { + [ + Some(spec.central.read().0.mesh_head( + body.species, + body.body_type, + )), + Some(spec.central.read().0.mesh_chest( + body.species, + body.body_type, + )), + Some(spec.central.read().0.mesh_shorts( + body.species, + body.body_type, + )), + Some(spec.central.read().0.mesh_tail( + body.species, + body.body_type, + )), + Some(spec.central.read().0.mesh_main( + body.species, + body.body_type, + )), + Some(spec.lateral.read().0.mesh_hand_l( + body.species, + body.body_type, + )), + Some(spec.lateral.read().0.mesh_hand_r( + body.species, + body.body_type, + )), + Some(spec.lateral.read().0.mesh_foot_l( + body.species, + body.body_type, + )), + Some(spec.lateral.read().0.mesh_foot_r( + body.species, + body.body_type, + )), + None, + None, + None, + None, + None, + None, + None, + ] + }, +); + +impl BipedSmallCentralSpec { + fn mesh_head(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No head specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let central = graceful_load_segment(&spec.head.central.0); + + (central, Vec3::from(spec.head.offset)) + } + + fn mesh_chest(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No chest specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let central = graceful_load_segment(&spec.chest.central.0); + + (central, Vec3::from(spec.chest.offset)) + } + + fn mesh_shorts(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No shorts specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let central = graceful_load_segment(&spec.shorts.central.0); + + (central, Vec3::from(spec.shorts.offset)) + } + + fn mesh_tail(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No tail specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let central = graceful_load_segment(&spec.tail.central.0); + + (central, Vec3::from(spec.tail.offset)) + } + + fn mesh_main(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No main specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let central = graceful_load_segment(&spec.main.central.0); + + (central, Vec3::from(spec.main.offset)) + } +} + +impl BipedSmallLateralSpec { + fn mesh_hand_l(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No hand specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let lateral = graceful_load_segment(&spec.hand_l.lateral.0); + + (lateral, Vec3::from(spec.hand_l.offset)) + } + + fn mesh_hand_r(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No hand specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let lateral = graceful_load_segment(&spec.hand_r.lateral.0); + + (lateral, Vec3::from(spec.hand_r.offset)) + } + + fn mesh_foot_l(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No foot specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let lateral = graceful_load_segment(&spec.foot_l.lateral.0); + + (lateral, Vec3::from(spec.foot_l.offset)) + } + + fn mesh_foot_r(&self, species: BSSpecies, body_type: BSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No foot specification exists for the combination of {:?} and {:?}", + species, body_type + ); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + }, + }; + let lateral = graceful_load_segment(&spec.foot_r.lateral.0); + + (lateral, Vec3::from(spec.foot_r.offset)) + } +} +//// + #[derive(Deserialize)] struct DragonCentralSpec(HashMap<(DSpecies, DBodyType), SidedDCentralVoxSpec>); diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 295a3552e5..88c6146357 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -18,10 +18,10 @@ use crate::{ }, }; use anim::{ - biped_large::BipedLargeSkeleton, bird_medium::BirdMediumSkeleton, - bird_small::BirdSmallSkeleton, character::CharacterSkeleton, dragon::DragonSkeleton, - fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton, golem::GolemSkeleton, - object::ObjectSkeleton, quadruped_low::QuadrupedLowSkeleton, + biped_large::BipedLargeSkeleton, biped_small::BipedSmallSkeleton, + bird_medium::BirdMediumSkeleton, bird_small::BirdSmallSkeleton, character::CharacterSkeleton, + dragon::DragonSkeleton, fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton, + golem::GolemSkeleton, object::ObjectSkeleton, quadruped_low::QuadrupedLowSkeleton, quadruped_medium::QuadrupedMediumSkeleton, quadruped_small::QuadrupedSmallSkeleton, theropod::TheropodSkeleton, Animation, Skeleton, }; @@ -98,6 +98,7 @@ struct FigureMgrStates { bird_small_states: HashMap>, fish_small_states: HashMap>, biped_large_states: HashMap>, + biped_small_states: HashMap>, golem_states: HashMap>, object_states: HashMap>, } @@ -116,6 +117,7 @@ impl FigureMgrStates { bird_small_states: HashMap::new(), fish_small_states: HashMap::new(), biped_large_states: HashMap::new(), + biped_small_states: HashMap::new(), golem_states: HashMap::new(), object_states: HashMap::new(), } @@ -172,6 +174,10 @@ impl FigureMgrStates { .biped_large_states .get_mut(&entity) .map(DerefMut::deref_mut), + Body::BipedSmall(_) => self + .biped_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), Body::Golem(_) => self.golem_states.get_mut(&entity).map(DerefMut::deref_mut), Body::Object(_) => self.object_states.get_mut(&entity).map(DerefMut::deref_mut), } @@ -196,6 +202,7 @@ impl FigureMgrStates { Body::BirdSmall(_) => self.bird_small_states.remove(&entity).map(|e| e.meta), Body::FishSmall(_) => self.fish_small_states.remove(&entity).map(|e| e.meta), Body::BipedLarge(_) => self.biped_large_states.remove(&entity).map(|e| e.meta), + Body::BipedSmall(_) => self.biped_small_states.remove(&entity).map(|e| e.meta), Body::Golem(_) => self.golem_states.remove(&entity).map(|e| e.meta), Body::Object(_) => self.object_states.remove(&entity).map(|e| e.meta), } @@ -214,6 +221,7 @@ impl FigureMgrStates { self.bird_small_states.retain(|k, v| f(k, &mut *v)); self.fish_small_states.retain(|k, v| f(k, &mut *v)); self.biped_large_states.retain(|k, v| f(k, &mut *v)); + self.biped_small_states.retain(|k, v| f(k, &mut *v)); self.golem_states.retain(|k, v| f(k, &mut *v)); self.object_states.retain(|k, v| f(k, &mut *v)); } @@ -231,6 +239,7 @@ impl FigureMgrStates { + self.bird_small_states.len() + self.fish_small_states.len() + self.biped_large_states.len() + + self.biped_small_states.len() + self.golem_states.len() + self.object_states.len() } @@ -290,6 +299,11 @@ impl FigureMgrStates { .iter() .filter(|(_, c)| c.visible()) .count() + + self + .biped_small_states + .iter() + .filter(|(_, c)| c.visible()) + .count() + self .golem_states .iter() @@ -316,6 +330,7 @@ pub struct FigureMgr { fish_medium_model_cache: FigureModelCache, fish_small_model_cache: FigureModelCache, biped_large_model_cache: FigureModelCache, + biped_small_model_cache: FigureModelCache, object_model_cache: FigureModelCache, golem_model_cache: FigureModelCache, states: FigureMgrStates, @@ -336,6 +351,7 @@ impl FigureMgr { fish_medium_model_cache: FigureModelCache::new(), fish_small_model_cache: FigureModelCache::new(), biped_large_model_cache: FigureModelCache::new(), + biped_small_model_cache: FigureModelCache::new(), object_model_cache: FigureModelCache::new(), golem_model_cache: FigureModelCache::new(), states: FigureMgrStates::default(), @@ -365,6 +381,8 @@ impl FigureMgr { .clean(&mut self.col_lights, tick); self.biped_large_model_cache .clean(&mut self.col_lights, tick); + self.biped_small_model_cache + .clean(&mut self.col_lights, tick); self.object_model_cache.clean(&mut self.col_lights, tick); self.golem_model_cache.clean(&mut self.col_lights, tick); } @@ -2519,6 +2537,99 @@ impl FigureMgr { terrain, ); }, + Body::BipedSmall(body) => { + let (model, skeleton_attr) = self.biped_small_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + inventory, + tick, + player_camera_mode, + player_character_state, + scene_data.thread_pool, + ); + + let state = self + .states + .biped_small_states + .entry(entity) + .or_insert_with(|| { + FigureState::new(renderer, BipedSmallSkeleton::default()) + }); + + let (character, last_character) = match (character, last_character) { + (Some(c), Some(l)) => (c, l), + _ => continue, + }; + + if !character.same_variant(&last_character.0) { + state.state_time = 0.0; + } + + let target_base = match ( + physics.on_ground, + vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving + physics.in_liquid.is_some(), // In water + ) { + // Idle + (true, false, false) => anim::biped_small::IdleAnimation::update_skeleton( + &BipedSmallSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + // Run + (true, true, _) => anim::biped_small::RunAnimation::update_skeleton( + &BipedSmallSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + // Jump + (false, _, false) => anim::biped_small::RunAnimation::update_skeleton( + &BipedSmallSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + // Swim + (false, _, true) => anim::biped_small::RunAnimation::update_skeleton( + &BipedSmallSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + _ => anim::biped_small::RunAnimation::update_skeleton( + &BipedSmallSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + }; + + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt_lerp); + state.update( + renderer, + pos.0, + ori, + scale, + col, + dt, + state_animation_rate, + model, + lpindex, + in_frustum, + is_player, + camera, + &mut update_buf, + terrain, + ); + }, Body::Dragon(body) => { let (model, skeleton_attr) = self.dragon_model_cache.get_or_create_model( renderer, @@ -3808,6 +3919,7 @@ impl FigureMgr { fish_medium_model_cache, fish_small_model_cache, biped_large_model_cache, + biped_small_model_cache, object_model_cache, golem_model_cache, states: @@ -3823,6 +3935,7 @@ impl FigureMgr { bird_small_states, fish_small_states, biped_large_states, + biped_small_states, golem_states, object_states, }, @@ -4016,6 +4129,23 @@ impl FigureMgr { ), ) }), + Body::BipedSmall(body) => biped_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + biped_small_model_cache.get_model( + col_lights, + *body, + inventory, + tick, + player_camera_mode, + character_state, + ), + ) + }), Body::Golem(body) => golem_states .get(&entity) .filter(|state| filter_state(&*state))