read animation data

This commit is contained in:
Robin Appelman 2024-08-26 17:11:32 +02:00
commit 5e48fd2a6f
13 changed files with 472 additions and 73 deletions

1
Cargo.lock generated
View file

@ -2804,6 +2804,7 @@ dependencies = [
"criterion", "criterion",
"gltf", "gltf",
"gltf-json", "gltf-json",
"half",
"iai", "iai",
"image", "image",
"itertools 0.13.0", "itertools 0.13.0",

View file

@ -18,6 +18,7 @@ tracing = "0.1.40"
bytemuck = { version = "1.17.0", features = ["derive"] } bytemuck = { version = "1.17.0", features = ["derive"] }
cgmath = "0.18.0" cgmath = "0.18.0"
num_enum = "0.7.3" num_enum = "0.7.3"
half = "2.4.1"
[dev-dependencies] [dev-dependencies]
three-d = { version = "0.14.1", features = ["egui-gui"] } three-d = { version = "0.14.1", features = ["egui-gui"] }

View file

@ -1,4 +1,4 @@
use cgmath::{Euler, Matrix4, Quaternion}; use cgmath::Euler;
use std::env::args; use std::env::args;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@ -21,16 +21,23 @@ fn main() -> Result<(), vmdl::ModelError> {
// dbg!(&mdl.header2); // dbg!(&mdl.header2);
for bone in &mdl.bones { // for bone in &mdl.bones {
println!( // println!(
"{}: from {} at\n\t{:?}\n\t{:?}\n\t{:?}", // "{}: from {} at\n\t{:?}\n\t{:?}\n\t{:?}",
bone.name, bone.parent, bone.rot, bone.q_alignment, bone.pose_to_bone // bone.name, bone.parent, bone.rot, bone.q_alignment, bone.pose_to_bone
); // );
} // }
dbg!(&mdl.animation_blocks); dbg!(&mdl.local_animations[0]);
let transform = mdl
.local_animations
.first()
.map(|a| a.animations[0].rotation(0))
.unwrap();
dbg!(transform);
dbg!(Euler::from(cgmath::Quaternion::from(transform)));
// dbg!(&mdl.attachments); // dbg!(&mdl.attachments);
let model = Model::from_parts(mdl, vtx, vvd); let _model = Model::from_parts(mdl, vtx, vvd);
// dbg!(Euler::from(Quaternion::from(model.root_transform()))); // dbg!(Euler::from(Quaternion::from(model.root_transform())));
// for strip in model.vertex_strips() { // for strip in model.vertex_strips() {
// for vertex in strip { // for vertex in strip {

View file

@ -251,7 +251,8 @@ fn model_to_model(model: &Model, loader: &Loader, skin: usize) -> CpuModel {
let skin = model.skin_tables().nth(skin).unwrap(); let skin = model.skin_tables().nth(skin).unwrap();
let transforms = model.root_transform(); let transforms = model.root_transform();
let transforms = Matrix4::identity(); let transforms = dbg!(model.idle_transform());
// let transforms = Matrix4::identity();
let geometries = model let geometries = model
.meshes() .meshes()

132
src/compressed_vector.rs Normal file
View file

@ -0,0 +1,132 @@
use crate::{Quaternion, ReadableRelative, Vector};
use bytemuck::{Pod, Zeroable};
use cgmath::{InnerSpace, Vector4};
use half::f16;
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
pub struct Vector48 {
x: u16,
y: u16,
z: u16,
}
impl ReadableRelative for Vector48 {}
impl Vector48 {
pub fn x(&self) -> f32 {
f16::from_bits(self.x).into()
}
pub fn y(&self) -> f32 {
f16::from_bits(self.y).into()
}
pub fn z(&self) -> f32 {
f16::from_bits(self.z).into()
}
}
impl From<Vector48> for Vector {
fn from(value: Vector48) -> Self {
Vector {
x: value.x(),
y: value.y(),
z: value.z(),
}
}
}
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
pub struct Quaternion48 {
x: u16,
y: u16,
z: u16,
}
impl ReadableRelative for Quaternion48 {}
fn calc_w(x: f32, y: f32, z: f32, w_neg: bool) -> f32 {
let w_sign = if w_neg { -1.0 } else { 1.0 };
f32::sqrt(1.0 - ((x * x) - (y * y) - (z * z))) * w_sign
}
impl Quaternion48 {
const Z_MASK: u16 = 0x7F_FF;
const W_NEG_MASK: u16 = 0x80_00;
pub fn x(&self) -> f32 {
((self.x as f32) - 32768.0) / 32768.0
}
pub fn y(&self) -> f32 {
((self.y as f32) - 32768.0) / 32768.0
}
pub fn z(&self) -> f32 {
(((self.z & Self::Z_MASK) as f32) - 16384.0) / 16384.0
}
pub fn w(&self) -> f32 {
calc_w(
self.x(),
self.y(),
self.z(),
self.z & Self::W_NEG_MASK == Self::W_NEG_MASK,
)
}
}
impl From<Quaternion48> for Quaternion {
fn from(value: Quaternion48) -> Self {
let normalized = Vector4::new(value.x(), value.y(), value.z(), value.w()).normalize();
Quaternion {
x: normalized.x,
y: normalized.y,
z: normalized.z,
w: normalized.w,
}
}
}
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
pub struct Quaternion64(u64);
impl ReadableRelative for Quaternion64 {}
impl Quaternion64 {
const MASK_21_BIT: u64 = 0b111111111111111111111;
const W_NEG_MASK: u64 = 0x80_00_00_00_00_00_00_00;
fn val(&self, offset: i32) -> f32 {
let raw = (self.0 >> offset) & Self::MASK_21_BIT;
(raw as f32 - 1048576.0) / 1048576.5
}
pub fn x(&self) -> f32 {
self.val(0)
}
pub fn y(&self) -> f32 {
self.val(21)
}
pub fn z(&self) -> f32 {
self.val(42)
}
pub fn w(&self) -> f32 {
calc_w(
self.x(),
self.y(),
self.z(),
self.0 & Self::W_NEG_MASK == Self::W_NEG_MASK,
)
}
}
impl From<Quaternion64> for Quaternion {
fn from(value: Quaternion64) -> Self {
let normalized = Vector4::new(value.x(), value.y(), value.z(), value.w()).normalize();
Quaternion {
x: normalized.x,
y: normalized.y,
z: normalized.z,
w: normalized.w,
}
}
}

View file

@ -1,3 +1,4 @@
mod compressed_vector;
mod error; mod error;
mod handle; mod handle;
pub mod mdl; pub mod mdl;
@ -11,7 +12,7 @@ pub use crate::vtx::Vtx;
use crate::vvd::Vertex; use crate::vvd::Vertex;
pub use crate::vvd::Vvd; pub use crate::vvd::Vvd;
use bytemuck::{pod_read_unaligned, Contiguous, Pod}; use bytemuck::{pod_read_unaligned, Contiguous, Pod};
use cgmath::{Matrix4, SquareMatrix, Transform, Vector3}; use cgmath::{Matrix4, SquareMatrix};
pub use error::*; pub use error::*;
pub use handle::Handle; pub use handle::Handle;
use itertools::Either; use itertools::Either;
@ -145,6 +146,20 @@ impl Model {
.unwrap_or_else(Matrix4::identity) .unwrap_or_else(Matrix4::identity)
} }
pub fn idle_transform(&self) -> Matrix4<f32> {
if self.mdl.header.flags.contains(ModelFlags::STATIC_PROP) {
return Matrix4::identity();
}
self.mdl
.local_animations
.first()
.and_then(|desc| desc.animations.first())
.map(|animation| animation.rotation(0))
.map(Matrix4::from)
.unwrap_or_else(Matrix4::identity)
}
pub fn surface_prop(&self) -> &str { pub fn surface_prop(&self) -> &str {
self.mdl.surface_prop.as_str() self.mdl.surface_prop.as_str()
} }
@ -154,7 +169,9 @@ impl Model {
} }
pub fn vertex_to_world_space(&self, vertex: &Vertex) -> Vector { pub fn vertex_to_world_space(&self, vertex: &Vertex) -> Vector {
vertex.position.transformed(self.root_transform()) // vertex.position.transformed(self.root_transform())
vertex.position.transformed(self.idle_transform())
// let mut pos = Vector3::from(vertex.position); // let mut pos = Vector3::from(vertex.position);
// for weights in vertex.bone_weights.weights() { // for weights in vertex.bone_weights.weights() {
// if let Some(bone) = self.mdl.bones.get(weights.bone_id as usize) { // if let Some(bone) = self.mdl.bones.get(weights.bone_id as usize) {

View file

@ -1,6 +1,5 @@
mod raw; mod raw;
use bytemuck::{Pod, Zeroable};
pub use raw::header::*; pub use raw::header::*;
pub use raw::header2::*; pub use raw::header2::*;
pub use raw::*; pub use raw::*;
@ -63,7 +62,16 @@ impl Mdl {
let key_values = (header.key_value_size > 0) let key_values = (header.key_value_size > 0)
.then(|| read_single(data, header.key_value_index)) .then(|| read_single(data, header.key_value_index))
.transpose()?; .transpose()?;
let local_animations = read_relative(data, header.local_animation_indexes())?; let mut local_animations: Vec<AnimationDescription> =
read_relative(data, header.local_animation_indexes())?;
local_animations
.iter_mut()
.flat_map(|desc| desc.animations.iter_mut())
.for_each(|animation| {
if let Some(bone) = bones.get(animation.bone as usize) {
animation.set_scales(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)?;
let animation_blocks = read_relative(data, header.animation_block_indexes())?; let animation_blocks = read_relative(data, header.animation_block_indexes())?;

View file

@ -1,5 +1,9 @@
use crate::vvd::Vertex; use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48};
use crate::{ModelError, ReadRelative, ReadableRelative}; use crate::mdl::Bone;
use crate::{
read_single, ModelError, Quaternion, RadianEuler, ReadRelative, Readable, ReadableRelative,
Vector,
};
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::mem::size_of; use std::mem::size_of;
@ -29,14 +33,8 @@ impl ReadRelative for PoseParameterDescription {
type Header = PoseParameterDescriptionHeader; type Header = PoseParameterDescriptionHeader;
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> { fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
let name_bytes = data
.get(header.name_index as usize..)
.ok_or(ModelError::OutOfBounds {
data: "pose name",
offset: header.name_index as usize,
})?;
Ok(PoseParameterDescription { Ok(PoseParameterDescription {
name: String::read(name_bytes, ())?, name: read_single(data, header.name_index)?,
flags: header.flags, flags: header.flags,
start: header.start, start: header.start,
end: header.end, end: header.end,
@ -87,26 +85,33 @@ pub struct AnimationDescription {
pub name: String, pub name: String,
pub fps: f32, pub fps: f32,
pub frame_count: i32, pub frame_count: i32,
pub animation_block: i32, pub animations: Vec<Animation>,
pub animation: i32,
} }
impl ReadRelative for AnimationDescription { impl ReadRelative for AnimationDescription {
type Header = AnimationDescriptionHeader; type Header = AnimationDescriptionHeader;
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> { fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
let name_bytes = let mut animations = Vec::with_capacity(1);
data.get(header.name_offset as usize..) let mut offset = header.animation_index as usize;
.ok_or(ModelError::OutOfBounds { loop {
data: "animation name", let (animation, next_offset) = if header.animation_block == 0 {
offset: header.name_offset as usize, read_animation(data, offset, header.frame_count as usize)?
})?; } else {
todo!("read animation from animation block");
};
animations.push(animation);
if next_offset == 0 {
break;
}
offset += next_offset;
}
Ok(AnimationDescription { Ok(AnimationDescription {
name: String::read(name_bytes, ())?, name: read_single(data, header.name_offset)?,
fps: header.fps, fps: header.fps,
frame_count: header.frame_count, frame_count: header.frame_count,
animation_block: header.animation_block, animations,
animation: header.animation_index,
}) })
} }
} }
@ -147,3 +152,234 @@ bitflags! {
const STUDIO_ANIM_RAWROT2 = 0x00000020; const STUDIO_ANIM_RAWROT2 = 0x00000020;
} }
} }
#[derive(Zeroable, Pod, Copy, Clone, Debug)]
#[repr(C)]
struct AnimationValuePointer([u16; 3]);
impl ReadableRelative for AnimationValuePointer {}
#[derive(Zeroable, Pod, Copy, Clone, Debug, Default)]
#[repr(C)]
struct ValueHeader {
valid: u8,
total: u8,
}
impl ReadableRelative for ValueHeader {}
fn read_animation_values(
data: &[u8], // data starting at the AnimationValuePointer
frame: usize,
base_pointers: AnimationValuePointer,
) -> Result<[f32; 3], ModelError> {
let mut result = [0.0; 3];
for (out, base_pointer) in result.iter_mut().zip(base_pointers.0) {
if base_pointer == 0 {
*out = 0.0;
} else {
let header: ValueHeader = read_single(data, base_pointer)?;
let values = FrameValues {
header,
data: &data[base_pointer as usize..],
};
*out = values.get(frame as u8).map(|val| val as f32)?;
}
}
Ok(result)
}
/// I hate this data structure
///
/// Seems to be an array of
///
/// FrameValues {
/// header: ValueHeader,
/// values: [u16; self.header.valid]
/// }
///
/// each item containing `header.total` worth of frames (for frames larger than `header.valid` it re-uses the last valid data)
/// when looking up frame `k` we skip through the list of values until we find the value range for the frame
struct FrameValues<'a> {
header: ValueHeader,
data: &'a [u8], // data starting at self.header
}
impl<'a> FrameValues<'a> {
pub fn get(&self, index: u8) -> Result<u16, ModelError> {
if self.header.total <= index {
let offset_count = self.header.valid + 1;
let offset = (offset_count as usize) * size_of::<u16>();
let next_header: ValueHeader = read_single(self.data, offset)?;
let next = FrameValues {
header: next_header,
data: &self.data[offset..],
};
if next_header.total == 0 {
return Err(ModelError::OutOfBounds {
data: "animation value",
offset,
});
}
next.get(index - self.header.total)
} else {
let offset_count = if self.header.valid > index {
index + 1
} else {
self.header.valid
};
let offset = (offset_count as usize) * size_of::<u16>();
read_single(self.data, offset)
}
}
}
#[derive(Clone, Debug)]
pub enum RotationData {
Quaternion48(Quaternion48),
Quaternion64(Quaternion64),
RotationValues(Vec<RadianEuler>),
None,
}
impl RotationData {
pub fn rotation(&self, frame: usize) -> Quaternion {
match self {
RotationData::Quaternion48(q) => Quaternion::from(*q),
RotationData::Quaternion64(q) => Quaternion::from(*q),
RotationData::RotationValues(values) => {
values.get(frame).copied().unwrap_or_default().into()
}
RotationData::None => Quaternion::default(),
}
}
pub fn size(&self) -> usize {
match self {
RotationData::Quaternion48(_) => size_of::<Quaternion48>(),
RotationData::Quaternion64(_) => size_of::<Quaternion64>(),
RotationData::RotationValues(_) => size_of::<AnimationValuePointer>(),
RotationData::None => 0,
}
}
fn set_scale(&mut self, scale: Vector) {
if let RotationData::RotationValues(values) = self {
values.iter_mut().for_each(|value| {
*value = RadianEuler {
x: value.x * scale.x,
y: value.y * scale.y,
z: value.z * scale.z,
}
});
}
}
}
#[derive(Clone, Debug)]
pub enum PositionData {
Vector48(Vector48),
PositionValues(Vec<Vector>),
None,
}
impl PositionData {
pub fn position(&self, frame: usize) -> Vector {
match self {
PositionData::Vector48(vector) => Vector::from(*vector),
PositionData::PositionValues(values) => values.get(frame).copied().unwrap_or_default(),
PositionData::None => Vector::default(),
}
}
fn set_scale(&mut self, scale: Vector) {
if let PositionData::PositionValues(values) = self {
values.iter_mut().for_each(|value| {
*value = Vector {
x: value.x * scale.x,
y: value.y * scale.y,
z: value.z * scale.z,
}
});
}
}
}
/// Per bone animation data
#[derive(Clone, Debug)]
pub struct Animation {
pub bone: u8,
pub flags: AnimationFlags,
rotation_data: RotationData,
position_data: PositionData,
}
impl Animation {
pub fn rotation(&self, frame: usize) -> Quaternion {
self.rotation_data.rotation(frame)
}
pub fn position(&self, frame: usize) -> Vector {
self.position_data.position(frame)
}
pub(crate) fn set_scales(&mut self, bone: &Bone) {
self.rotation_data.set_scale(bone.rot_scale);
self.position_data.set_scale(bone.pos_scale);
}
}
fn read_animation(
data: &[u8],
header_offset: usize,
frames: usize,
) -> Result<(Animation, usize), ModelError> {
let data = data
.get(header_offset..)
.ok_or_else(|| ModelError::OutOfBounds {
data: "animation data",
offset: header_offset,
})?;
let header = <AnimationHeader as Readable>::read(data)?;
let offset = size_of::<AnimationHeader>();
let rotation_data = if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWROT) {
RotationData::Quaternion48(read_single(data, offset)?)
} else if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWROT2) {
RotationData::Quaternion64(read_single(data, offset)?)
} else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMROT) {
let pointers: AnimationValuePointer = read_single(data, offset)?;
let value_data = &data[offset..];
let values = (0..frames)
.map(|frame| read_animation_values(value_data, frame, pointers))
.map(|r| r.map(|[x, y, z]| RadianEuler { x, y, z }))
.collect::<Result<_, ModelError>>()?;
RotationData::RotationValues(values)
} else {
RotationData::None
};
let position_offset = offset + rotation_data.size();
let position_data = if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWPOS) {
PositionData::Vector48(read_single(data, position_offset)?)
} else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMPOS) {
let pointers: AnimationValuePointer = read_single(data, offset)?;
let value_data = &data[offset..];
let values = (0..frames)
.map(|frame| read_animation_values(value_data, frame, pointers))
.map(|r| r.map(Vector::from))
.collect::<Result<_, ModelError>>()?;
PositionData::PositionValues(values)
} else {
PositionData::None
};
Ok((
Animation {
bone: header.bone,
flags: header.flags,
rotation_data,
position_data,
},
header.next_offset as usize,
))
}

View file

@ -1,4 +1,6 @@
use crate::{ModelError, Quaternion, RadianEuler, ReadRelative, Readable, Transform3x4, Vector}; use crate::{
read_single, ModelError, Quaternion, RadianEuler, ReadRelative, Readable, Transform3x4, Vector,
};
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
@ -58,19 +60,6 @@ impl ReadRelative for Bone {
type Header = BoneHeader; type Header = BoneHeader;
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> { fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
let name_bytes =
data.get(header.sz_name_index as usize..)
.ok_or(ModelError::OutOfBounds {
data: "bone name",
offset: header.sz_name_index as usize,
})?;
let surface_prop_bytes =
data.get(header.surface_prop_idx as usize..)
.ok_or(ModelError::OutOfBounds {
data: "bone surface property",
offset: header.surface_prop_idx as usize,
})?;
let prop_type = ProceduralBoneType::try_from(header.proc_type).ok(); let prop_type = ProceduralBoneType::try_from(header.proc_type).ok();
let proc_bytes = (header.proc_index != 0) let proc_bytes = (header.proc_index != 0)
.then(|| { .then(|| {
@ -103,7 +92,7 @@ impl ReadRelative for Bone {
.transpose()?; .transpose()?;
Ok(Bone { Ok(Bone {
name: String::read(name_bytes, ())?, name: read_single(data, header.sz_name_index)?,
parent: header.parent, parent: header.parent,
bone_controller: header.bone_controller, bone_controller: header.bone_controller,
pos: header.pos, pos: header.pos,
@ -116,7 +105,7 @@ impl ReadRelative for Bone {
flags: header.flags, flags: header.flags,
procedural_rules, procedural_rules,
physics_bone: header.physics_bone, physics_bone: header.physics_bone,
surface_prop: String::read(surface_prop_bytes, ())?, surface_prop: read_single(data, header.surface_prop_idx)?,
contents: header.contents, contents: header.contents,
}) })
} }
@ -336,17 +325,10 @@ impl ReadRelative for SourceBoneTransform {
type Header = SourceBoneTransformHeader; type Header = SourceBoneTransformHeader;
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> { fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
let name_bytes =
data.get(header.sz_name_index as usize..)
.ok_or(ModelError::OutOfBounds {
data: "source bone transform name",
offset: header.sz_name_index as usize,
})?;
Ok(SourceBoneTransform { Ok(SourceBoneTransform {
name: String::read(name_bytes, ())?, name: read_single(data, header.sz_name_index)?,
pre_transform: header.pre_transform, pre_transform: header.pre_transform,
post_transform: header.post_transform, post_transform: header.post_transform,
}) })
} }
} }

View file

@ -1,6 +1,5 @@
use crate::mdl::raw::*; use crate::mdl::raw::*;
use crate::mdl::StudioHeader2; use crate::{index_range, Vector};
use crate::{index_range, read_single, Vector};
use std::mem::size_of; use std::mem::size_of;
pub const FILETYPE_ID: i32 = i32::from_be_bytes(*b"IDST"); pub const FILETYPE_ID: i32 = i32::from_be_bytes(*b"IDST");

View file

@ -1,8 +1,8 @@
use std::mem::size_of; use crate::mdl::SourceBoneTransformHeader;
use crate::{index_range, ReadableRelative}; use crate::{index_range, ReadableRelative};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::mem::size_of;
use std::ops::Range; use std::ops::Range;
use crate::mdl::{BoneHeader, SourceBoneTransformHeader};
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)] #[repr(C)]
@ -29,7 +29,11 @@ impl ReadableRelative for StudioHeader2 {}
impl StudioHeader2 { impl StudioHeader2 {
pub fn source_bone_transforms(&self) -> impl Iterator<Item = usize> { pub fn source_bone_transforms(&self) -> impl Iterator<Item = usize> {
index_range(self.source_bone_transform_index, self.source_bone_transform_count, size_of::<SourceBoneTransformHeader>()) index_range(
self.source_bone_transform_index,
self.source_bone_transform_count,
size_of::<SourceBoneTransformHeader>(),
)
} }
pub fn bone_flex_drivers(&self) -> Range<i32> { pub fn bone_flex_drivers(&self) -> Range<i32> {

View file

@ -1,5 +1,5 @@
use crate::Vector;
use crate::{index_range, FixedString, Transform3x4}; use crate::{index_range, FixedString, Transform3x4};
use crate::{ModelError, ReadRelative, Vector};
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::mem::size_of; use std::mem::size_of;
@ -145,7 +145,11 @@ pub struct HitBoxSetHeader {
impl HitBoxSetHeader { impl HitBoxSetHeader {
pub fn hitbox_indexes(&self) -> impl Iterator<Item = usize> { pub fn hitbox_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.hitbox_offset, self.hitbox_count, size_of::<BoundingBoxHeader>()) index_range(
self.hitbox_offset,
self.hitbox_count,
size_of::<BoundingBoxHeader>(),
)
} }
} }
@ -159,4 +163,4 @@ pub struct BoundingBoxHeader {
pub bounding_box_max: Vector, pub bounding_box_max: Vector,
pub name_index: i32, pub name_index: i32,
padding: [i32; 8], padding: [i32; 8],
} }

View file

@ -6,7 +6,7 @@ use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::{Add, Mul}; use std::ops::{Add, Mul};
#[derive(Debug, Clone, Copy, Zeroable, Pod, PartialEq)] #[derive(Debug, Clone, Copy, Zeroable, Pod, PartialEq, Default)]
#[repr(C)] #[repr(C)]
pub struct Vector { pub struct Vector {
pub x: f32, pub x: f32,
@ -100,6 +100,17 @@ pub struct Quaternion {
pub w: f32, pub w: f32,
} }
impl Default for Quaternion {
fn default() -> Self {
Quaternion {
x: 1.0,
y: 0.0,
z: 0.0,
w: 0.0,
}
}
}
impl From<Quaternion> for cgmath::Quaternion<f32> { impl From<Quaternion> for cgmath::Quaternion<f32> {
fn from(q: Quaternion) -> Self { fn from(q: Quaternion) -> Self {
[q.x, q.y, q.z, q.w].into() [q.x, q.y, q.z, q.w].into()
@ -123,7 +134,7 @@ impl From<Quaternion> for cgmath::Matrix4<f32> {
} }
} }
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod, Default)]
#[repr(C)] #[repr(C)]
pub struct RadianEuler { pub struct RadianEuler {
pub x: f32, pub x: f32,
@ -244,14 +255,10 @@ impl Transform3x4 {
// mat // mat
let quat = cgmath::Quaternion::from(mat); let quat = cgmath::Quaternion::from(mat);
let euler = Euler::from(quat); let euler = Euler::from(quat);
#[cfg(debug_assertions)]
dbg!(euler);
let mapped_rotation = cgmath::Quaternion::from_angle_x(-euler.z) let mapped_rotation = cgmath::Quaternion::from_angle_x(-euler.z)
* cgmath::Quaternion::from_angle_y(euler.y) * cgmath::Quaternion::from_angle_y(euler.y)
* cgmath::Quaternion::from_angle_z(euler.x); * cgmath::Quaternion::from_angle_z(euler.x);
#[cfg(debug_assertions)]
dbg!(Euler::from(mapped_rotation));
mapped_rotation.into() mapped_rotation.into()
} }