This commit is contained in:
Robin Appelman 2025-06-02 21:05:00 +02:00
commit 4722ce296e
4 changed files with 41 additions and 42 deletions

View file

@ -6,7 +6,8 @@ Rust parser for source engine model files (`.mdl`, `.vtx`, `.vvd`)
## Examples ## Examples
All examples require the `.mdl`, `.vvd` and `.dx90.vtx` to be next to each other and TF2 to be installed to load the texture data from. All examples require the `.mdl`, `.vvd` and `.dx90.vtx` to be next to each other
and TF2 to be installed to load the texture data from.
## Viewer ## Viewer

View file

@ -1,6 +1,7 @@
use cgmath::Matrix4; use cgmath::Matrix4;
use std::env::args; use std::env::args;
use std::fs; use std::fs;
use std::hint::black_box;
use std::path::PathBuf; use std::path::PathBuf;
use vmdl::mdl::Mdl; use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx; use vmdl::vtx::Vtx;
@ -19,35 +20,28 @@ fn main() -> Result<(), vmdl::ModelError> {
let data = fs::read(path.with_extension("vvd"))?; let data = fs::read(path.with_extension("vvd"))?;
let vvd = Vvd::read(&data)?; let vvd = Vvd::read(&data)?;
for bone in &mdl.bones { // for bone in &mdl.bones {
// println!(
// "{}: from {} at\n\t{:?}\n\t{:?}\n\t{:?}\n\t{:?}",
// bone.name, bone.parent, bone.rot, bone.rot_scale, bone.quaternion, bone.pose_to_bone
// );
// }
for animation_desc in mdl.local_animations.iter() {
println!( println!(
"{}: from {} at\n\t{:?}\n\t{:?}\n\t{:?}\n\t{:?}", "{}: {} frames at {}fps",
bone.name, bone.parent, bone.rot, bone.rot_scale, bone.quaternion, bone.pose_to_bone animation_desc.name, animation_desc.frame_count, animation_desc.fps,
);
for animation in &animation_desc.animations.first() {
dbg!(animation.flags);
println!(
"\tbone {:.2} frame 0:\n\t\ttrans: {:?}\n\t\tpos: {:?}",
animation.bone,
animation.rotation(0),
animation.translation(0),
); );
} }
}
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);

View file

@ -4,19 +4,16 @@
nixpkgs.url = "nixpkgs/release-22.11"; nixpkgs.url = "nixpkgs/release-22.11";
}; };
outputs = outputs = {
{ self self,
, nixpkgs nixpkgs,
, utils utils,
,
}: }:
utils.lib.eachDefaultSystem (system: utils.lib.eachDefaultSystem (system: let
let
pkgs = (import nixpkgs) { pkgs = (import nixpkgs) {
inherit system; inherit system;
}; };
in in rec {
rec {
# `nix develop` # `nix develop`
devShell = pkgs.mkShell { devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [rustup cargo-edit cargo-fuzz]; nativeBuildInputs = with pkgs; [rustup cargo-edit cargo-fuzz];

View file

@ -94,6 +94,7 @@ 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> {
dbg!(header);
let mut animations = Vec::with_capacity(1); let mut animations = Vec::with_capacity(1);
let mut offset = header.animation_index as usize; let mut offset = header.animation_index as usize;
loop { loop {
@ -186,6 +187,7 @@ impl ReadableRelative for ValueHeader {}
fn read_animation_values( fn read_animation_values(
frame: usize, frame: usize,
animation_value_pointers: AnimationValuePointers, animation_value_pointers: AnimationValuePointers,
debug: bool,
) -> Result<[f32; 3], ModelError> { ) -> Result<[f32; 3], ModelError> {
let [x, y, z] = animation_value_pointers let [x, y, z] = animation_value_pointers
.offsets .offsets
@ -194,7 +196,10 @@ fn read_animation_values(
Ok(0) Ok(0)
} else { } else {
let values: FrameValues = read_single(animation_value_pointers.data, offset)?; let values: FrameValues = read_single(animation_value_pointers.data, offset)?;
Ok(values.get(frame as u8)?) if debug {
println!("frame {frame}");
}
Ok(values.get(frame as u8, debug)?)
} }
}); });
let [x, y, z] = [x?, y?, z?]; let [x, y, z] = [x?, y?, z?];
@ -279,7 +284,6 @@ impl From<Quaternion64> for RotationData {
impl From<Vec<RadianEuler>> for RotationData { impl From<Vec<RadianEuler>> for RotationData {
fn from(value: Vec<RadianEuler>) -> Self { fn from(value: Vec<RadianEuler>) -> Self {
// axis get fixed up when applying the scale
RotationData::Animated(value) RotationData::Animated(value)
} }
} }
@ -292,6 +296,7 @@ impl RotationData {
RotationData::Animated(values) => values RotationData::Animated(values) => values
.get(frame) .get(frame)
.copied() .copied()
.inspect(|v| println!("{:?}", v))
.unwrap_or_else(|| values.last().copied().unwrap_or_default()) .unwrap_or_else(|| values.last().copied().unwrap_or_default())
.into(), .into(),
RotationData::None => Quaternion::default(), RotationData::None => Quaternion::default(),
@ -408,6 +413,7 @@ fn read_animation(
offset: header_offset, offset: header_offset,
})?; })?;
let header = <AnimationHeader as Readable>::read(data)?; let header = <AnimationHeader as Readable>::read(data)?;
dbg!(header);
let offset = size_of::<AnimationHeader>(); let offset = size_of::<AnimationHeader>();
@ -417,8 +423,9 @@ fn read_animation(
RotationData::from(read_single::<Quaternion64, _>(data, offset)?) RotationData::from(read_single::<Quaternion64, _>(data, offset)?)
} else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMROT) { } else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMROT) {
let pointers: AnimationValuePointers = read_single(data, offset)?; let pointers: AnimationValuePointers = read_single(data, offset)?;
println!("bone: {}", header.bone);
let values: Vec<RadianEuler> = (0..frames) let values: Vec<RadianEuler> = (0..frames)
.map(|frame| read_animation_values(frame, pointers)) .map(|frame| read_animation_values(frame, pointers, header.bone == 0))
.map_ok(|[pitch, yaw, roll]| RadianEuler { pitch, yaw, roll }) .map_ok(|[pitch, yaw, roll]| RadianEuler { pitch, yaw, roll })
.collect::<Result<_, ModelError>>()?; .collect::<Result<_, ModelError>>()?;
RotationData::from(values) RotationData::from(values)
@ -432,7 +439,7 @@ fn read_animation(
} else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMPOS) { } else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMPOS) {
let pointers: AnimationValuePointers = read_single(data, position_offset)?; let pointers: AnimationValuePointers = read_single(data, position_offset)?;
let values = (0..frames) let values = (0..frames)
.map(|frame| read_animation_values(frame, pointers)) .map(|frame| read_animation_values(frame, pointers, false))
.map_ok(Vector::from) .map_ok(Vector::from)
.collect::<Result<_, ModelError>>()?; .collect::<Result<_, ModelError>>()?;
PositionData::PositionValues(values) PositionData::PositionValues(values)