Add quadruped_low skeleton

This commit is contained in:
Snowram 2020-06-02 01:56:50 +02:00 committed by jshipsey
parent 79f5d3189c
commit 6b6e9d5c8d
36 changed files with 1327 additions and 2 deletions

@ -561,5 +561,19 @@
"generic": "Reddragon"
}
}
},
"quadruped_low": {
"body": {
"keyword": "quadruped_low",
"names": [
"Sobek"
]
},
"species": {
"crocodile": {
"keyword": "crocodile",
"generic": "Crocodile"
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,62 @@
({
(Crocodile, Male): (
upper: (
offset: (-7.0, -9.0, -5.5),
central: ("npc.crocodile.male.head_upper"),
),
lower: (
offset: (-7.0, -4.5, -5.0),
central: ("npc.crocodile.male.head_lower"),
),
jaw: (
offset: (-3.0, -3.0, -2.5),
central: ("npc.crocodile.male.jaw"),
),
chest_rear: (
offset: (-8.0, -5.5, -6.0),
central: ("npc.crocodile.male.chest_front"),
),
chest_front: (
offset: (-8.0, -5.5, -6.0),
central: ("npc.crocodile.male.chest_front"),
),
tail_rear: (
offset: (-2.0, -9.5, -5.0),
central: ("npc.crocodile.male.tail"),
),
tail_front: (
offset: (-2.0, -9.5, -5.0),
central: ("npc.crocodile.male.tail"),
),
),
(Crocodile, Female): (
upper: (
offset: (-7.0, -9.0, -5.5),
central: ("npc.crocodile.female.head_upper"),
),
lower: (
offset: (-7.0, -4.5, -5.0),
central: ("npc.crocodile.female.head_lower"),
),
jaw: (
offset: (-3.0, -3.0, -2.5),
central: ("npc.crocodile.female.jaw"),
),
chest_rear: (
offset: (-8.0, -5.5, -6.0),
central: ("npc.crocodile.female.chest_rear"),
),
chest_front: (
offset: (-8.0, -5.5, -6.0),
central: ("npc.crocodile.female.chest_front"),
),
tail_rear: (
offset: (-2.0, -9.5, -5.0),
central: ("npc.crocodile.female.tail"),
),
tail_front: (
offset: (-2.0, -9.5, -5.0),
central: ("npc.crocodile.female.tail"),
),
),
})

@ -0,0 +1,38 @@
({
(Crocodile, Male): (
front_left: (
offset: (-2.5, -4.0, -1.5),
lateral: ("npc.crocodile.male.foot_fl"),
),
front_right: (
offset: (-2.5, -4.0, -1.5),
lateral: ("npc.crocodile.male.foot_fr"),
),
back_left: (
offset: (-2.5, -4.0, -3.0),
lateral: ("npc.crocodile.male.foot_bl"),
),
back_right: (
offset: (-2.5, -4.0, -3.0),
lateral: ("npc.crocodile.male.foot_br"),
),
),
(Crocodile, Female): (
front_left: (
offset: (-2.5, -4.0, -1.5),
lateral: ("npc.crocodile.female.foot_fl"),
),
front_right: (
offset: (-2.5, -4.0, -1.5),
lateral: ("npc.crocodile.female.foot_fr"),
),
back_left: (
offset: (-2.5, -4.0, -3.0),
lateral: ("npc.crocodile.female.foot_bl"),
),
back_right: (
offset: (-2.5, -4.0, -3.0),
lateral: ("npc.crocodile.female.foot_br"),
),
),
})

@ -10,6 +10,7 @@ pub mod humanoid;
pub mod object;
pub mod quadruped_medium;
pub mod quadruped_small;
pub mod quadruped_low;
use crate::{
assets::{self, Asset},
@ -34,6 +35,7 @@ pub enum Body {
Object(object::Body) = 9,
Golem(golem::Body) = 10,
Critter(critter::Body) = 11,
QuadrupedLow(quadruped_low::Body) = 12,
}
/// Data representing data generic to the body together with per-species data.
@ -61,6 +63,7 @@ pub struct AllBodies<BodyMeta, SpeciesMeta> {
pub golem: BodyData<BodyMeta, golem::AllSpecies<SpeciesMeta>>,
pub critter: BodyData<BodyMeta, critter::AllSpecies<SpeciesMeta>>,
pub dragon: BodyData<BodyMeta, dragon::AllSpecies<SpeciesMeta>>,
pub quadruped_low: BodyData<BodyMeta, quadruped_low::AllSpecies<SpeciesMeta>>,
}
/// Can only retrieve body metadata by direct index.
@ -78,6 +81,7 @@ impl<BodyMeta, SpeciesMeta> core::ops::Index<NpcKind> for AllBodies<BodyMeta, Sp
NpcKind::StoneGolem => &self.golem.body,
NpcKind::Rat => &self.critter.body,
NpcKind::Reddragon => &self.dragon.body,
NpcKind::Crocodile => &self.quadruped_low.body,
}
}
}
@ -118,6 +122,7 @@ impl Body {
Body::FishSmall(_) => 0.2,
Body::BipedLarge(_) => 2.0,
Body::Golem(_) => 2.5,
Body::QuadrupedLow(_) => 1.0,
Body::Object(_) => 0.3,
}
}

