mirror of
https://codeberg.org/icewind/vmdl.git
synced 2026-06-03 16:44:11 +02:00
bone controller and animation sequence
This commit is contained in:
parent
5e48fd2a6f
commit
06cbe395eb
4 changed files with 197 additions and 3 deletions
|
|
@ -19,6 +19,7 @@ pub struct Mdl {
|
||||||
pub header: StudioHeader,
|
pub header: StudioHeader,
|
||||||
pub header2: Option<StudioHeader2>,
|
pub header2: Option<StudioHeader2>,
|
||||||
pub bones: Vec<Bone>,
|
pub bones: Vec<Bone>,
|
||||||
|
pub bone_controllers: Vec<BoneController>,
|
||||||
pub body_parts: Vec<BodyPart>,
|
pub body_parts: Vec<BodyPart>,
|
||||||
pub textures: Vec<TextureInfo>,
|
pub textures: Vec<TextureInfo>,
|
||||||
pub texture_paths: Vec<String>,
|
pub texture_paths: Vec<String>,
|
||||||
|
|
@ -28,6 +29,7 @@ pub struct Mdl {
|
||||||
pub local_animations: Vec<AnimationDescription>,
|
pub local_animations: Vec<AnimationDescription>,
|
||||||
pub animation_block_source: String,
|
pub animation_block_source: String,
|
||||||
pub animation_blocks: Vec<AnimationBlock>,
|
pub animation_blocks: Vec<AnimationBlock>,
|
||||||
|
pub animation_sequences: Vec<AnimationSequence>,
|
||||||
pub pose_parameters: Vec<PoseParameterDescription>,
|
pub pose_parameters: Vec<PoseParameterDescription>,
|
||||||
pub attachments: Vec<StudioAttachment>,
|
pub attachments: Vec<StudioAttachment>,
|
||||||
pub hit_boxes: Vec<HitBoxSet>,
|
pub hit_boxes: Vec<HitBoxSet>,
|
||||||
|
|
@ -57,6 +59,7 @@ impl Mdl {
|
||||||
|
|
||||||
let skin_table = read_relative::<u16, _>(data, header.skin_reference_indexes())?;
|
let skin_table = read_relative::<u16, _>(data, header.skin_reference_indexes())?;
|
||||||
let bones = read_relative(data, header.bone_indexes())?;
|
let bones = read_relative(data, header.bone_indexes())?;
|
||||||
|
let bone_controllers = read_relative(data, header.bone_controller_indexes())?;
|
||||||
|
|
||||||
let surface_prop = read_single(data, header.surface_prop_index)?;
|
let surface_prop = read_single(data, header.surface_prop_index)?;
|
||||||
let key_values = (header.key_value_size > 0)
|
let key_values = (header.key_value_size > 0)
|
||||||
|
|
@ -74,6 +77,11 @@ impl Mdl {
|
||||||
});
|
});
|
||||||
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())?;
|
||||||
|
let mut animation_sequences: Vec<AnimationSequence> =
|
||||||
|
read_relative(data, header.animation_sequence_indexes())?;
|
||||||
|
animation_sequences
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|seq| seq.bone_weights.truncate(bones.len()));
|
||||||
|
|
||||||
let pose_parameters = read_relative(data, header.local_pose_param_indexes())?;
|
let pose_parameters = read_relative(data, header.local_pose_param_indexes())?;
|
||||||
let attachments = read_relative(data, header.attachment_indexes())?;
|
let attachments = read_relative(data, header.attachment_indexes())?;
|
||||||
|
|
@ -82,6 +90,7 @@ impl Mdl {
|
||||||
Ok(Mdl {
|
Ok(Mdl {
|
||||||
name,
|
name,
|
||||||
bones,
|
bones,
|
||||||
|
bone_controllers,
|
||||||
body_parts: header
|
body_parts: header
|
||||||
.body_part_indexes()
|
.body_part_indexes()
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
|
|
@ -104,6 +113,7 @@ impl Mdl {
|
||||||
local_animations,
|
local_animations,
|
||||||
animation_block_source,
|
animation_block_source,
|
||||||
animation_blocks,
|
animation_blocks,
|
||||||
|
animation_sequences,
|
||||||
attachments,
|
attachments,
|
||||||
hit_boxes,
|
hit_boxes,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48};
|
use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48};
|
||||||
use crate::mdl::Bone;
|
use crate::mdl::Bone;
|
||||||
use crate::{
|
use crate::{
|
||||||
read_single, ModelError, Quaternion, RadianEuler, ReadRelative, Readable, ReadableRelative,
|
index_range, read_relative, read_single, ModelError, Quaternion, RadianEuler, ReadRelative,
|
||||||
Vector,
|
Readable, ReadableRelative, Vector,
|
||||||
};
|
};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
@ -383,3 +383,109 @@ fn read_animation(
|
||||||
header.next_offset as usize,
|
header.next_offset as usize,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Zeroable, Pod, Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AnimationSequenceHeader {
|
||||||
|
base: i32,
|
||||||
|
label_index: i32,
|
||||||
|
activity_name_index: i32,
|
||||||
|
flags: i32, // todo
|
||||||
|
activity: i32,
|
||||||
|
weight: i32,
|
||||||
|
event_count: i32,
|
||||||
|
event_offset: i32,
|
||||||
|
bounding_box_min: Vector,
|
||||||
|
bounding_box_max: Vector,
|
||||||
|
blend_count: i32,
|
||||||
|
animation_index_index: i32,
|
||||||
|
movement_index: i32,
|
||||||
|
group_size: [i32; 2],
|
||||||
|
param_index: [i32; 2],
|
||||||
|
param_start: [i32; 2],
|
||||||
|
param_end: [i32; 2],
|
||||||
|
param_parent: i32,
|
||||||
|
|
||||||
|
fade_in_time: f32,
|
||||||
|
fade_out_time: f32,
|
||||||
|
|
||||||
|
local_entry_node: i32,
|
||||||
|
local_exit_node: i32,
|
||||||
|
node_flags: i32,
|
||||||
|
|
||||||
|
entry_phase: f32,
|
||||||
|
exit_phase: f32,
|
||||||
|
|
||||||
|
last_frame: f32,
|
||||||
|
|
||||||
|
next_sequence: i32,
|
||||||
|
pose: i32,
|
||||||
|
|
||||||
|
ik_rule_count: i32,
|
||||||
|
|
||||||
|
auto_layer_count: i32,
|
||||||
|
auto_layer_offset: i32,
|
||||||
|
|
||||||
|
weight_list_offset: i32,
|
||||||
|
|
||||||
|
pose_key_offset: i32,
|
||||||
|
|
||||||
|
ik_lock_count: i32,
|
||||||
|
ik_lock_offset: i32,
|
||||||
|
|
||||||
|
key_value_offset: i32,
|
||||||
|
key_value_size: i32,
|
||||||
|
|
||||||
|
cycle_pose_offset: i32,
|
||||||
|
|
||||||
|
activity_modifiers_offset: i32,
|
||||||
|
activity_modifiers_count: i32,
|
||||||
|
|
||||||
|
_padding: [i32; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimationSequenceHeader {
|
||||||
|
fn bone_weight_indices(&self) -> impl Iterator<Item = usize> {
|
||||||
|
// weight/bone count isn't stored here, so we assume the next indexed values is stored after it in the file
|
||||||
|
// we trim down the list of weights later
|
||||||
|
let other_indices = [
|
||||||
|
self.pose_key_offset,
|
||||||
|
self.ik_lock_offset,
|
||||||
|
self.key_value_offset,
|
||||||
|
self.activity_modifiers_offset,
|
||||||
|
];
|
||||||
|
let weight_count = if let Some(next_index) = other_indices
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.find(|index| *index > self.weight_list_offset)
|
||||||
|
{
|
||||||
|
(next_index - self.weight_list_offset) as usize / size_of::<f32>()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
index_range(
|
||||||
|
self.weight_list_offset,
|
||||||
|
weight_count as i32,
|
||||||
|
size_of::<f32>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AnimationSequence {
|
||||||
|
pub name: String,
|
||||||
|
pub label: String,
|
||||||
|
pub bone_weights: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadRelative for AnimationSequence {
|
||||||
|
type Header = AnimationSequenceHeader;
|
||||||
|
|
||||||
|
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
|
||||||
|
Ok(AnimationSequence {
|
||||||
|
name: read_single(data, header.activity_name_index)?,
|
||||||
|
label: read_single(data, header.label_index)?,
|
||||||
|
bone_weights: read_relative(data, header.bone_weight_indices())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,3 +332,69 @@ impl ReadRelative for SourceBoneTransform {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BoneControllerHeader {
|
||||||
|
bone: i32, // -1 == 0
|
||||||
|
ty: i32, // X, Y, Z, XR, YR, ZR, M
|
||||||
|
start: f32,
|
||||||
|
end: f32,
|
||||||
|
rest: i32,
|
||||||
|
input_field: i32,
|
||||||
|
_padding: [i32; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum BoneControllerType {
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
Z,
|
||||||
|
XR,
|
||||||
|
YR,
|
||||||
|
ZR,
|
||||||
|
M,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<i32> for BoneControllerType {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::X),
|
||||||
|
1 => Ok(Self::Y),
|
||||||
|
2 => Ok(Self::Z),
|
||||||
|
3 => Ok(Self::XR),
|
||||||
|
4 => Ok(Self::YR),
|
||||||
|
5 => Ok(Self::ZR),
|
||||||
|
6 => Ok(Self::M),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BoneController {
|
||||||
|
pub bone: i32,
|
||||||
|
pub ty: BoneControllerType,
|
||||||
|
pub start: f32,
|
||||||
|
pub end: f32,
|
||||||
|
pub rest: i32,
|
||||||
|
pub input_field: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadRelative for BoneController {
|
||||||
|
type Header = BoneControllerHeader;
|
||||||
|
|
||||||
|
fn read(_data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
|
||||||
|
Ok(BoneController {
|
||||||
|
bone: header.bone,
|
||||||
|
ty: header.ty.try_into().unwrap_or(BoneControllerType::X),
|
||||||
|
start: header.start,
|
||||||
|
end: header.end,
|
||||||
|
rest: header.rest,
|
||||||
|
input_field: header.input_field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,11 @@ impl StudioHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bone_controller_indexes(&self) -> impl Iterator<Item = usize> {
|
pub fn bone_controller_indexes(&self) -> impl Iterator<Item = usize> {
|
||||||
index_range(self.bone_controller_offset, self.bone_controller_count, 1)
|
index_range(
|
||||||
|
self.bone_controller_offset,
|
||||||
|
self.bone_controller_count,
|
||||||
|
size_of::<BoneControllerHeader>(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hitbox_set_indexes(&self) -> impl Iterator<Item = usize> {
|
pub fn hitbox_set_indexes(&self) -> impl Iterator<Item = usize> {
|
||||||
|
|
@ -327,6 +331,14 @@ impl StudioHeader {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn animation_sequence_indexes(&self) -> impl Iterator<Item = usize> {
|
||||||
|
index_range(
|
||||||
|
self.local_seq_offset,
|
||||||
|
self.local_seq_count,
|
||||||
|
size_of::<AnimationSequenceHeader>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn flex_controller_ui_indexes(&self) -> impl Iterator<Item = usize> {
|
pub fn flex_controller_ui_indexes(&self) -> impl Iterator<Item = usize> {
|
||||||
index_range(
|
index_range(
|
||||||
self.flex_controller_ui_index,
|
self.flex_controller_ui_index,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue