animation fixes

This commit is contained in:
Robin Appelman 2024-12-25 17:28:16 +01:00
commit 2309050a25
10 changed files with 397 additions and 66 deletions

View file

@ -6,7 +6,7 @@ description = "Rust parser for valve model files."
repository = "https://github.com/icewind1991/vmdl" repository = "https://github.com/icewind1991/vmdl"
license = "MIT" license = "MIT"
exclude = ["data"] exclude = ["data"]
rust-version = "1.74.0" rust-version = "1.76.0"
[dependencies] [dependencies]
arrayvec = "0.7.6" arrayvec = "0.7.6"

View file

@ -1,3 +1,4 @@
use cgmath::Matrix4;
use std::env::args; use std::env::args;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@ -25,9 +26,32 @@ fn main() -> Result<(), vmdl::ModelError> {
); );
} }
dbg!(Matrix4::from(mdl.bones[1].pose_to_bone.clone()));
// for animation_desc in &mdl.local_animations {
// println!(
// "{}: {} frames at {}fps",
// animation_desc.name, animation_desc.frame_count, animation_desc.fps,
// );
// for animation in &animation_desc.animations {
// println!(
// "\tbone {:.2} frame 0:\n\t\trot: {:?}\n\t\tpos: {:?}",
// animation.bone,
// animation.rotation(0),
// animation.position(0),
// );
// println!(
// "\tbone {:.2} frame 1:\n\t\trot: {:?}\n\t\tpos: {:?}",
// animation.bone,
// animation.rotation(10),
// animation.position(10),
// );
// }
// }
let model = Model::from_parts(mdl, vtx, vvd); let model = Model::from_parts(mdl, vtx, vvd);
dbg!(model.root_transform());
dbg!(model.idle_transform()); let _ = model;
Ok(()) Ok(())
} }

View file

@ -6,13 +6,16 @@ mod material;
use crate::error::Error; use crate::error::Error;
use crate::material::{load_material_fallback, MaterialData}; use crate::material::{load_material_fallback, MaterialData};
use cgmath::{vec3, Matrix4, SquareMatrix}; use cgmath::{vec3, Matrix4, SquareMatrix};
use std::collections::HashMap;
use std::env::args_os; use std::env::args_os;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use tf_asset_loader::Loader; use tf_asset_loader::Loader;
use three_d::{ use three_d::{
AmbientLight, Camera, ClearState, ColorMaterial, CpuMaterial, CpuMesh, CpuModel, CpuTexture, AmbientLight, Camera, ClearState, ColorMaterial, Context, CpuMaterial, CpuMesh, CpuModel,
DepthMaterial, DirectionalLight, FrameOutput, Light, NormalMaterial, ORMMaterial, OrbitControl, CpuTexture, DepthMaterial, DirectionalLight, FrameOutput, Light, NormalMaterial, ORMMaterial,
PhysicalMaterial, PositionMaterial, UVMaterial, Vec2, Vec4, Window, WindowSettings, OrbitControl, PhysicalMaterial, PositionMaterial, UVMaterial, Vec2, Vec4, Window,
WindowSettings,
}; };
use three_d_asset::{ use three_d_asset::{
degrees, Geometry, Mat4, Positions, Primitive, Srgba, TextureData, Vec3, Viewport, degrees, Geometry, Mat4, Positions, Primitive, Srgba, TextureData, Vec3, Viewport,
@ -64,8 +67,7 @@ fn main() -> Result<(), Error> {
let loader = Loader::new().expect("loader"); let loader = Loader::new().expect("loader");
let skin_count = source_model.skin_tables().count(); let skin_count = source_model.skin_tables().count();
let animation_count = source_model.animations().count();
let cpu_models = (0..skin_count).map(|skin| model_to_model(&source_model, &loader, skin));
let ph_material = PhysicalMaterial { let ph_material = PhysicalMaterial {
albedo: Srgba { albedo: Srgba {
@ -77,10 +79,6 @@ fn main() -> Result<(), Error> {
..Default::default() ..Default::default()
}; };
let models: Vec<three_d::Model<PhysicalMaterial>> = cpu_models
.map(|cpu_model| three_d::Model::new(&context, &cpu_model).expect("failed to load model"))
.collect();
let mut directional = [ let mut directional = [
DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(1.0, -1.0, 0.0)), DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(1.0, -1.0, 0.0)),
DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(1.0, 1.0, 0.0)), DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(1.0, 1.0, 0.0)),
@ -98,9 +96,19 @@ fn main() -> Result<(), Error> {
let mut fov = 60.0; let mut fov = 60.0;
let mut debug_type = DebugType::NONE; let mut debug_type = DebugType::NONE;
let mut skin_index = 0; let mut skin_index = 0;
let mut animation_index = 0;
let mut frame = 0;
let mut skip_count = 0;
let mut playing = false;
let model_builder = ModelBuilder::new(source_model, loader);
window.render_loop(move |mut frame_input| { window.render_loop(move |mut frame_input| {
let model = &models[skin_index]; let model_key = ModelKey::new(skin_index, animation_index, frame);
let model = model_builder.get(&context, model_key);
let frame_count = model_builder.frame_count(animation_index);
let mut change = frame_input.first_frame; let mut change = frame_input.first_frame;
let mut panel_width = frame_input.viewport.width; let mut panel_width = frame_input.viewport.width;
change |= gui.update( change |= gui.update(
@ -142,6 +150,14 @@ fn main() -> Result<(), Error> {
ui.add(Slider::new(&mut depth_max, 1.0..=30.0).text("Depth max")); ui.add(Slider::new(&mut depth_max, 1.0..=30.0).text("Depth max"));
ui.add(Slider::new(&mut fov, 45.0..=90.0).text("FOV")); ui.add(Slider::new(&mut fov, 45.0..=90.0).text("FOV"));
ui.label("Animation");
ui.add(
Slider::new(&mut animation_index, 0..=(animation_count - 1))
.text("Animation"),
);
ui.add(Slider::new(&mut frame, 0..=(frame_count - 1)).text("Frame"));
ui.add(Checkbox::new(&mut playing, "Play"));
ui.label("Position"); ui.label("Position");
ui.add(Label::new(format!("\tx: {}", camera.position().x))); ui.add(Label::new(format!("\tx: {}", camera.position().x)));
ui.add(Label::new(format!("\ty: {}", camera.position().y))); ui.add(Label::new(format!("\ty: {}", camera.position().y)));
@ -150,6 +166,17 @@ fn main() -> Result<(), Error> {
panel_width = gui_context.used_size().x as u32; panel_width = gui_context.used_size().x as u32;
}, },
); );
if playing {
skip_count += 1;
if skip_count > 4 {
frame += 1;
if frame >= frame_count {
frame = 0;
}
change = true;
skip_count = 0;
}
}
let viewport = Viewport { let viewport = Viewport {
x: panel_width as i32, x: panel_width as i32,
@ -224,7 +251,7 @@ fn main() -> Result<(), Error> {
model.iter().map(|gm| &gm.geometry), model.iter().map(|gm| &gm.geometry),
lights, lights,
), ),
DebugType::NONE => target.render(&camera, model, lights), DebugType::NONE => target.render(&camera, model.as_ref(), lights),
} }
.write(|| gui.render()) .write(|| gui.render())
.expect("failed to render"); .expect("failed to render");
@ -249,17 +276,28 @@ pub fn map_coords<C: Into<Vec3>>(vec: C) -> Vec3 {
} }
} }
fn model_to_model(model: &Model, loader: &Loader, skin: usize) -> CpuModel { fn model_to_model(
model: &Model,
loader: &Loader,
skin: usize,
animation: usize,
frame: usize,
) -> CpuModel {
let skin = model.skin_tables().nth(skin).unwrap(); let skin = model.skin_tables().nth(skin).unwrap();
let transforms = Matrix4::identity(); let transforms = Matrix4::identity();
let animation = model
.animations()
.nth(animation)
.unwrap_or_else(|| model.animations().next().unwrap());
let geometries = model let geometries = model
.meshes() .meshes()
.map(|mesh| { .map(|mesh| {
let positions: Vec<Vec3> = mesh let positions: Vec<Vec3> = mesh
.vertices() .vertices()
.map(|vertex| model.apply_root_transform(vertex.position)) .map(|vertex| model.apply_animation(animation, vertex, frame))
.map(|position| map_coords(position) * 10.0) .map(|position| map_coords(position) * 10.0)
.map(|vertex: Vec3| (transforms * vertex.extend(1.0)).truncate()) .map(|vertex: Vec3| (transforms * vertex.extend(1.0)).truncate())
.collect(); .collect();
@ -348,3 +386,64 @@ fn convert_texture(texture: material::TextureData, keep_alpha: bool) -> CpuTextu
..CpuTexture::default() ..CpuTexture::default()
} }
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
struct ModelKey {
skin: usize,
animation: usize,
frame: usize,
}
impl ModelKey {
pub fn new(skin: usize, animation: usize, frame: usize) -> Self {
ModelKey {
skin,
animation,
frame,
}
}
}
pub struct ModelBuilder {
source: Model,
loader: Loader,
cache: Mutex<HashMap<ModelKey, Arc<three_d::Model<PhysicalMaterial>>>>,
}
impl ModelBuilder {
fn new(source: Model, loader: Loader) -> Self {
ModelBuilder {
source,
loader,
cache: Mutex::default(),
}
}
fn frame_count(&self, animation: usize) -> usize {
self.source
.animations()
.nth(animation)
.map(|animation| animation.frame_count)
.unwrap_or_default()
}
fn get(&self, context: &Context, key: ModelKey) -> Arc<three_d::Model<PhysicalMaterial>> {
self.cache
.lock()
.unwrap()
.entry(key)
.or_insert_with(|| {
let cpu_model = model_to_model(
&self.source,
&self.loader,
key.skin,
key.animation,
key.frame,
);
Arc::new(
three_d::Model::new(context, &cpu_model).expect("failed to build gpu model"),
)
})
.clone()
}
}

View file

@ -1,32 +1,116 @@
use crate::mdl::Mdl; use crate::mdl::{Bone, BoneId, Mdl};
use std::collections::VecDeque;
use std::ops::Deref; use std::ops::Deref;
/// A handle represents a mdl structure in the mdl file and the mdl file containing it. /// A handle represents a mdl structure in the mdl file and the mdl file containing it.
/// ///
/// Keeping a reference of the mdl file with the mdl is required since a lot of mdl types /// Keeping a reference of the mdl file with the mdl is required since a lot of mdl types
/// reference parts from other structures in the mdl file /// reference parts from other structures in the mdl file
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Handle<'a, T> { pub struct Handle<'a, T, K> {
_mdl: &'a Mdl, mdl: &'a Mdl,
data: &'a T, data: &'a T,
key: K,
} }
impl<T> Clone for Handle<'_, T> { impl<T, K: PartialEq> PartialEq for Handle<'_, T, K> {
fn clone(&self) -> Self { fn eq(&self, other: &Self) -> bool {
Handle { ..*self } self.key == other.key
} }
} }
impl<'a, T> AsRef<T> for Handle<'a, T> { impl<'a, T, K> Handle<'a, T, K> {
pub fn new(mdl: &'a Mdl, data: &'a T, key: K) -> Self {
Self { mdl, data, key }
}
}
impl<T, K: Clone> Handle<'_, T, K> {
pub fn key(&self) -> K {
self.key.clone()
}
}
impl<'a, T, K> AsRef<T> for Handle<'a, T, K> {
fn as_ref(&self) -> &'a T { fn as_ref(&self) -> &'a T {
self.data self.data
} }
} }
impl<T> Deref for Handle<'_, T> { impl<T, K> Deref for Handle<'_, T, K> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.data self.data
} }
} }
impl<'a> Handle<'a, Bone, BoneId> {
pub fn parent(&self) -> Option<Self> {
Some(Self::new(
self.mdl,
self.mdl.bones.get(usize::from(self.parent))?,
self.parent,
))
}
pub fn children(&self) -> impl Iterator<Item = Self> + 'a {
let key = self.key();
let mdl = self.mdl;
self.mdl
.bones
.iter()
.enumerate()
.filter(move |(_, bone)| bone.parent == key)
.map(move |(i, bone)| Self::new(mdl, bone, i.into()))
}
pub fn tree(&self) -> impl Iterator<Item = Self> {
BoneTreeIter::new(self.clone())
}
pub fn ancestors(&self) -> impl Iterator<Item = Self> {
BoneAncestorsIter { bone: self.clone() }
}
pub fn is_affected_by(&self, bone_id: BoneId) -> bool {
self.key == bone_id || self.ancestors().any(|ancestor| ancestor.key == bone_id)
}
}
struct BoneTreeIter<'a> {
queue: VecDeque<Handle<'a, Bone, BoneId>>,
}
impl<'a> BoneTreeIter<'a> {
pub fn new(root: Handle<'a, Bone, BoneId>) -> Self {
let mut queue = VecDeque::with_capacity(16);
queue.push_back(root);
BoneTreeIter { queue }
}
}
impl<'a> Iterator for BoneTreeIter<'a> {
type Item = Handle<'a, Bone, BoneId>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.queue.pop_front()?;
self.queue.extend(next.children());
Some(next)
}
}
struct BoneAncestorsIter<'a> {
bone: Handle<'a, Bone, BoneId>,
}
impl<'a> Iterator for BoneAncestorsIter<'a> {
type Item = Handle<'a, Bone, BoneId>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.bone.parent()?;
self.bone = next.clone();
Some(next)
}
}