@ -0,0 +1,69 @@
use rand::{seq::SliceRandom, thread_rng};
#[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<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::QuadrupedLow(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Crocodile = 0,
}
/// Data representing per-species generic data.
///
/// NOTE: Deliberately don't (yet?) implement serialize.
#[derive(Clone, Debug, Deserialize)]
pub struct AllSpecies<SpeciesMeta> {
pub crocodile: SpeciesMeta,
}
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
type Output = SpeciesMeta;
#[inline]
fn index(&self, &index: &'a Species) -> &Self::Output {
match index {
Species::Crocodile => &self.crocodile,
}
}
}
pub const ALL_SPECIES: [Species; 1] = [
Species::Crocodile,
];
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
type IntoIter = std::iter::Copied<std::slice::Iter<'static, Self::Item>>;
type Item = Species;
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[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];

@ -23,7 +23,7 @@ pub use admin::{Admin, AdminList};
pub use agent::{Agent, Alignment};
pub use body::{
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, golem,
humanoid, object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
humanoid, object, quadruped_medium, quadruped_small, quadruped_low, AllBodies, Body, BodyData,
};
pub use character_state::{Attacking, CharacterState, StateUpdate};
pub use chat::{ChatMode, ChatMsg, ChatType, Faction, Group, SpeechBubble, SpeechBubbleType};

@ -108,6 +108,7 @@ impl EntityInfo {
Some(get_npc_name(&NPC_NAMES.quadruped_small, body.species))
},
Body::Dragon(body) => Some(get_npc_name(&NPC_NAMES.dragon, body.species)),
Body::QuadrupedLow(body) => Some(get_npc_name(&NPC_NAMES.quadruped_low, body.species)),
_ => None,
}
.map(|s| {

@ -16,9 +16,10 @@ pub enum NpcKind {
Rat,
StoneGolem,
Reddragon,
Crocodile,
}
pub const ALL_NPCS: [NpcKind; 8] = [
pub const ALL_NPCS: [NpcKind; 9] = [
NpcKind::Humanoid,
NpcKind::Wolf,
NpcKind::Pig,
@ -27,6 +28,7 @@ pub const ALL_NPCS: [NpcKind; 8] = [
NpcKind::Rat,
NpcKind::StoneGolem,
NpcKind::Reddragon,
NpcKind::Crocodile,
];
/// Body-specific NPC name metadata.
@ -94,6 +96,7 @@ pub fn kind_to_body(kind: NpcKind) -> Body {
NpcKind::Rat => comp::critter::Body::random().into(),
NpcKind::StoneGolem => comp::golem::Body::random().into(),
NpcKind::Reddragon => comp::dragon::Body::random().into(),
NpcKind::Crocodile => comp::quadruped_low::Body::random().into(),
}
}
@ -223,6 +226,14 @@ impl NpcBody {
comp::dragon::Body::random_with,
)
})
.or_else(|| {
parse(
s,
NpcKind::Crocodile,
&npc_names.quadruped_low,
comp::quadruped_low::Body::random_with,
)
})
.ok_or(())
}
}

76
voxygen/src/anim/mod.rs Normal file

@ -0,0 +1,76 @@
pub mod biped_large;
pub mod bird_medium;
pub mod bird_small;
pub mod character;
pub mod critter;
pub mod dragon;
pub mod fish_medium;
pub mod fish_small;
pub mod fixture;
pub mod golem;
pub mod object;
pub mod quadruped_medium;
pub mod quadruped_small;
pub mod quadruped_low;
use crate::render::FigureBoneData;
use vek::*;
#[derive(Copy, Clone, Debug)]
pub struct Bone {
pub offset: Vec3<f32>,
pub ori: Quaternion<f32>,
pub scale: Vec3<f32>,
}
impl Default for Bone {
fn default() -> Self {
Self {
offset: Vec3::zero(),
ori: Quaternion::identity(),
scale: Vec3::broadcast(1.0 / 11.0),
}
}
}
impl Bone {
pub fn compute_base_matrix(&self) -> Mat4<f32> {
Mat4::<f32>::translation_3d(self.offset)
* Mat4::scaling_3d(self.scale)
* Mat4::from(self.ori)
}
/// Change the current bone to be more like `target`.
fn interpolate(&mut self, target: &Bone, dt: f32) {
// TODO: Make configurable.
let factor = (15.0 * dt).min(1.0);
self.offset += (target.offset - self.offset) * factor;
self.ori = vek::Slerp::slerp(self.ori, target.ori, factor);
self.scale += (target.scale - self.scale) * factor;
}
}
pub trait Skeleton: Send + Sync + 'static {
type Attr;
fn bone_count(&self) -> usize { 16 }
fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3<f32>);
/// Change the current skeleton to be more like `target`.
fn interpolate(&mut self, target: &Self, dt: f32);
}
pub trait Animation {
type Skeleton: Skeleton;
type Dependency;
/// Returns a new skeleton that is generated by the animation.
fn update_skeleton(
skeleton: &Self::Skeleton,
dependency: Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &<<Self as Animation>::Skeleton as Skeleton>::Attr,
) -> Self::Skeleton;
}

