Add quadruped_low skeleton

This commit is contained in:
Snowram 2020-06-02 01:56:50 +02:00 committed by jshipsey
parent 2a7a8b05e6
commit 72eb03ceb5
36 changed files with 1387 additions and 2 deletions

View File

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

BIN
assets/voxygen/voxel/npc/crocodile/female/chest_front.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/chest_rear.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/foot_bl.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/foot_br.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/foot_fl.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/foot_fr.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/head_lower.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/head_upper.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/jaw.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/female/tail.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/chest_front.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/chest_rear.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/foot_bl.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/foot_br.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/foot_fl.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/foot_fr.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/head_lower.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/head_upper.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/jaw.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/npc/crocodile/male/tail.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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"),
),
),
})

View File

@ -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"),
),
),
})

View File

@ -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,
}
}

View File

@ -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];

View File

@ -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};

View File

@ -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| {

View File

@ -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
View 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;
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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),
},
}
}
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -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>,

View File

@ -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()