View file

@ -7,7 +7,9 @@ pub mod vtx;
pub mod vvd; pub mod vvd;
pub use crate::mdl::Mdl; pub use crate::mdl::Mdl;
use crate::mdl::{AnimationDescription, Bone, ModelFlags, PoseParameterDescription, TextureInfo}; use crate::mdl::{
AnimationDescription, Bone, BoneId, ModelFlags, PoseParameterDescription, TextureInfo,
};
pub use crate::vtx::Vtx; pub use crate::vtx::Vtx;
use crate::vvd::Vertex; use crate::vvd::Vertex;
pub use crate::vvd::Vvd; pub use crate::vvd::Vvd;
@ -134,8 +136,19 @@ impl Model {
self.mdl.name.as_str() self.mdl.name.as_str()
} }
pub fn bones(&self) -> impl Iterator<Item = &Bone> { pub fn bones(&self) -> impl Iterator<Item = Handle<Bone, BoneId>> {
self.mdl.bones.iter() self.mdl
.bones
.iter()
.enumerate()
.map(|(i, bone)| Handle::new(&self.mdl, bone, i.into()))
}
pub fn bone(&self, id: BoneId) -> Option<Handle<Bone, BoneId>> {
self.mdl
.bones
.get(usize::from(id))
.map(|bone| Handle::new(&self.mdl, bone, id))
} }
pub fn root_transform(&self) -> Matrix4<f32> { pub fn root_transform(&self) -> Matrix4<f32> {
@ -157,7 +170,11 @@ impl Model {
self.mdl self.mdl
.local_animations .local_animations
.iter() .iter()
.filter_map(|desc| desc.animations.iter().find(|animation| animation.bone == 0)) .filter_map(|desc| {
desc.animations
.iter()
.find(|animation| animation.bone == BoneId::default())
})
.next() .next()
.map(|animation| animation.rotation(0)) .map(|animation| animation.rotation(0))
.map(Matrix4::from) .map(Matrix4::from)
@ -176,6 +193,38 @@ impl Model {
let transform = self.idle_transform() * self.root_transform(); let transform = self.idle_transform() * self.root_transform();
transform.transform_vector(Vector3::from(vec)).into() transform.transform_vector(Vector3::from(vec)).into()
} }
pub fn apply_animation(
&self,
animation: &AnimationDescription,
vertex: &Vertex,
frame: usize,
) -> Vector {
let mut position = vertex.position.into();
for animation in animation.animations.iter() {
if let Some(animated_bone) = self.bone(animation.bone) {
let weight: f32 = vertex
.bone_weights
.weights()
.flat_map(|weight| Some((self.bone(weight.bone_id)?, weight)))
.filter(|(bone, _)| bone.is_affected_by(animated_bone.key()))
.map(|(_, weight)| weight.weight)
.sum();
let pose_to_bone = animated_bone.pos.into();
let bone_rotation = Matrix4::from(animated_bone.rot);
if weight > 0.0 {
position -= pose_to_bone;
let transform = (animation.transform(frame)) * bone_rotation;
position = transform.transform_vector(position);
position += pose_to_bone;
}
}
}
position.into()
}
} }
pub struct SkinTable<'a> { pub struct SkinTable<'a> {

View file

@ -73,8 +73,8 @@ impl Mdl {
.iter_mut() .iter_mut()
.flat_map(|desc| desc.animations.iter_mut()) .flat_map(|desc| desc.animations.iter_mut())
.for_each(|animation| { .for_each(|animation| {
if let Some(bone) = bones.get(animation.bone as usize) { if let Some(bone) = bones.get(usize::from(animation.bone)) {
animation.set_scales(bone); animation.apply_bone_data(bone);
} }
}); });
let animation_block_source: String = read_single(data, header.anim_blocks_name_index)?; let animation_block_source: String = read_single(data, header.anim_blocks_name_index)?;

View file

@ -1,12 +1,12 @@
use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48}; use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48};
use crate::mdl::Bone; use crate::mdl::{Bone, BoneId};
use crate::{ use crate::{
index_range, read_relative, read_single, ModelError, Quaternion, RadianEuler, ReadRelative, index_range, read_relative, read_single, ModelError, Quaternion, RadianEuler, ReadRelative,
Readable, ReadableRelative, Vector, Readable, ReadableRelative, Vector,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use cgmath::{Matrix4, SquareMatrix}; use cgmath::Matrix4;
use std::mem::size_of; use std::mem::size_of;
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
@ -89,16 +89,6 @@ pub struct AnimationDescription {
pub animations: Vec<Animation>, pub animations: Vec<Animation>,
} }
impl AnimationDescription {
pub fn get_bone_transform(&self, bone: u8, frame: usize) -> Matrix4<f32> {
let Some(animation) = self.animations.iter().find(|anim| anim.bone == bone) else {
return Matrix4::identity();
};
Matrix4::from_translation(animation.position(frame).into())
* Matrix4::from(animation.rotation(frame))
}
}
impl ReadRelative for AnimationDescription { impl ReadRelative for AnimationDescription {
type Header = AnimationDescriptionHeader; type Header = AnimationDescriptionHeader;
@ -139,7 +129,7 @@ impl ReadableRelative for AnimationBlock {}
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct AnimationHeader { pub struct AnimationHeader {
bone: u8, bone: BoneId,
flags: AnimationFlags, flags: AnimationFlags,
next_offset: u16, next_offset: u16,
} }
@ -215,7 +205,7 @@ struct FrameValues<'a> {
} }
impl FrameValues<'_> { impl FrameValues<'_> {
pub fn get(&self, index: u8) -> Result<u16, ModelError> { pub fn get(&self, index: u8) -> Result<i16, ModelError> {
if self.header.total <= index { if self.header.total <= index {
let offset_count = self.header.valid + 1; let offset_count = self.header.valid + 1;
let offset = (offset_count as usize) * size_of::<u16>(); let offset = (offset_count as usize) * size_of::<u16>();
@ -292,14 +282,25 @@ impl RotationData {
} }
} }
fn set_scale(&mut self, scale: Vector) { fn set_scale(&mut self, scale: RadianEuler) {
if let RotationData::Animated(values) = self { if let RotationData::Animated(values) = self {
values.iter_mut().for_each(|value| { values.iter_mut().for_each(|value| {
// scale and fixup the angles
*value = RadianEuler { *value = RadianEuler {
y: value.x * scale.x, x: value.x * scale.x,
z: value.y * scale.y, y: value.y * scale.y,
x: value.z * scale.z, z: value.z * scale.z,
}
});
}
}
fn set_base_rotation(&mut self, base: RadianEuler) {
if let RotationData::Animated(values) = self {
values.iter_mut().for_each(|value| {
*value = RadianEuler {
x: value.x + base.x,
y: value.y + base.y,
z: value.z + base.z,
} }
}); });
} }
@ -338,7 +339,7 @@ impl PositionData {
/// Per bone animation data /// Per bone animation data
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Animation { pub struct Animation {
pub bone: u8, pub bone: BoneId,
pub flags: AnimationFlags, pub flags: AnimationFlags,
rotation_data: RotationData, rotation_data: RotationData,
position_data: PositionData, position_data: PositionData,
@ -353,8 +354,15 @@ impl Animation {
self.position_data.position(frame) self.position_data.position(frame)
} }
pub(crate) fn set_scales(&mut self, bone: &Bone) { pub fn transform(&self, frame: usize) -> Matrix4<f32> {
Matrix4::from_translation(self.position(frame).into()) * Matrix4::from(self.rotation(frame))
}
pub(crate) fn apply_bone_data(&mut self, bone: &Bone) {
self.rotation_data.set_scale(bone.rot_scale); self.rotation_data.set_scale(bone.rot_scale);
if self.flags.contains(AnimationFlags::STUDIO_ANIM_DELTA) {
self.rotation_data.set_base_rotation(bone.rot);
}
self.position_data.set_scale(bone.pos_scale); self.position_data.set_scale(bone.pos_scale);
} }
} }
@ -381,7 +389,7 @@ fn read_animation(
let value_data = &data[offset..]; let value_data = &data[offset..];
let values: Vec<RadianEuler> = (0..frames) let values: Vec<RadianEuler> = (0..frames)
.map(|frame| read_animation_values(value_data, frame, pointers)) .map(|frame| read_animation_values(value_data, frame, pointers))
.map(|r| r.map(|[x, y, z]| RadianEuler { x, z, y })) .map(|r| r.map(|[y, z, x]| RadianEuler { x, z, y }))
.collect::<Result<_, ModelError>>()?; .collect::<Result<_, ModelError>>()?;
RotationData::from(values) RotationData::from(values)
} else { } else {

View file

@ -4,8 +4,43 @@ use crate::{
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use std::fmt::Display;
use std::mem::size_of; use std::mem::size_of;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Zeroable, Pod, Default)]
#[repr(transparent)]
pub struct BoneId(u8);
impl From<u8> for BoneId {
fn from(val: u8) -> Self {
BoneId(val)
}
}
impl From<i32> for BoneId {
fn from(val: i32) -> Self {
BoneId(u8::try_from(val).unwrap_or(255))
}
}
impl From<usize> for BoneId {
fn from(val: usize) -> Self {
BoneId(u8::try_from(val).unwrap_or_default())
}
}
impl From<BoneId> for usize {
fn from(val: BoneId) -> Self {
val.0 as usize
}
}
impl Display for BoneId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)] #[repr(C)]
pub struct BoneHeader { pub struct BoneHeader {
@ -17,7 +52,7 @@ pub struct BoneHeader {
pub quaternion: Quaternion, pub quaternion: Quaternion,
pub rot: RadianEuler, pub rot: RadianEuler,
pub pos_scale: Vector, pub pos_scale: Vector,
pub rot_scale: Vector, pub rot_scale: [f32; 3],
pub pose_to_bone: Transform3x4, pub pose_to_bone: Transform3x4,
pub q_alignment: Quaternion, pub q_alignment: Quaternion,
@ -38,14 +73,14 @@ static_assertions::const_assert_eq!(size_of::<BoneHeader>(), 216);
#[repr(C)] #[repr(C)]
pub struct Bone { pub struct Bone {
pub name: String, pub name: String,
pub parent: i32, // parent bone pub parent: BoneId,
pub bone_controller: [i32; 6], // bone controller index, -1 == none pub bone_controller: [i32; 6], // bone controller index, -1 == none
pub pos: Vector, pub pos: Vector,
pub quaternion: Quaternion, pub quaternion: Quaternion,
pub rot: RadianEuler, pub rot: RadianEuler,
pub pos_scale: Vector, pub pos_scale: Vector,
pub rot_scale: Vector, pub rot_scale: RadianEuler,
pub pose_to_bone: Transform3x4, pub pose_to_bone: Transform3x4,
pub q_alignment: Quaternion, pub q_alignment: Quaternion,
@ -93,13 +128,21 @@ impl ReadRelative for Bone {
Ok(Bone { Ok(Bone {
name: read_single(data, header.sz_name_index)?, name: read_single(data, header.sz_name_index)?,
parent: header.parent, parent: header.parent.into(),
bone_controller: header.bone_controller, bone_controller: header.bone_controller,
pos: header.pos, pos: Vector {
x: header.pos.z,
y: header.pos.x,
z: header.pos.y,
},
quaternion: header.quaternion, quaternion: header.quaternion,
rot: header.rot, rot: header.rot,
pos_scale: header.pos_scale, pos_scale: header.pos_scale,
rot_scale: header.rot_scale, rot_scale: RadianEuler {
x: header.rot_scale[2],
y: header.rot_scale[0],
z: header.rot_scale[1],
},
pose_to_bone: header.pose_to_bone, pose_to_bone: header.pose_to_bone,
q_alignment: header.q_alignment, q_alignment: header.q_alignment,
flags: header.flags, flags: header.flags,

View file

@ -129,7 +129,7 @@ impl From<cgmath::Quaternion<f32>> for Quaternion {
} }
} }
impl From<Quaternion> for cgmath::Matrix4<f32> { impl From<Quaternion> for Matrix4<f32> {
fn from(q: Quaternion) -> Self { fn from(q: Quaternion) -> Self {
// cgmath::Quaternion::from(Quaternion { // cgmath::Quaternion::from(Quaternion {
// x: q.z, // x: q.z,
@ -240,6 +240,18 @@ impl From<RadianEuler> for Matrix4<f32> {
} }
} }
impl Mul<f32> for RadianEuler {
type Output = RadianEuler;
fn mul(self, rhs: f32) -> Self::Output {
RadianEuler {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
/// Fixed length, null-terminated string /// Fixed length, null-terminated string
#[derive(Debug, Clone, Default, Copy)] #[derive(Debug, Clone, Default, Copy)]
pub struct FixedString<const LEN: usize>(ArrayString<LEN>); pub struct FixedString<const LEN: usize>(ArrayString<LEN>);

View file

@ -1,3 +1,4 @@
use crate::mdl::BoneId;
use crate::{index_range, ReadableRelative, Vector}; use crate::{index_range, ReadableRelative, Vector};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::cmp::min; use std::cmp::min;
@ -82,22 +83,33 @@ static_assertions::const_assert_eq!(size_of::<Vertex>(), 48);
#[repr(C)] #[repr(C)]
pub struct BoneWeights { pub struct BoneWeights {
weight: [f32; 3], weight: [f32; 3],
bone: [u8; 3], bone: [BoneId; 3],
bone_count: u8, bone_count: u8,
} }
impl BoneWeights { impl BoneWeights {
pub fn weights(&self) -> impl Iterator<Item = BoneWeight> + '_ { pub fn weights(&self) -> impl Iterator<Item = BoneWeight> + '_ {
(0..min(self.bone_count as usize, 3)).map(|i| BoneWeight { self.bone
weight: self.weight[i] / self.bone_count as f32, .into_iter()
bone_id: self.bone[i], .zip(self.weight)
.take(min(self.bone_count as usize, 3))
.map(|(bone_id, weight)| BoneWeight {
bone_id,
weight: weight / self.bone_count as f32,
}) })
} }
pub fn get_weight(&self, bone_id: BoneId) -> f32 {
self.weights()
.find(|weight| weight.bone_id == bone_id)
.map(|weight| weight.weight)
.unwrap_or_default()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BoneWeight { pub struct BoneWeight {
pub bone_id: u8, pub bone_id: BoneId,
pub weight: f32, pub weight: f32,
} }