@ -0,0 +1,116 @@
use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr};
use std::{f32::consts::PI, ops::Mul};
use vek::*;
pub struct IdleAnimation;
impl Animation for IdleAnimation {
type Dependency = f64;
type Skeleton = QuadrupedLowSkeleton;
fn update_skeleton(
skeleton: &Self::Skeleton,
global_time: Self::Dependency,
anim_time: f64,
_rate: &mut f32,
skeleton_attr: &SkeletonAttr,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let ultra_slow = (anim_time as f32 * 1.0).sin();
let slow = (anim_time as f32 * 2.5).sin();
let slowalt = (anim_time as f32 * 2.5 + PI / 2.0).sin();
let dragon_look = Vec2::new(
((global_time + anim_time) as f32 / 8.0)
.floor()
.mul(7331.0)
.sin()
* 0.5,
((global_time + anim_time) as f32 / 8.0)
.floor()
.mul(1337.0)
.sin()
* 0.25,
);
next.head_upper.offset = Vec3::new(
0.0,
skeleton_attr.head_upper.0,
skeleton_attr.head_upper.1 + ultra_slow * 0.20,
);
next.head_upper.ori = Quaternion::rotation_z(0.8 * dragon_look.x)
* Quaternion::rotation_x(0.8 * dragon_look.y);
next.head_upper.scale = Vec3::one();
next.head_lower.offset = Vec3::new(
0.0,
skeleton_attr.head_lower.0,
skeleton_attr.head_lower.1 + ultra_slow * 0.20,
);
next.head_lower.ori = Quaternion::rotation_z(0.8 * dragon_look.x)
* Quaternion::rotation_x(-0.2 + 0.8 * dragon_look.y);
next.head_lower.scale = Vec3::one() * 1.05;
next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1);
next.jaw.ori = Quaternion::rotation_x(slow * 0.04);
next.jaw.scale = Vec3::one() * 1.05;
next.chest_front.offset = Vec3::new(
0.0,
skeleton_attr.chest_front.0,
skeleton_attr.chest_front.1,
);
next.chest_front.ori = Quaternion::rotation_y(slow * 0.01);
next.chest_front.scale = Vec3::one() * 1.05;
next.chest_rear.offset =
Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1);
next.chest_rear.ori = Quaternion::rotation_y(slow * 0.01);
next.chest_rear.scale = Vec3::one() * 1.05;
next.tail_front.offset =
Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1);
next.tail_front.ori = Quaternion::rotation_z(slowalt * 0.10) * Quaternion::rotation_x(0.1);
next.tail_front.scale = Vec3::one() * 0.98;
next.tail_rear.offset =
Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1);
next.tail_rear.ori = Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(0.05);
next.tail_rear.scale = Vec3::one() * 0.98;
next.foot_fl.offset = Vec3::new(
-skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1,
skeleton_attr.feet_f.2,
);
next.foot_fl.ori = Quaternion::rotation_x(0.0);
next.foot_fl.scale = Vec3::one();
next.foot_fr.offset = Vec3::new(
skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1,
skeleton_attr.feet_f.2,
);
next.foot_fr.ori = Quaternion::rotation_x(0.0);
next.foot_fr.scale = Vec3::one();
next.foot_bl.offset = Vec3::new(
-skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1,
skeleton_attr.feet_b.2,
);
next.foot_bl.ori = Quaternion::rotation_x(0.0);
next.foot_bl.scale = Vec3::one();
next.foot_br.offset = Vec3::new(
skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1,
skeleton_attr.feet_b.2,
);
next.foot_br.ori = Quaternion::rotation_x(0.0);
next.foot_br.scale = Vec3::one();
next
}
}

