diff --git a/assets/common/npc_names.json b/assets/common/npc_names.json index a6ed9ad6a5..121b6da491 100644 --- a/assets/common/npc_names.json +++ b/assets/common/npc_names.json @@ -700,16 +700,30 @@ "fish_small": { "body": { "keyword": "fish_small", - "names": [] + "names": [ + "Dagon" + ] }, - "species": null + "species": { + "sardine": { + "keyword": "sardine", + "generic": "Sardine" + } + } }, "fish_medium": { "body": { "keyword": "fish_medium", - "names": [] + "names": [ + "Njord" + ] }, - "species": null + "species": { + "marlin": { + "keyword": "marlin", + "generic": "Marlin" + } + } }, "bird_small": { "body": { diff --git a/assets/voxygen/voxel/fish_medium_central_manifest.ron b/assets/voxygen/voxel/fish_medium_central_manifest.ron new file mode 100644 index 0000000000..bba6d4e700 --- /dev/null +++ b/assets/voxygen/voxel/fish_medium_central_manifest.ron @@ -0,0 +1,46 @@ +({ + (Marlin, Male): ( + head: ( + offset: (-2.5, 0.0, -3.0), + central: ("npc.marlin.male.head"), + ), + jaw: ( + offset: (-4.0, 0.0, -1.5), + central: ("armor.empty"), + ), + chest_front: ( + offset: (-2.5, -5.0, -2.5), + central: ("npc.marlin.male.chest_front"), + ), + chest_back: ( + offset: (-2.5, -7.0, -3.5), + central: ("npc.marlin.male.chest_back"), + ), + tail: ( + offset: (-0.5, -11.0, -4.5), + central: ("npc.marlin.male.tail"), + ), + ), + (Marlin, Female): ( + head: ( + offset: (-2.5, 0.0, -3.0), + central: ("npc.marlin.male.head"), + ), + jaw: ( + offset: (-4.0, 0.0, -1.5), + central: ("armor.empty"), + ), + chest_front: ( + offset: (-2.5, -5.0, -2.5), + central: ("npc.marlin.male.chest_front"), + ), + chest_back: ( + offset: (-2.5, -7.0, -3.5), + central: ("npc.marlin.male.chest_back"), + ), + tail: ( + offset: (-0.5, -11.0, -4.5), + central: ("npc.marlin.male.tail"), + ), + ), +}) diff --git a/assets/voxygen/voxel/fish_medium_lateral_manifest.ron b/assets/voxygen/voxel/fish_medium_lateral_manifest.ron new file mode 100644 index 0000000000..cd6184b305 --- /dev/null +++ b/assets/voxygen/voxel/fish_medium_lateral_manifest.ron @@ -0,0 +1,22 @@ +({ + (Marlin, Male): ( + fin_l: ( + offset: (-3.0, -5.0, -4.0), + lateral: ("npc.marlin.male.fin_l"), + ), + fin_r: ( + offset: (0.0, -5.0, -4.0), + lateral: ("npc.marlin.male.fin_r"), + ), + ), + (Marlin, Female): ( + fin_l: ( + offset: (-3.0, -5.0, -4.0), + lateral: ("npc.marlin.male.fin_l"), + ), + fin_r: ( + offset: (0.0, -5.0, -4.0), + lateral: ("npc.marlin.male.fin_r"), + ), + ), +}) diff --git a/assets/voxygen/voxel/fish_small_central_manifest.ron b/assets/voxygen/voxel/fish_small_central_manifest.ron new file mode 100644 index 0000000000..13abb5b285 --- /dev/null +++ b/assets/voxygen/voxel/fish_small_central_manifest.ron @@ -0,0 +1,30 @@ +({ + (Sardine, Male): ( + head: ( + offset: (-4.0, 0.0, -3.0), + central: ("npc.sardine.male.head"), + ), + chest: ( + offset: (-5.0, -7.5, -3.0), + central: ("npc.sardine.male.chest"), + ), + tail: ( + offset: (-1.0, -9.0, -1.5), + central: ("npc.sardine.male.tail"), + ), + ), + (Sardine, Female): ( + head: ( + offset: (-4.0, 0.0, -3.0), + central: ("npc.sardine.male.head"), + ), + chest: ( + offset: (-5.0, -7.5, -3.0), + central: ("npc.sardine.male.chest"), + ), + tail: ( + offset: (-1.0, -9.0, -1.5), + central: ("npc.sardine.male.tail"), + ), + ), +}) diff --git a/assets/voxygen/voxel/fish_small_lateral_manifest.ron b/assets/voxygen/voxel/fish_small_lateral_manifest.ron new file mode 100644 index 0000000000..c93e3579df --- /dev/null +++ b/assets/voxygen/voxel/fish_small_lateral_manifest.ron @@ -0,0 +1,22 @@ +({ + (Sardine, Male): ( + fin_l: ( + offset: (-1.5, -5.0, -3.0), + lateral: ("npc.sardine.male.fin_l"), + ), + fin_r: ( + offset: (-1.5, -5.0, -3.0), + lateral: ("npc.sardine.male.fin_r"), + ), + ), + (Sardine, Female): ( + fin_l: ( + offset: (-1.5, -5.0, -3.0), + lateral: ("npc.sardine.male.fin_l"), + ), + fin_r: ( + offset: (-1.5, -5.0, -3.0), + lateral: ("npc.sardine.male.fin_r"), + ), + ), +}) diff --git a/assets/voxygen/voxel/npc/marlin/fin_l.vox b/assets/voxygen/voxel/npc/marlin/fin_l.vox deleted file mode 100644 index 4b6f517af8..0000000000 --- a/assets/voxygen/voxel/npc/marlin/fin_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:80b0d57ec40c04bad6207cbd2443126cf731898df1f2586b26806c426c46ff5e -size 1152 diff --git a/assets/voxygen/voxel/npc/marlin/fin_r.vox b/assets/voxygen/voxel/npc/marlin/fin_r.vox deleted file mode 100644 index d208e485aa..0000000000 --- a/assets/voxygen/voxel/npc/marlin/fin_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aef2d194365db70e6a30551071f361863fad2f6c6a768f8c3368982363cdcdd6 -size 1152 diff --git a/assets/voxygen/voxel/npc/marlin/head.vox b/assets/voxygen/voxel/npc/marlin/head.vox deleted file mode 100644 index 45a31f5b69..0000000000 --- a/assets/voxygen/voxel/npc/marlin/head.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dcbe6707a2278ebd4828998b58d9fa69c2eba15b3b31f11de2e9104a9b59c1eb -size 1352 diff --git a/assets/voxygen/voxel/npc/marlin/male/chest_back.vox b/assets/voxygen/voxel/npc/marlin/male/chest_back.vox new file mode 100644 index 0000000000..9c177fc89c --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/chest_back.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53e81be715a3925dd6c4b8e2dac80e537ac0b71072090d29ff54488d600a7164 +size 1360 diff --git a/assets/voxygen/voxel/npc/marlin/male/chest_front.vox b/assets/voxygen/voxel/npc/marlin/male/chest_front.vox new file mode 100644 index 0000000000..7a254664bf --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/chest_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43f06662fee72dc5a1ac48c7ea17971efd1fefd8f90160991c7be625348e147f +size 1476 diff --git a/assets/voxygen/voxel/npc/marlin/male/fin_l.vox b/assets/voxygen/voxel/npc/marlin/male/fin_l.vox new file mode 100644 index 0000000000..b1f8e28fb6 --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/fin_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba8060df483358747e137dbb17bcc04145e65c2330e65c60c4fc9273cc34917d +size 1152 diff --git a/assets/voxygen/voxel/npc/marlin/male/fin_r.vox b/assets/voxygen/voxel/npc/marlin/male/fin_r.vox new file mode 100644 index 0000000000..35eda93dc6 --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/fin_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7311438a08fc6141342bfb594f2242bed811dd16a9dea670b693ee082cd9d25e +size 1152 diff --git a/assets/voxygen/voxel/npc/marlin/male/head.vox b/assets/voxygen/voxel/npc/marlin/male/head.vox new file mode 100644 index 0000000000..8dd567c293 --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46517efe195dcc1527d52a3e821750d88e989efb07541c98523a357f72fc8e4e +size 1352 diff --git a/assets/voxygen/voxel/npc/marlin/male/tail.vox b/assets/voxygen/voxel/npc/marlin/male/tail.vox new file mode 100644 index 0000000000..823d88ff58 --- /dev/null +++ b/assets/voxygen/voxel/npc/marlin/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90afdc11bfa775a3f05a21dd47bde734c9ecd14dccf6caf6d1c20dd9389e7303 +size 1308 diff --git a/assets/voxygen/voxel/npc/marlin/rear.vox b/assets/voxygen/voxel/npc/marlin/rear.vox deleted file mode 100644 index ab15e4fc13..0000000000 --- a/assets/voxygen/voxel/npc/marlin/rear.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dea03905c68b83f25f5196393ca8d06196b5a35eed0b12be61aa6bcfc8d5e081 -size 1360 diff --git a/assets/voxygen/voxel/npc/marlin/tail.vox b/assets/voxygen/voxel/npc/marlin/tail.vox deleted file mode 100644 index 30a1696564..0000000000 --- a/assets/voxygen/voxel/npc/marlin/tail.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1e55b9da2a45123b3109b763d741b550a11f274fc2563c4e54df17da389426c8 -size 1308 diff --git a/assets/voxygen/voxel/npc/marlin/torso.vox b/assets/voxygen/voxel/npc/marlin/torso.vox deleted file mode 100644 index 1c0219ac08..0000000000 --- a/assets/voxygen/voxel/npc/marlin/torso.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e0398753b0ff2719940aa1e2f20430892ebee6b7da26b61d8b16dad5d826655a -size 1476 diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 24dbdad9d6..90a25eb131 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -65,10 +65,10 @@ pub struct AllBodies { pub quadruped_small: BodyData>, pub quadruped_medium: BodyData>, pub bird_medium: BodyData>, - pub fish_medium: BodyData, + pub fish_medium: BodyData>, pub dragon: BodyData>, pub bird_small: BodyData, - pub fish_small: BodyData, + pub fish_small: BodyData>, pub biped_large: BodyData>, pub object: BodyData, pub golem: BodyData>, @@ -87,6 +87,8 @@ impl core::ops::Index for AllBodies &self.quadruped_small.body, NpcKind::Wolf => &self.quadruped_medium.body, NpcKind::Duck => &self.bird_medium.body, + NpcKind::Sardine => &self.fish_medium.body, + NpcKind::Marlin => &self.fish_small.body, NpcKind::Ogre => &self.biped_large.body, NpcKind::StoneGolem => &self.golem.body, NpcKind::Archaeos => &self.theropod.body, @@ -231,10 +233,10 @@ impl Body { bird_medium::Species::Cockatrice => 1.8, _ => 1.1, }, - Body::FishMedium(_) => 1.1, + Body::FishMedium(_) => 0.8, Body::Dragon(_) => 16.0, Body::BirdSmall(_) => 1.1, - Body::FishSmall(_) => 0.9, + Body::FishSmall(_) => 0.6, Body::BipedLarge(body) => match body.species { biped_large::Species::Slysaurok => 2.3, biped_large::Species::Occultsaurok => 2.8, diff --git a/common/src/comp/body/fish_medium.rs b/common/src/comp/body/fish_medium.rs index e1fe43cb74..8ddd3695b2 100644 --- a/common/src/comp/body/fish_medium.rs +++ b/common/src/comp/body/fish_medium.rs @@ -6,86 +6,73 @@ make_proj_elim!( body, #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Body { - pub head: Head, - pub torso: Torso, - pub rear: Rear, - pub tail: Tail, - pub fin_l: FinL, - pub fin_r: FinR, + pub species: Species, + pub body_type: BodyType, } ); impl Body { pub fn random() -> Self { let mut rng = thread_rng(); - Self { - head: *(&ALL_HEADS).choose(&mut rng).unwrap(), - torso: *(&ALL_TORSOS).choose(&mut rng).unwrap(), - rear: *(&ALL_REARS).choose(&mut rng).unwrap(), - tail: *(&ALL_TAILS).choose(&mut rng).unwrap(), - fin_l: *(&ALL_FIN_LS).choose(&mut rng).unwrap(), - fin_r: *(&ALL_FIN_RS).choose(&mut rng).unwrap(), + 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::FishMedium(body) } +} + +make_case_elim!( + species, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Species { + Marlin = 0, + } +); + +/// Data representing per-species generic data. +/// +/// NOTE: Deliberately don't (yet?) implement serialize. +#[derive(Clone, Debug, Deserialize)] +pub struct AllSpecies { + pub marlin: 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::Marlin => &self.marlin, } } } -make_case_elim!( - head, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum Head { - Default = 0, - } -); +pub const ALL_SPECIES: [Species; 1] = [Species::Marlin]; -const ALL_HEADS: [Head; 1] = [Head::Default]; +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!( - torso, + body_type, #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] - pub enum Torso { - Default = 0, + pub enum BodyType { + Female = 0, + Male = 1, } ); -const ALL_TORSOS: [Torso; 1] = [Torso::Default]; - -make_case_elim!( - rear, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum Rear { - Default = 0, - } -); -const ALL_REARS: [Rear; 1] = [Rear::Default]; - -make_case_elim!( - tail, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum Tail { - Default = 0, - } -); -const ALL_TAILS: [Tail; 1] = [Tail::Default]; - -make_case_elim!( - fin_l, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum FinL { - Default = 0, - } -); -const ALL_FIN_LS: [FinL; 1] = [FinL::Default]; - -make_case_elim!( - fin_r, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum FinR { - Default = 0, - } -); -const ALL_FIN_RS: [FinR; 1] = [FinR::Default]; +pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male]; diff --git a/common/src/comp/body/fish_small.rs b/common/src/comp/body/fish_small.rs index 31753df95d..b962d56ea4 100644 --- a/common/src/comp/body/fish_small.rs +++ b/common/src/comp/body/fish_small.rs @@ -6,39 +6,73 @@ make_proj_elim!( body, #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Body { - pub torso: Torso, - pub tail: Tail, + pub species: Species, + pub body_type: BodyType, } ); impl Body { pub fn random() -> Self { let mut rng = thread_rng(); - Self { - torso: *(&ALL_TORSOS).choose(&mut rng).unwrap(), - tail: *(&ALL_TAILS).choose(&mut rng).unwrap(), + 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::FishSmall(body) } +} + +make_case_elim!( + species, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Species { + Sardine = 0, + } +); + +/// Data representing per-species generic data. +/// +/// NOTE: Deliberately don't (yet?) implement serialize. +#[derive(Clone, Debug, Deserialize)] +pub struct AllSpecies { + pub sardine: 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::Sardine => &self.sardine, } } } -make_case_elim!( - torso, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - #[repr(u32)] - pub enum Torso { - Default = 0, - } -); +pub const ALL_SPECIES: [Species; 1] = [Species::Sardine]; -const ALL_TORSOS: [Torso; 1] = [Torso::Default]; +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!( - tail, + body_type, #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] - pub enum Tail { - Default = 0, + pub enum BodyType { + Female = 0, + Male = 1, } ); - -const ALL_TAILS: [Tail; 1] = [Tail::Default]; +pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male]; diff --git a/common/src/generation.rs b/common/src/generation.rs index 4e5dd28649..c16131918b 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -121,6 +121,8 @@ impl EntityInfo { Some(get_npc_name(&NPC_NAMES.quadruped_medium, body.species)) }, Body::BirdMedium(body) => Some(get_npc_name(&NPC_NAMES.bird_medium, body.species)), + Body::FishSmall(body) => Some(get_npc_name(&NPC_NAMES.fish_small, body.species)), + Body::FishMedium(body) => Some(get_npc_name(&NPC_NAMES.fish_medium, body.species)), Body::Theropod(body) => Some(get_npc_name(&NPC_NAMES.theropod, body.species)), Body::QuadrupedSmall(body) => { Some(get_npc_name(&NPC_NAMES.quadruped_small, body.species)) diff --git a/common/src/npc.rs b/common/src/npc.rs index 881aea55fa..ee997f4726 100644 --- a/common/src/npc.rs +++ b/common/src/npc.rs @@ -13,6 +13,8 @@ pub enum NpcKind { Wolf, Pig, Duck, + Sardine, + Marlin, Ogre, Archaeos, StoneGolem, @@ -20,11 +22,13 @@ pub enum NpcKind { Crocodile, } -pub const ALL_NPCS: [NpcKind; 9] = [ +pub const ALL_NPCS: [NpcKind; 11] = [ NpcKind::Humanoid, NpcKind::Wolf, NpcKind::Pig, NpcKind::Duck, + NpcKind::Sardine, + NpcKind::Marlin, NpcKind::Ogre, NpcKind::Archaeos, NpcKind::StoneGolem, @@ -93,6 +97,8 @@ pub fn kind_to_body(kind: NpcKind) -> Body { NpcKind::Pig => comp::quadruped_small::Body::random().into(), NpcKind::Wolf => comp::quadruped_medium::Body::random().into(), NpcKind::Duck => comp::bird_medium::Body::random().into(), + NpcKind::Sardine => comp::fish_small::Body::random().into(), + NpcKind::Marlin => comp::fish_medium::Body::random().into(), NpcKind::Ogre => comp::biped_large::Body::random().into(), NpcKind::Archaeos => comp::theropod::Body::random().into(), NpcKind::StoneGolem => comp::golem::Body::random().into(), @@ -195,6 +201,22 @@ impl NpcBody { comp::bird_medium::Body::random_with, ) }) + .or_else(|| { + parse( + s, + NpcKind::Sardine, + &npc_names.fish_small, + comp::fish_small::Body::random_with, + ) + }) + .or_else(|| { + parse( + s, + NpcKind::Marlin, + &npc_names.fish_medium, + comp::fish_medium::Body::random_with, + ) + }) .or_else(|| { parse( s, diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index a57cce9e00..09abdbfe44 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -58,10 +58,10 @@ impl Body { quadruped_medium::Species::Horse => 180.0, }, Body::BirdMedium(_) => 80.0, - Body::FishMedium(_) => 50.0, + Body::FishMedium(_) => 80.0, Body::Dragon(_) => 250.0, Body::BirdSmall(_) => 75.0, - Body::FishSmall(_) => 40.0, + Body::FishSmall(_) => 60.0, Body::BipedLarge(_) => 75.0, Body::Object(_) => 40.0, Body::Golem(_) => 60.0, diff --git a/voxygen/anim/src/fish_medium/idle.rs b/voxygen/anim/src/fish_medium/idle.rs index 09b0c820e6..3c8497f5e7 100644 --- a/voxygen/anim/src/fish_medium/idle.rs +++ b/voxygen/anim/src/fish_medium/idle.rs @@ -1,6 +1,7 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; use super::super::vek::*; +use std::f32::consts::PI; pub struct IdleAnimation; @@ -15,36 +16,38 @@ impl Animation for IdleAnimation { fn update_skeleton_inner( skeleton: &Self::Skeleton, _global_time: Self::Dependency, - _anim_time: f64, + anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.scale = Vec3::one() / 10.88; - next.torso.scale = Vec3::one() * 1.01; - next.rear.scale = Vec3::one() * 0.98; - next.tail.scale = Vec3::one() / 11.0; - next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.scale = Vec3::one() / 10.5; + let slower = (anim_time as f32 * 1.0 + PI).sin(); + let slow = (anim_time as f32 * 3.5 + PI).sin(); + let slowalt = (anim_time as f32 * 3.5 + PI + 0.2).sin(); - next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.chest_front.scale = Vec3::one() / 11.0; - next.torso.position = Vec3::new(0.0, 4.5, 2.0); - next.torso.orientation = Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(slowalt * -0.1); - next.rear.position = Vec3::new(0.0, 3.1, -4.5); - next.rear.orientation = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.chest_front.position = Vec3::new(0.0, s_a.chest_front.0, s_a.chest_front.1) / 11.0; + next.chest_front.orientation = Quaternion::rotation_x(0.0); - next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.orientation = Quaternion::rotation_y(0.0); + next.chest_back.position = Vec3::new(0.0, s_a.chest_back.0, s_a.chest_back.1); + next.chest_back.orientation = Quaternion::rotation_z(slowalt * 0.1); - next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.orientation = Quaternion::rotation_y(0.0); + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.1); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_z(slow * 0.1 - 0.1); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_z(-slow * 0.1 + 0.1); next } } diff --git a/voxygen/anim/src/fish_medium/jump.rs b/voxygen/anim/src/fish_medium/jump.rs index 7b54e4f476..c7b4162e2d 100644 --- a/voxygen/anim/src/fish_medium/jump.rs +++ b/voxygen/anim/src/fish_medium/jump.rs @@ -17,33 +17,31 @@ impl Animation for JumpAnimation { _global_time: Self::Dependency, _anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.scale = Vec3::one() / 10.88; - next.torso.scale = Vec3::one() * 1.01; - next.rear.scale = Vec3::one() * 0.98; - next.tail.scale = Vec3::one() / 11.0; - next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.scale = Vec3::one() / 10.5; + next.chest_front.scale = Vec3::one() / 11.0; - next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.torso.position = Vec3::new(0.0, 4.5, 2.0); - next.torso.orientation = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.rear.position = Vec3::new(0.0, 3.1, -4.5); - next.rear.orientation = Quaternion::rotation_z(0.0); + next.chest_front.position = Vec3::new(0.0, s_a.chest_front.0, s_a.chest_front.1) / 11.0; + next.chest_front.orientation = Quaternion::rotation_x(0.0); - next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.chest_back.position = Vec3::new(0.0, s_a.chest_back.0, s_a.chest_back.1); + next.chest_back.orientation = Quaternion::rotation_z(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); next.fin_l.orientation = Quaternion::rotation_y(0.0); - next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); next.fin_r.orientation = Quaternion::rotation_y(0.0); next } diff --git a/voxygen/anim/src/fish_medium/mod.rs b/voxygen/anim/src/fish_medium/mod.rs index 63b1b7e6b9..4448b4edad 100644 --- a/voxygen/anim/src/fish_medium/mod.rs +++ b/voxygen/anim/src/fish_medium/mod.rs @@ -1,9 +1,14 @@ pub mod idle; pub mod jump; pub mod run; +pub mod swim; +pub mod swimidle; // Reexports -pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; +pub use self::{ + idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, swim::SwimAnimation, + swimidle::SwimIdleAnimation, +}; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; use common::comp::{self}; @@ -13,8 +18,9 @@ pub type Body = comp::fish_medium::Body; skeleton_impls!(struct FishMediumSkeleton { + head, - + torso, - + rear, + + jaw, + + chest_front, + + chest_back, + tail, + fin_l, + fin_r, @@ -24,7 +30,7 @@ impl Skeleton for FishMediumSkeleton { type Attr = SkeletonAttr; type Body = Body; - const BONE_COUNT: usize = 6; + const BONE_COUNT: usize = 7; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_medium_compute_mats\0"; @@ -34,22 +40,31 @@ impl Skeleton for FishMediumSkeleton { base_mat: Mat4, buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], ) -> Vec3 { - let torso_mat = base_mat * Mat4::::from(self.torso); - let rear_mat = torso_mat * Mat4::::from(self.rear); + let chest_front_mat = base_mat * Mat4::::from(self.chest_front); + let chest_back_mat = Mat4::::from(self.chest_back); + let head_mat = Mat4::::from(self.head); *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ - make_bone(torso_mat * Mat4::::from(self.head)), - make_bone(torso_mat), - make_bone(rear_mat), - make_bone(rear_mat * Mat4::::from(self.tail)), - make_bone(rear_mat * Mat4::::from(self.fin_l)), - make_bone(rear_mat * Mat4::::from(self.fin_r)), + make_bone(chest_front_mat * head_mat), + make_bone(chest_front_mat * head_mat * Mat4::::from(self.jaw)), + make_bone(chest_front_mat), + make_bone(chest_front_mat * chest_back_mat), + make_bone(chest_front_mat * chest_back_mat * Mat4::::from(self.tail)), + make_bone(chest_front_mat * Mat4::::from(self.fin_l)), + make_bone(chest_front_mat * Mat4::::from(self.fin_r)), ]; Vec3::default() } } -pub struct SkeletonAttr; +pub struct SkeletonAttr { + head: (f32, f32), + jaw: (f32, f32), + chest_front: (f32, f32), + chest_back: (f32, f32), + tail: (f32, f32), + fin: (f32, f32, f32), +} impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { type Error = (); @@ -63,9 +78,40 @@ impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { } impl Default for SkeletonAttr { - fn default() -> Self { Self } + fn default() -> Self { + Self { + head: (0.0, 0.0), + jaw: (0.0, 0.0), + chest_front: (0.0, 0.0), + chest_back: (0.0, 0.0), + tail: (0.0, 0.0), + fin: (0.0, 0.0, 0.0), + } + } } impl<'a> From<&'a Body> for SkeletonAttr { - fn from(_body: &'a Body) -> Self { Self } + fn from(body: &'a Body) -> Self { + use comp::fish_medium::Species::*; + Self { + head: match (body.species, body.body_type) { + (Marlin, _) => (2.0, 1.5), + }, + jaw: match (body.species, body.body_type) { + (Marlin, _) => (2.5, -3.0), + }, + chest_front: match (body.species, body.body_type) { + (Marlin, _) => (0.0, 2.5), + }, + chest_back: match (body.species, body.body_type) { + (Marlin, _) => (-1.0, 1.0), + }, + tail: match (body.species, body.body_type) { + (Marlin, _) => (-7.0, 0.0), + }, + fin: match (body.species, body.body_type) { + (Marlin, _) => (2.5, 1.0, 3.5), + }, + } + } } diff --git a/voxygen/anim/src/fish_medium/run.rs b/voxygen/anim/src/fish_medium/run.rs index ec875b0441..81ed079734 100644 --- a/voxygen/anim/src/fish_medium/run.rs +++ b/voxygen/anim/src/fish_medium/run.rs @@ -17,33 +17,31 @@ impl Animation for RunAnimation { _global_time: Self::Dependency, _anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.scale = Vec3::one() / 10.88; - next.torso.scale = Vec3::one() * 1.01; - next.rear.scale = Vec3::one() * 0.98; - next.tail.scale = Vec3::one() / 11.0; - next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.scale = Vec3::one() / 10.5; + next.chest_front.scale = Vec3::one() / 11.0; - next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.torso.position = Vec3::new(0.0, 4.5, 2.0); - next.torso.orientation = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.rear.position = Vec3::new(0.0, 3.1, -4.5); - next.rear.orientation = Quaternion::rotation_z(0.0); + next.chest_front.position = Vec3::new(0.0, s_a.chest_front.0, s_a.chest_front.1) / 11.0; + next.chest_front.orientation = Quaternion::rotation_x(0.0); - next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.chest_back.position = Vec3::new(0.0, s_a.chest_back.0, s_a.chest_back.1); + next.chest_back.orientation = Quaternion::rotation_z(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); next.fin_l.orientation = Quaternion::rotation_y(0.0); - next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); next.fin_r.orientation = Quaternion::rotation_y(0.0); next } diff --git a/voxygen/anim/src/fish_small/idle.rs b/voxygen/anim/src/fish_small/idle.rs index 841627ec41..42a0fd663e 100644 --- a/voxygen/anim/src/fish_small/idle.rs +++ b/voxygen/anim/src/fish_small/idle.rs @@ -17,18 +17,26 @@ impl Animation for IdleAnimation { _global_time: Self::Dependency, _anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.torso.scale = Vec3::one() / 10.88; + next.head.scale = Vec3::one() / 11.0; - next.tail.position = Vec3::new(0.0, 4.5, 2.0); - next.tail.orientation = Quaternion::rotation_x(0.0); - next.tail.scale = Vec3::one() * 1.01; + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0; + next.chest.orientation = Quaternion::rotation_x(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_y(0.0); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_y(0.0); next } } diff --git a/voxygen/anim/src/fish_small/jump.rs b/voxygen/anim/src/fish_small/jump.rs index 5af66c2869..b02202ee0f 100644 --- a/voxygen/anim/src/fish_small/jump.rs +++ b/voxygen/anim/src/fish_small/jump.rs @@ -17,18 +17,26 @@ impl Animation for JumpAnimation { _global_time: Self::Dependency, _anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.torso.scale = Vec3::one() / 10.88; + next.head.scale = Vec3::one() / 11.0; - next.tail.position = Vec3::new(0.0, 4.5, 2.0); - next.tail.orientation = Quaternion::rotation_x(0.0); - next.tail.scale = Vec3::one() * 1.01; + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0; + next.chest.orientation = Quaternion::rotation_x(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_y(0.0); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_y(0.0); next } } diff --git a/voxygen/anim/src/fish_small/mod.rs b/voxygen/anim/src/fish_small/mod.rs index f055901750..a18c569e2f 100644 --- a/voxygen/anim/src/fish_small/mod.rs +++ b/voxygen/anim/src/fish_small/mod.rs @@ -1,9 +1,10 @@ pub mod idle; pub mod jump; pub mod run; +pub mod swim; // Reexports -pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; +pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, swim::SwimAnimation}; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; use common::comp::{self}; @@ -12,36 +13,46 @@ use core::convert::TryFrom; pub type Body = comp::fish_small::Body; skeleton_impls!(struct FishSmallSkeleton { - + torso, + + head, + + chest, + tail, + + fin_l, + + fin_r, }); impl Skeleton for FishSmallSkeleton { type Attr = SkeletonAttr; type Body = Body; - const BONE_COUNT: usize = 2; + const BONE_COUNT: usize = 5; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_small_compute_mats\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_small_compute_mats")] - fn compute_matrices_inner( &self, base_mat: Mat4, buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], ) -> Vec3 { - let torso_mat = base_mat * Mat4::::from(self.torso); + let chest_mat = base_mat * Mat4::::from(self.chest); *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ - make_bone(torso_mat), - make_bone(torso_mat * Mat4::::from(self.tail)), + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.tail)), + make_bone(chest_mat * Mat4::::from(self.fin_l)), + make_bone(chest_mat * Mat4::::from(self.fin_r)), ]; Vec3::default() } } -pub struct SkeletonAttr; +pub struct SkeletonAttr { + head: (f32, f32), + chest: (f32, f32), + tail: (f32, f32), + fin: (f32, f32, f32), +} impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { type Error = (); @@ -55,9 +66,32 @@ impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { } impl Default for SkeletonAttr { - fn default() -> Self { Self } + fn default() -> Self { + Self { + head: (0.0, 0.0), + chest: (0.0, 0.0), + tail: (0.0, 0.0), + fin: (0.0, 0.0, 0.0), + } + } } impl<'a> From<&'a Body> for SkeletonAttr { - fn from(_body: &'a Body) -> Self { Self } + fn from(body: &'a Body) -> Self { + use comp::fish_small::Species::*; + Self { + head: match (body.species, body.body_type) { + (Sardine, _) => (1.5, 2.0), + }, + chest: match (body.species, body.body_type) { + (Sardine, _) => (0.0, 5.0), + }, + tail: match (body.species, body.body_type) { + (Sardine, _) => (-6.0, 0.0), + }, + fin: match (body.species, body.body_type) { + (Sardine, _) => (3.5, 6.0, -1.0), + }, + } + } } diff --git a/voxygen/anim/src/fish_small/run.rs b/voxygen/anim/src/fish_small/run.rs index f19d685e9e..c3fa97be8e 100644 --- a/voxygen/anim/src/fish_small/run.rs +++ b/voxygen/anim/src/fish_small/run.rs @@ -17,18 +17,26 @@ impl Animation for RunAnimation { (_velocity, _global_time): Self::Dependency, _anim_time: f64, _rate: &mut f32, - _skeleton_attr: &SkeletonAttr, + s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); - next.torso.scale = Vec3::one() / 10.88; + next.head.scale = Vec3::one() / 11.0; - next.tail.position = Vec3::new(0.0, 4.5, 2.0); - next.tail.orientation = Quaternion::rotation_x(0.0); - next.tail.scale = Vec3::one() * 1.01; + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0; + next.chest.orientation = Quaternion::rotation_x(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_y(0.0); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_y(0.0); next } } diff --git a/voxygen/src/anim/src/fish_medium/swim.rs b/voxygen/src/anim/src/fish_medium/swim.rs new file mode 100644 index 0000000000..dc4ebf2c55 --- /dev/null +++ b/voxygen/src/anim/src/fish_medium/swim.rs @@ -0,0 +1,78 @@ +use super::{ + super::{vek::*, Animation}, + FishMediumSkeleton, SkeletonAttr, +}; +use std::f32::consts::PI; + +pub struct SwimAnimation; + +type SwimAnimationDependency = (Vec3, Vec3, Vec3, f64, Vec3); + +impl Animation for SwimAnimation { + type Dependency = SwimAnimationDependency; + type Skeleton = FishMediumSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"fish_medium_swim\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_medium_swim")] + + 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(); + + let slower = (anim_time as f32 * 1.0 + PI).sin(); + let slow = (anim_time as f32 * 3.5 + PI).sin(); + let slowalt = (anim_time as f32 * 3.5 + PI + 0.2).sin(); + let fast = (anim_time as f32 * 5.5 + PI).sin(); + let fastalt = (anim_time as f32 * 5.5 + PI + 0.2).sin(); + + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.8) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; + let abstilt = tilt.abs(); + let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); + + let squash = if abstilt > 0.2 { 0.35 } else { 1.0 }; //condenses the body at strong turns + + next.chest_front.scale = Vec3::one() / 11.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(slowalt * -0.1 + tilt * -2.0); + + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.chest_front.position = Vec3::new(0.0, s_a.chest_front.0, s_a.chest_front.1) / 11.0; + next.chest_front.orientation = + Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0 + x_tilt); + + next.chest_back.position = Vec3::new(0.0, s_a.chest_back.0, s_a.chest_back.1); + next.chest_back.orientation = Quaternion::rotation_z(fastalt * 0.3 + tilt * 2.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(fast * 0.3 + tilt * 2.0); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_z(fast * 0.3 - 0.1 + tilt * -0.5); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_z(-fast * 0.3 + 0.1 + tilt * -0.5); + next + } +} diff --git a/voxygen/src/anim/src/fish_medium/swimidle.rs b/voxygen/src/anim/src/fish_medium/swimidle.rs new file mode 100644 index 0000000000..3abd898a32 --- /dev/null +++ b/voxygen/src/anim/src/fish_medium/swimidle.rs @@ -0,0 +1,57 @@ +use super::{ + super::{vek::*, Animation}, + FishMediumSkeleton, SkeletonAttr, +}; +use std::{f32::consts::PI, ops::Mul}; + +pub struct SwimIdleAnimation; + +type SwimIdleAnimationDependency = (Vec3, Vec3, Vec3, f64, Vec3); + +impl Animation for SwimIdleAnimation { + type Dependency = SwimIdleAnimationDependency; + type Skeleton = FishMediumSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"fish_medium_swimidle\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_medium_swimidle")] + + 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(); + + let slower = (anim_time as f32 * 1.0 + PI).sin(); + let slow = (anim_time as f32 * 3.5 + PI).sin(); + let slowalt = (anim_time as f32 * 3.5 + PI + 0.2).sin(); + + next.chest_front.scale = Vec3::one() / 11.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(slowalt * -0.1); + + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.chest_front.position = Vec3::new(0.0, s_a.chest_front.0, s_a.chest_front.1) / 11.0; + next.chest_front.orientation = Quaternion::rotation_x(0.0); + + next.chest_back.position = Vec3::new(0.0, s_a.chest_back.0, s_a.chest_back.1); + next.chest_back.orientation = Quaternion::rotation_z(slowalt * 0.1); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.1); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_z(slow * 0.1 - 0.1); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_z(-slow * 0.1 + 0.1); + next + } +} diff --git a/voxygen/src/anim/src/fish_small/swim.rs b/voxygen/src/anim/src/fish_small/swim.rs new file mode 100644 index 0000000000..774dc43316 --- /dev/null +++ b/voxygen/src/anim/src/fish_small/swim.rs @@ -0,0 +1,46 @@ +use super::{ + super::{vek::*, Animation}, + FishSmallSkeleton, SkeletonAttr, +}; + +pub struct SwimAnimation; + +type SwimAnimationDependency = (Vec3, Vec3, Vec3, f64, Vec3); + +impl Animation for SwimAnimation { + type Dependency = SwimAnimationDependency; + type Skeleton = FishSmallSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"fish_small_swim\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_small_swim")] + + 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() / 11.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0; + next.chest.orientation = Quaternion::rotation_x(0.0); + + next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + + next.fin_l.position = Vec3::new(-s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_l.orientation = Quaternion::rotation_y(0.0); + + next.fin_r.position = Vec3::new(s_a.fin.0, s_a.fin.1, s_a.fin.2); + next.fin_r.orientation = Quaternion::rotation_y(0.0); + next + } +} diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 9ca1f4c3fa..12ef0b1ea9 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -6,7 +6,8 @@ use common::{ bird_medium::{self, BodyType as BMBodyType, Species as BMSpecies}, bird_small, dragon::{self, BodyType as DBodyType, Species as DSpecies}, - fish_medium, fish_small, + fish_medium::{self, BodyType as FMBodyType, Species as FMSpecies}, + fish_small::{self, BodyType as FSBodyType, Species as FSSpecies}, golem::{self, BodyType as GBodyType, Species as GSpecies}, humanoid::{self, Body, BodyType, EyeColor, Skin, Species}, object, @@ -2086,17 +2087,257 @@ impl TheropodLateralSpec { } } //// +#[derive(Deserialize)] +struct FishMediumCentralSpec(HashMap<(FMSpecies, FMBodyType), SidedFMCentralVoxSpec>); + +#[derive(Deserialize)] +struct SidedFMCentralVoxSpec { + head: FishMediumCentralSubSpec, + jaw: FishMediumCentralSubSpec, + chest_front: FishMediumCentralSubSpec, + chest_back: FishMediumCentralSubSpec, + tail: FishMediumCentralSubSpec, +} +#[derive(Deserialize)] +struct FishMediumCentralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + central: VoxSimple, +} +#[derive(Deserialize)] +struct FishMediumLateralSpec(HashMap<(FMSpecies, FMBodyType), SidedFMLateralVoxSpec>); +#[derive(Deserialize)] +struct SidedFMLateralVoxSpec { + fin_l: FishMediumLateralSubSpec, + fin_r: FishMediumLateralSubSpec, +} +#[derive(Deserialize)] +struct FishMediumLateralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + lateral: VoxSimple, +} + make_vox_spec!( fish_medium::Body, - struct FishMediumSpec {}, - |FigureKey { body, .. }, _spec| { + struct FishMediumSpec { + central: FishMediumCentralSpec = "voxygen.voxel.fish_medium_central_manifest", + lateral: FishMediumLateralSpec = "voxygen.voxel.fish_medium_lateral_manifest", + }, + |FigureKey { body, .. }, spec| { [ - Some(mesh_fish_medium_head(body.head)), - Some(mesh_fish_medium_torso(body.torso)), - Some(mesh_fish_medium_rear(body.rear)), - Some(mesh_fish_medium_tail(body.tail)), - Some(mesh_fish_medium_fin_l(body.fin_l)), - Some(mesh_fish_medium_fin_r(body.fin_r)), + Some(spec.central.asset.mesh_head( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_jaw( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_chest_front( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_chest_back( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_tail( + body.species, + body.body_type, + )), + Some(spec.lateral.asset.mesh_fin_l( + body.species, + body.body_type, + )), + Some(spec.lateral.asset.mesh_fin_r( + body.species, + body.body_type, + )), + None, + None, + None, + None, + None, + None, + None, + None, + None, + ] + }, +); + +impl FishMediumCentralSpec { + fn mesh_head(&self, species: FMSpecies, body_type: FMBodyType) -> 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_jaw(&self, species: FMSpecies, body_type: FMBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No jaw 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.jaw.central.0); + + (central, Vec3::from(spec.jaw.offset)) + } + + fn mesh_chest_front(&self, species: FMSpecies, body_type: FMBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No front 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_front.central.0); + + (central, Vec3::from(spec.chest_front.offset)) + } + + fn mesh_chest_back(&self, species: FMSpecies, body_type: FMBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No back 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_back.central.0); + + (central, Vec3::from(spec.chest_back.offset)) + } + + fn mesh_tail(&self, species: FMSpecies, body_type: FMBodyType) -> 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)) + } +} + +impl FishMediumLateralSpec { + fn mesh_fin_l(&self, species: FMSpecies, body_type: FMBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No fin 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.fin_l.lateral.0); + + (lateral, Vec3::from(spec.fin_l.offset)) + } + + fn mesh_fin_r(&self, species: FMSpecies, body_type: FMBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No fin 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.fin_r.lateral.0); + + (lateral, Vec3::from(spec.fin_r.offset)) + } +} + +//// +#[derive(Deserialize)] +struct FishSmallCentralSpec(HashMap<(FSSpecies, FSBodyType), SidedFSCentralVoxSpec>); + +#[derive(Deserialize)] +struct SidedFSCentralVoxSpec { + head: FishSmallCentralSubSpec, + chest: FishSmallCentralSubSpec, + tail: FishSmallCentralSubSpec, +} +#[derive(Deserialize)] +struct FishSmallCentralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + central: VoxSimple, +} +#[derive(Deserialize)] +struct FishSmallLateralSpec(HashMap<(FSSpecies, FSBodyType), SidedFSLateralVoxSpec>); +#[derive(Deserialize)] +struct SidedFSLateralVoxSpec { + fin_l: FishSmallLateralSubSpec, + fin_r: FishSmallLateralSubSpec, +} +#[derive(Deserialize)] +struct FishSmallLateralSubSpec { + offset: [f32; 3], // Should be relative to initial origin + lateral: VoxSimple, +} + +make_vox_spec!( + fish_small::Body, + struct FishSmallSpec { + central: FishSmallCentralSpec = "voxygen.voxel.fish_small_central_manifest", + lateral: FishSmallLateralSpec = "voxygen.voxel.fish_small_lateral_manifest", + }, + |FigureKey { body, .. }, spec| { + [ + Some(spec.central.asset.mesh_head( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_chest( + body.species, + body.body_type, + )), + Some(spec.central.asset.mesh_tail( + body.species, + body.body_type, + )), + Some(spec.lateral.asset.mesh_fin_l( + body.species, + body.body_type, + )), + Some(spec.lateral.asset.mesh_fin_r( + body.species, + body.body_type, + )), + None, None, None, None, @@ -2111,58 +2352,88 @@ make_vox_spec!( }, ); -fn mesh_fish_medium_head(head: fish_medium::Head) -> BoneMeshes { - load_mesh( - match head { - fish_medium::Head::Default => "npc.marlin.head", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) +impl FishSmallCentralSpec { + fn mesh_head(&self, species: FSSpecies, body_type: FSBodyType) -> 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: FSSpecies, body_type: FSBodyType) -> 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_tail(&self, species: FSSpecies, body_type: FSBodyType) -> 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_fish_medium_torso(torso: fish_medium::Torso) -> BoneMeshes { - load_mesh( - match torso { - fish_medium::Torso::Default => "npc.marlin.torso", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} +impl FishSmallLateralSpec { + fn mesh_fin_l(&self, species: FSSpecies, body_type: FSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No fin 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.fin_l.lateral.0); -fn mesh_fish_medium_rear(rear: fish_medium::Rear) -> BoneMeshes { - load_mesh( - match rear { - fish_medium::Rear::Default => "npc.marlin.rear", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} + (lateral, Vec3::from(spec.fin_l.offset)) + } -fn mesh_fish_medium_tail(tail: fish_medium::Tail) -> BoneMeshes { - load_mesh( - match tail { - fish_medium::Tail::Default => "npc.marlin.tail", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} + fn mesh_fin_r(&self, species: FSSpecies, body_type: FSBodyType) -> BoneMeshes { + let spec = match self.0.get(&(species, body_type)) { + Some(spec) => spec, + None => { + error!( + "No fin 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.fin_r.lateral.0); -fn mesh_fish_medium_fin_l(fin_l: fish_medium::FinL) -> BoneMeshes { - load_mesh( - match fin_l { - fish_medium::FinL::Default => "npc.marlin.fin_l", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} - -fn mesh_fish_medium_fin_r(fin_r: fish_medium::FinR) -> BoneMeshes { - load_mesh( - match fin_r { - fish_medium::FinR::Default => "npc.marlin.fin_r", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) + (lateral, Vec3::from(spec.fin_r.offset)) + } } //// @@ -2583,49 +2854,7 @@ fn mesh_bird_small_wing_r(wing_r: bird_small::WingR) -> BoneMeshes { Vec3::new(-7.0, -6.0, -6.0), ) } -//// -make_vox_spec!( - fish_small::Body, - struct FishSmallSpec {}, - |FigureKey { body, .. }, _spec| { - [ - Some(mesh_fish_small_torso(body.torso)), - Some(mesh_fish_small_tail(body.tail)), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ] - }, -); -fn mesh_fish_small_torso(torso: fish_small::Torso) -> BoneMeshes { - load_mesh( - match torso { - fish_small::Torso::Default => "npc.cardinalfish.torso", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} - -fn mesh_fish_small_tail(tail: fish_small::Tail) -> BoneMeshes { - load_mesh( - match tail { - fish_small::Tail::Default => "npc.cardinalfish.tail", - }, - Vec3::new(-7.0, -6.0, -6.0), - ) -} //// #[derive(Deserialize)] struct BipedLargeCentralSpec(HashMap<(BLSpecies, BLBodyType), SidedBLCentralVoxSpec>); diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index cea487d962..cd58b7d409 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -2203,11 +2203,18 @@ impl FigureMgr { &mut state_animation_rate, skeleton_attr, ), - - // TODO! - _ => anim::fish_medium::IdleAnimation::update_skeleton( + // Idle Water + (_, false, true) => anim::fish_medium::SwimIdleAnimation::update_skeleton( &FishMediumSkeleton::default(), - time, + (vel.0, ori, state.last_ori, time, state.avg_vel), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), + // Swim + (_, true, true) => anim::fish_medium::SwimAnimation::update_skeleton( + &FishMediumSkeleton::default(), + (vel.0, ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, skeleton_attr, @@ -2598,11 +2605,10 @@ impl FigureMgr { &mut state_animation_rate, skeleton_attr, ), - - // TODO! - _ => anim::fish_small::IdleAnimation::update_skeleton( + // Swim + (_, _, true) => anim::fish_small::SwimAnimation::update_skeleton( &FishSmallSkeleton::default(), - time, + (vel.0, ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, skeleton_attr, diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index fac6efe6b0..54c7d3970b 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -1,8 +1,8 @@ use crate::{column::ColumnSample, sim::SimChunk, IndexRef, CONFIG}; use common::{ comp::{ - biped_large, bird_medium, quadruped_low, quadruped_medium, quadruped_small, theropod, - Alignment, + biped_large, bird_medium, fish_medium, quadruped_low, quadruped_medium, quadruped_small, + theropod, Alignment, }, generation::{ChunkSupplement, EntityInfo}, terrain::Block, @@ -761,6 +761,21 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 5.0 }, }, + // Underwater + Entry { + make_entity: |pos, rng| { + EntityInfo::at(pos) + .with_body( + fish_medium::Body::random_with(rng, &fish_medium::Species::Marlin).into(), + ) + .with_alignment(Alignment::Wild) + }, + group_size: 3..5, + is_underwater: true, + get_density: |c, col| { + close(c.temp, CONFIG.temperate_temp, 1.0) * col.tree_density * BASE_DENSITY * 5.0 + }, + }, ]; for y in 0..vol.size_xy().y as i32 {