@ -0,0 +1,121 @@
use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr};
use std::f32::consts::PI;
use vek::*;
pub struct JumpAnimation;
impl Animation for JumpAnimation {
type Dependency = (f32, f64);
type Skeleton = QuadrupedLowSkeleton;
fn update_skeleton(
skeleton: &Self::Skeleton,
_global_time: Self::Dependency,
anim_time: f64,
_rate: &mut f32,
skeleton_attr: &SkeletonAttr,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let lab = 12.0;
let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin();
let wave_ultra_slow_cos = (anim_time as f32 * 3.0 + PI).cos();
let wave_slow = (anim_time as f32 * 3.5 + PI).sin();
let footl = (anim_time as f32 * lab as f32 + PI).sin();
let footr = (anim_time as f32 * lab as f32).sin();
let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin();
let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin();
next.head_upper.offset = Vec3::new(
0.0,
skeleton_attr.head_upper.0,
skeleton_attr.head_upper.1 + wave_ultra_slow * 0.20,
);
next.head_upper.ori =
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10);
next.head_upper.scale = Vec3::one() * 1.05;
next.head_lower.offset = Vec3::new(
0.0,
skeleton_attr.head_lower.0,
skeleton_attr.head_lower.1 + wave_ultra_slow * 0.20,
);
next.head_lower.ori =
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10);
next.head_lower.scale = Vec3::one() * 1.05;
next.jaw.offset = Vec3::new(
0.0,
skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12,
skeleton_attr.jaw.1 + wave_slow * 0.2,
);
next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03);
next.jaw.scale = Vec3::one() * 1.05;
next.tail_front.offset = Vec3::new(
0.0,
skeleton_attr.tail_front.0,
skeleton_attr.tail_front.1 + centeroffset * 0.6,
);
next.tail_front.ori = Quaternion::rotation_x(center * 0.03);
next.tail_front.scale = Vec3::one() * 0.98;
next.tail_rear.offset = Vec3::new(
0.0,
skeleton_attr.tail_rear.0,
skeleton_attr.tail_rear.1 + centeroffset * 0.6,
);
next.tail_rear.ori = Quaternion::rotation_x(center * 0.03);
next.tail_rear.scale = Vec3::one() * 0.98;
next.chest_front.offset = Vec3::new(
0.0,
skeleton_attr.chest_front.0,
skeleton_attr.chest_front.1,
);
next.chest_front.ori = Quaternion::rotation_y(center * 0.05);
next.chest_front.scale = Vec3::one();
next.chest_rear.offset =
Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1);
next.chest_rear.ori = Quaternion::rotation_y(center * 0.05);
next.chest_rear.scale = Vec3::one();
next.foot_fl.offset = Vec3::new(
-skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1,
skeleton_attr.feet_f.2,
);
next.foot_fl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06);
next.foot_fl.scale = Vec3::one();
next.foot_fr.offset = Vec3::new(
skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1,
skeleton_attr.feet_f.2,
);
next.foot_fr.ori = Quaternion::rotation_x(-1.3 + footr * 0.06);
next.foot_fr.scale = Vec3::one();
next.foot_bl.offset = Vec3::new(
-skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1,
skeleton_attr.feet_b.2,
);
next.foot_bl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06);
next.foot_bl.scale = Vec3::one();
next.foot_br.offset = Vec3::new(
skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1,
skeleton_attr.feet_b.2,
);
next.foot_br.ori = Quaternion::rotation_x(-1.3 + footr * 0.06);
next.foot_br.scale = Vec3::one();
next
}
}

@ -0,0 +1,167 @@
pub mod idle;
pub mod jump;
pub mod run;
// Reexports
pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation};
use super::{Bone, Skeleton};
use crate::render::FigureBoneData;
use common::comp::{self};
use vek::Vec3;
#[derive(Clone, Default)]
pub struct QuadrupedLowSkeleton {
head_upper: Bone,
head_lower: Bone,
jaw: Bone,
tail_front: Bone,
tail_rear: Bone,
chest_front: Bone,
chest_rear: Bone,
foot_fl: Bone,
foot_fr: Bone,
foot_bl: Bone,
foot_br: Bone,
}
impl QuadrupedLowSkeleton {
pub fn new() -> Self { Self::default() }
}
impl Skeleton for QuadrupedLowSkeleton {
type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 11 }
fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3<f32>) {
let head_upper_mat = self.head_upper.compute_base_matrix();
let head_lower_mat = self.head_lower.compute_base_matrix();
let chest_front_mat = self.chest_front.compute_base_matrix();
let chest_rear_mat = self.chest_rear.compute_base_matrix();
(
[
FigureBoneData::new(chest_front_mat * head_lower_mat * head_upper_mat),
FigureBoneData::new(chest_front_mat * head_lower_mat),
FigureBoneData::new(
chest_front_mat
* head_lower_mat
* head_upper_mat
* self.jaw.compute_base_matrix(),
),
FigureBoneData::new(chest_front_mat),
FigureBoneData::new(chest_front_mat * self.chest_rear.compute_base_matrix()),
FigureBoneData::new(chest_front_mat * chest_rear_mat * self.tail_front.compute_base_matrix()),
FigureBoneData::new(
chest_front_mat
* chest_rear_mat
* self.tail_front.compute_base_matrix()
* self.tail_rear.compute_base_matrix(),
),
FigureBoneData::new(self.foot_fl.compute_base_matrix()),
FigureBoneData::new(self.foot_fr.compute_base_matrix()),
FigureBoneData::new(self.foot_bl.compute_base_matrix()),
FigureBoneData::new(self.foot_br.compute_base_matrix()),
FigureBoneData::default(),
FigureBoneData::default(),
FigureBoneData::default(),
FigureBoneData::default(),
FigureBoneData::default(),
],
Vec3::default(),
)
}
fn interpolate(&mut self, target: &Self, dt: f32) {
self.head_upper.interpolate(&target.head_upper, dt);
self.head_lower.interpolate(&target.head_lower, dt);
self.jaw.interpolate(&target.jaw, dt);
self.tail_front.interpolate(&target.tail_front, dt);
self.tail_rear.interpolate(&target.tail_rear, dt);
self.chest_front.interpolate(&target.chest_front, dt);
self.chest_rear.interpolate(&target.chest_rear, dt);
self.foot_fl.interpolate(&target.foot_fl, dt);
self.foot_fr.interpolate(&target.foot_fr, dt);
self.foot_bl.interpolate(&target.foot_bl, dt);
self.foot_br.interpolate(&target.foot_br, dt);
}
}
pub struct SkeletonAttr {
head_upper: (f32, f32),
head_lower: (f32, f32),
jaw: (f32, f32),
tail_front: (f32, f32),
tail_rear: (f32, f32),
chest_front: (f32, f32),
chest_rear: (f32, f32),
feet_f: (f32, f32, f32),
feet_b: (f32, f32, f32),
height: f32,
}
impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr {
type Error = ();
fn try_from(body: &'a comp::Body) -> Result<Self, Self::Error> {
match body {
comp::Body::QuadrupedLow(body) => Ok(SkeletonAttr::from(body)),
_ => Err(()),
}
}
}
impl Default for SkeletonAttr {
fn default() -> Self {
Self {
head_upper: (0.0, 0.0),
head_lower: (0.0, 0.0),
jaw: (0.0, 0.0),
tail_front: (0.0, 0.0),
tail_rear: (0.0, 0.0),
chest_front: (0.0, 0.0),
chest_rear: (0.0, 0.0),
feet_f: (0.0, 0.0, 0.0),
feet_b: (0.0, 0.0, 0.0),
height: (0.0),
}
}
}
impl<'a> From<&'a comp::quadruped_low::Body> for SkeletonAttr {
fn from(body: &'a comp::quadruped_low::Body) -> Self {
use comp::quadruped_low::Species::*;
Self {
head_upper: match (body.species, body.body_type) {
(Crocodile, _) => (12.0, 16.0),
},
head_lower: match (body.species, body.body_type) {
(Crocodile, _) => (-4.0, -7.0),
},
jaw: match (body.species, body.body_type) {
(Crocodile, _) => (3.0, -5.0),
},
tail_rear: match (body.species, body.body_type) {
(Crocodile, _) => (-6.0, -2.0),
},
tail_front: match (body.species, body.body_type) {
(Crocodile, _) => (-6.0, -2.0),
},
chest_front: match (body.species, body.body_type) {
(Crocodile, _) => (4.0, 11.0),
},
chest_rear: match (body.species, body.body_type) {
(Crocodile, _) => (4.0, 11.0),
},
feet_f: match (body.species, body.body_type) {
(Crocodile, _) => (5.0, 6.0, 2.0),
},
feet_b: match (body.species, body.body_type) {
(Crocodile, _) => (5.0, -4.0, 3.0),
},
height: match (body.species, body.body_type) {
(Crocodile, _) => (1.2),
},
}
}
}

@ -0,0 +1,136 @@
use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr};
use std::{f32::consts::PI, ops::Mul};
use vek::*;
pub struct RunAnimation;
impl Animation for RunAnimation {
type Dependency = (f32, f64);
type Skeleton = QuadrupedLowSkeleton;
fn update_skeleton(
skeleton: &Self::Skeleton,
(_velocity, global_time): Self::Dependency,
anim_time: f64,
_rate: &mut f32,
skeleton_attr: &SkeletonAttr,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let lab = 14;
let wave_ultra_slow_cos = (anim_time as f32 * 3.0 + PI).cos();
let wave_slow = (anim_time as f32 * 4.5).sin();
let vertlf = (anim_time as f32 * lab as f32 + PI * 1.8).sin().max(0.15);
let vertrfoffset = (anim_time as f32 * lab as f32 + PI * 0.80).sin().max(0.15);
let vertlboffset = (anim_time as f32 * lab as f32).sin().max(0.15);
let vertrb = (anim_time as f32 * lab as f32 + PI).sin().max(0.15);
let horilf = (anim_time as f32 * lab as f32 + PI * 1.2).sin();
let horirfoffset = (anim_time as f32 * lab as f32 + PI * 0.20).sin();
let horilboffset = (anim_time as f32 * lab as f32 + PI * 1.4).sin();
let horirb = (anim_time as f32 * lab as f32 + PI * 0.4).sin();
let vertchest = (anim_time as f32 * lab as f32 + PI * 0.3).sin().max(0.2);
let horichest = (anim_time as f32 * lab as f32 + PI * 0.8).sin();
let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin();
let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin();
let dragon_look = Vec2::new(
((global_time + anim_time) as f32 / 4.0)
.floor()
.mul(7331.0)
.sin()
* 0.25,
((global_time + anim_time) as f32 / 4.0)
.floor()
.mul(1337.0)
.sin()
* 0.125,
);
next.head_upper.offset =
Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1);
next.head_upper.ori =
Quaternion::rotation_x(dragon_look.y) * Quaternion::rotation_z(dragon_look.x);
next.head_upper.scale = Vec3::one();
next.head_lower.offset =
Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1);
next.head_lower.ori = Quaternion::rotation_x(wave_slow * 0.05);
next.head_lower.scale = Vec3::one();
next.jaw.offset = Vec3::new(
0.0,
skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12,
skeleton_attr.jaw.1 + wave_slow * 0.2,
);
next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03);
next.jaw.scale = Vec3::one() * 1.05;
next.tail_front.offset = Vec3::new(
0.0,
skeleton_attr.tail_front.0,
skeleton_attr.tail_front.1 + centeroffset * 0.6,
);
next.tail_front.ori = Quaternion::rotation_x(center * 0.03);
next.tail_front.scale = Vec3::one() * 0.98;
next.tail_rear.offset = Vec3::new(
0.0,
skeleton_attr.tail_rear.0,
skeleton_attr.tail_rear.1 + centeroffset * 0.6,
);
next.tail_rear.ori = Quaternion::rotation_x(center * 0.03);
next.tail_rear.scale = Vec3::one() * 0.98;
next.chest_front.offset = Vec3::new(
0.0,
skeleton_attr.chest_front.0 + horichest * 1.25,
skeleton_attr.chest_front.1 + vertchest * -1.6 + 1.0,
);
next.chest_front.ori = Quaternion::rotation_y(horichest * -0.09);
next.chest_front.scale = Vec3::one();
next.chest_rear.offset =
Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1);
next.chest_rear.ori = Quaternion::rotation_y(horichest * -0.09);
next.chest_rear.scale = Vec3::one();
next.foot_fl.offset = Vec3::new(
-skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1 + horilf * 2.5,
skeleton_attr.feet_f.2 + vertlf * 5.0 * skeleton_attr.height - 0.5,
);
next.foot_fl.ori = Quaternion::rotation_x(horilf * 0.4);
next.foot_fl.scale = Vec3::one();
next.foot_fr.offset = Vec3::new(
skeleton_attr.feet_f.0,
skeleton_attr.feet_f.1 + horirfoffset * 2.5,
skeleton_attr.feet_f.2 + vertrfoffset * 5.0 * skeleton_attr.height - 0.5,
);
next.foot_fr.ori = Quaternion::rotation_x(horirfoffset * 0.4);
next.foot_fr.scale = Vec3::one();
next.foot_bl.offset = Vec3::new(
-skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1 + horilboffset * 3.0,
skeleton_attr.feet_b.2 + vertlboffset * 5.0 * skeleton_attr.height - 0.5,
);
next.foot_bl.ori = Quaternion::rotation_x(horilboffset * 0.35);
next.foot_bl.scale = Vec3::one();
next.foot_br.offset = Vec3::new(
skeleton_attr.feet_b.0,
skeleton_attr.feet_b.1 + horirb * 3.0,
skeleton_attr.feet_b.2 + vertrb * 5.0 * skeleton_attr.height - 0.5,
);
next.foot_br.ori = Quaternion::rotation_x(horirb * 0.35);
next.foot_br.scale = Vec3::one();
next
}
}

@ -678,6 +678,75 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
None,
]
},
Body::QuadrupedLow(body) => {
let quadruped_low_central_spec =
QuadrupedLowCentralSpec::load_watched(manifest_indicator);
let quadruped_low_lateral_spec =
QuadrupedLowLateralSpec::load_watched(manifest_indicator);
[
Some(quadruped_low_central_spec.mesh_head_upper(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_head_lower(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_jaw(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_tail_front(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_tail_rear(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_chest_front(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_central_spec.mesh_chest_rear(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_lateral_spec.mesh_foot_fl(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_lateral_spec.mesh_foot_fr(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_lateral_spec.mesh_foot_bl(
body.species,
body.body_type,
generate_mesh,
)),
Some(quadruped_low_lateral_spec.mesh_foot_br(
body.species,
body.body_type,
generate_mesh,
)),
None,
None,
None,
None,
None,
]
},
Body::Object(object) => [
Some(mesh_object(object, generate_mesh)),
None,

@ -18,6 +18,7 @@ use common::{
object,
quadruped_medium::{BodyType as QMBodyType, Species as QMSpecies},
quadruped_small::{BodyType as QSBodyType, Species as QSSpecies},
quadruped_low::{BodyType as QLBodyType, Species as QLSpecies},
Loadout,
},
figure::{DynaUnionizer, MatSegment, Material, Segment},
@ -2951,6 +2952,306 @@ impl GolemLateralSpec {
generate_mesh(&lateral, Vec3::from(spec.foot_r.offset))
}
}
///
#[derive(Serialize, Deserialize)]
pub struct QuadrupedLowCentralSpec(HashMap<(QLSpecies, QLBodyType), SidedQLCentralVoxSpec>);
#[derive(Serialize, Deserialize)]
struct SidedQLCentralVoxSpec {
upper: QuadrupedLowCentralSubSpec,
lower: QuadrupedLowCentralSubSpec,
jaw: QuadrupedLowCentralSubSpec,
chest_front: QuadrupedLowCentralSubSpec,
chest_rear: QuadrupedLowCentralSubSpec,
tail_front: QuadrupedLowCentralSubSpec,
tail_rear: QuadrupedLowCentralSubSpec,
}
#[derive(Serialize, Deserialize)]
struct QuadrupedLowCentralSubSpec {
offset: [f32; 3], // Should be relative to initial origin
central: VoxSimple,
}
#[derive(Serialize, Deserialize)]
pub struct QuadrupedLowLateralSpec(HashMap<(QLSpecies, QLBodyType), SidedQLLateralVoxSpec>);
#[derive(Serialize, Deserialize)]
struct SidedQLLateralVoxSpec {
front_left: QuadrupedLowLateralSubSpec,
front_right: QuadrupedLowLateralSubSpec,
back_left: QuadrupedLowLateralSubSpec,
back_right: QuadrupedLowLateralSubSpec,
}
#[derive(Serialize, Deserialize)]
struct QuadrupedLowLateralSubSpec {
offset: [f32; 3], // Should be relative to initial origin
lateral: VoxSimple,
}
impl Asset for QuadrupedLowCentralSpec {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
}
}
impl Asset for QuadrupedLowLateralSpec {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
}
}
impl QuadrupedLowCentralSpec {
pub fn load_watched(indicator: &mut ReloadIndicator) -> Arc<Self> {
assets::load_watched::<Self>("voxygen.voxel.quadruped_low_central_manifest", indicator)
.unwrap()
}
pub fn mesh_head_upper(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No upper head specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.upper.central.0);
generate_mesh(&central, Vec3::from(spec.upper.offset))
}
pub fn mesh_head_lower(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No lower head specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.lower.central.0);
generate_mesh(&central, Vec3::from(spec.lower.offset))
}
pub fn mesh_jaw(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
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), generate_mesh);
},
};
let central = graceful_load_segment(&spec.jaw.central.0);
generate_mesh(&central, Vec3::from(spec.jaw.offset))
}
pub fn mesh_chest_front(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No chest_front specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.chest_front.central.0);
generate_mesh(&central, Vec3::from(spec.chest_front.offset))
}
pub fn mesh_chest_rear(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No chest_rear specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.chest_rear.central.0);
generate_mesh(&central, Vec3::from(spec.chest_rear.offset))
}
pub fn mesh_tail_rear(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No tail_rear specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.tail_rear.central.0);
generate_mesh(&central, Vec3::from(spec.tail_rear.offset))
}
pub fn mesh_tail_front(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
let spec = match self.0.get(&(species, body_type)) {
Some(spec) => spec,
None => {
error!(
"No tail_front specification exists for the combination of {:?} and {:?}",
species, body_type
);
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh);
},
};
let central = graceful_load_segment(&spec.tail_front.central.0);
generate_mesh(&central, Vec3::from(spec.tail_front.offset))
}
}
impl QuadrupedLowLateralSpec {
pub fn load_watched(indicator: &mut ReloadIndicator) -> Arc<Self> {
assets::load_watched::<Self>("voxygen.voxel.quadruped_low_lateral_manifest", indicator)
.unwrap()
}
pub fn mesh_foot_fl(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
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), generate_mesh);
},
};
let lateral = graceful_load_segment(&spec.front_left.lateral.0);
generate_mesh(&lateral, Vec3::from(spec.front_left.offset))
}
pub fn mesh_foot_fr(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
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), generate_mesh);
},
};
let lateral = graceful_load_segment(&spec.front_right.lateral.0);
generate_mesh(&lateral, Vec3::from(spec.front_right.offset))
}
pub fn mesh_foot_bl(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
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), generate_mesh);
},
};
let lateral = graceful_load_segment(&spec.back_left.lateral.0);
generate_mesh(&lateral, Vec3::from(spec.back_left.offset))
}
pub fn mesh_foot_br(
&self,
species: QLSpecies,
body_type: QLBodyType,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,
) -> Mesh<FigurePipeline> {
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), generate_mesh);
},
};
let lateral = graceful_load_segment(&spec.back_right.lateral.0);
generate_mesh(&lateral, Vec3::from(spec.back_right.offset))
}
}
///
pub fn mesh_object(
obj: object::Body,
generate_mesh: impl FnOnce(&Segment, Vec3<f32>) -> Mesh<FigurePipeline>,

@ -5,6 +5,13 @@ pub use cache::FigureModelCache;
pub use load::load_mesh; // TODO: Don't make this public.
use crate::{
anim::{
self, biped_large::BipedLargeSkeleton, bird_medium::BirdMediumSkeleton,
bird_small::BirdSmallSkeleton, character::CharacterSkeleton, critter::CritterSkeleton,
dragon::DragonSkeleton, fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton,
golem::GolemSkeleton, object::ObjectSkeleton, quadruped_medium::QuadrupedMediumSkeleton,
quadruped_small::QuadrupedSmallSkeleton, quadruped_low::QuadrupedLowSkeleton, Animation, Skeleton,
},
ecs::comp::Interpolated,
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
scene::{
@ -44,6 +51,7 @@ pub struct FigureMgr {
critter_model_cache: FigureModelCache<CritterSkeleton>,
quadruped_small_model_cache: FigureModelCache<QuadrupedSmallSkeleton>,
quadruped_medium_model_cache: FigureModelCache<QuadrupedMediumSkeleton>,
quadruped_low_model_cache: FigureModelCache<QuadrupedLowSkeleton>,
bird_medium_model_cache: FigureModelCache<BirdMediumSkeleton>,
bird_small_model_cache: FigureModelCache<BirdSmallSkeleton>,
dragon_model_cache: FigureModelCache<DragonSkeleton>,
@ -54,6 +62,7 @@ pub struct FigureMgr {
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
quadruped_small_states: HashMap<EcsEntity, FigureState<QuadrupedSmallSkeleton>>,
quadruped_medium_states: HashMap<EcsEntity, FigureState<QuadrupedMediumSkeleton>>,
quadruped_low_states: HashMap<EcsEntity, FigureState<QuadrupedLowSkeleton>>,
bird_medium_states: HashMap<EcsEntity, FigureState<BirdMediumSkeleton>>,
fish_medium_states: HashMap<EcsEntity, FigureState<FishMediumSkeleton>>,
critter_states: HashMap<EcsEntity, FigureState<CritterSkeleton>>,
@ -73,6 +82,7 @@ impl FigureMgr {
critter_model_cache: FigureModelCache::new(),
quadruped_small_model_cache: FigureModelCache::new(),
quadruped_medium_model_cache: FigureModelCache::new(),
quadruped_low_model_cache: FigureModelCache::new(),
bird_medium_model_cache: FigureModelCache::new(),
bird_small_model_cache: FigureModelCache::new(),
dragon_model_cache: FigureModelCache::new(),
@ -83,6 +93,7 @@ impl FigureMgr {
character_states: HashMap::new(),
quadruped_small_states: HashMap::new(),
quadruped_medium_states: HashMap::new(),
quadruped_low_states: HashMap::new(),
bird_medium_states: HashMap::new(),
fish_medium_states: HashMap::new(),
critter_states: HashMap::new(),
@ -100,6 +111,7 @@ impl FigureMgr {
self.critter_model_cache.clean(tick);
self.quadruped_small_model_cache.clean(tick);
self.quadruped_medium_model_cache.clean(tick);
self.quadruped_low_model_cache.clean(tick);
self.bird_medium_model_cache.clean(tick);
self.bird_small_model_cache.clean(tick);
self.dragon_model_cache.clean(tick);
@ -261,6 +273,9 @@ impl FigureMgr {
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity);
},
Body::QuadrupedLow(_) => {
self.quadruped_low_states.remove(&entity);
},
Body::BirdMedium(_) => {
self.bird_medium_states.remove(&entity);
},
@ -307,6 +322,11 @@ impl FigureMgr {
.get_mut(&entity)
.map(|state| state.visible = false);
},
Body::QuadrupedLow(_) => {
self.quadruped_low_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
Body::BirdMedium(_) => {
self.bird_medium_states
.get_mut(&entity)
@ -374,6 +394,10 @@ impl FigureMgr {
.quadruped_medium_states
.get(&entity)
.map(|state| state.lpindex),
Body::QuadrupedLow(_) => self
.quadruped_low_states
.get(&entity)
.map(|state| state.lpindex),
Body::BirdMedium(_) => self
.bird_medium_states
.get(&entity)
@ -430,6 +454,12 @@ impl FigureMgr {
state.visible = false
});
},
Body::QuadrupedLow(_) => {
self.quadruped_low_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
Body::BirdMedium(_) => {
self.bird_medium_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
@ -989,6 +1019,89 @@ impl FigureMgr {
is_player,
);
},
Body::QuadrupedLow(_) => {
let skeleton_attr = &self
.quadruped_low_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
CameraMode::default(),
None,
)
.1;
let state = self
.quadruped_low_states
.entry(entity)
.or_insert_with(|| {
FigureState::new(renderer, QuadrupedLowSkeleton::new())
});
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_fluid, // In water
) {
// Standing
(true, false, false) => {
anim::quadruped_low::IdleAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// Running
(true, true, false) => {
anim::quadruped_low::RunAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// In air
(false, _, false) => {
anim::quadruped_low::JumpAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
is_player,
);
},
Body::BirdMedium(_) => {
let skeleton_attr = &self
.bird_medium_model_cache
@ -1622,6 +1735,8 @@ impl FigureMgr {
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.quadruped_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.quadruped_low_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.bird_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.fish_medium_states
@ -1774,6 +1889,7 @@ impl FigureMgr {
critter_model_cache,
quadruped_small_model_cache,
quadruped_medium_model_cache,
quadruped_low_model_cache,
bird_medium_model_cache,
bird_small_model_cache,
dragon_model_cache,
@ -1784,6 +1900,7 @@ impl FigureMgr {
character_states,
quadruped_small_states,
quadruped_medium_states,
quadruped_low_states,
bird_medium_states,
fish_medium_states,
critter_states,
@ -1846,6 +1963,22 @@ impl FigureMgr {
.0,
)
}),
Body::QuadrupedLow(_) => quadruped_low_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&quadruped_low_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::BirdMedium(_) => bird_medium_states.get(&entity).map(|state| {
(
state.locals(),
@ -2022,6 +2155,7 @@ impl FigureMgr {
+ self.quadruped_small_states.len()
+ self.character_states.len()
+ self.quadruped_medium_states.len()
+ self.quadruped_low_states.len()
+ self.bird_medium_states.len()
+ self.fish_medium_states.len()
+ self.critter_states.len()
@ -2048,6 +2182,11 @@ impl FigureMgr {
.iter()
.filter(|(_, c)| c.visible)
.count()
+ self
.quadruped_low_states
.iter()
.filter(|(_, c)| c.visible)
.count()
+ self
.bird_medium_states
.iter()