1use crate::compressed_vector::{Quaternion48, Quaternion64, Vector48};
2use crate::mdl::Bone;
3use crate::{
4 index_range, read_relative, read_single, ModelError, Quaternion, RadianEuler, ReadRelative,
5 Readable, ReadableRelative, Vector,
6};
7use bitflags::bitflags;
8use bytemuck::{Pod, Zeroable};
9use cgmath::{Matrix4, SquareMatrix};
10use std::mem::size_of;
11
12#[derive(Debug, Clone, Copy, Zeroable, Pod)]
13#[repr(C)]
14pub struct PoseParameterDescriptionHeader {
15 name_index: i32,
16 flags: i32,
17 start: f32,
18 end: f32,
19 loop_range: f32,
20}
21
22static_assertions::const_assert_eq!(size_of::<PoseParameterDescriptionHeader>(), 20);
23
24#[derive(Clone, Debug)]
25pub struct PoseParameterDescription {
26 pub name: String,
27 pub flags: i32,
28 pub start: f32,
29 pub end: f32,
30 pub loop_range: f32,
31}
32
33impl ReadRelative for PoseParameterDescription {
34 type Header = PoseParameterDescriptionHeader;
35
36 fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
37 Ok(PoseParameterDescription {
38 name: read_single(data, header.name_index)?,
39 flags: header.flags,
40 start: header.start,
41 end: header.end,
42 loop_range: header.loop_range,
43 })
44 }
45}
46
47#[derive(Debug, Clone, Copy, Zeroable, Pod)]
48#[repr(C)]
49pub struct AnimationDescriptionHeader {
50 base_ptr: i32,
51 name_offset: i32,
52 fps: f32,
53 flags: i32,
54
55 frame_count: i32,
56
57 movement_count: i32,
58 movement_offset: i32,
59
60 _padding: [i32; 6],
61
62 animation_block: i32,
63 animation_index: i32, ik_rule_count: i32,
66 ik_rule_offset: i32,
67 animation_block_ik_rule_index: i32,
68
69 local_hierarchy_count: i32,
70 local_hierarchy_offset: i32,
71
72 section_offset: i32,
73 section_frames: i32,
74
75 zero_frame_span: i16,
76 zero_frame_count: i16,
77 zero_frame_offset: i32,
78
79 zero_frame_stall_time: f32,
80}
81
82static_assertions::const_assert_eq!(size_of::<AnimationDescriptionHeader>(), 100);
83
84#[derive(Clone, Debug)]
85pub struct AnimationDescription {
86 pub name: String,
87 pub fps: f32,
88 pub frame_count: usize,
89 pub animations: Vec<Animation>,
90}
91
92impl AnimationDescription {
93 pub fn get_bone_transform(&self, bone: u8, frame: usize) -> Matrix4<f32> {
94 let Some(animation) = self.animations.iter().find(|anim| anim.bone == bone) else {
95 return Matrix4::identity();
96 };
97 Matrix4::from_translation(animation.position(frame).into())
98 * Matrix4::from(animation.rotation(frame))
99 }
100}
101
102impl ReadRelative for AnimationDescription {
103 type Header = AnimationDescriptionHeader;
104
105 fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
106 let mut animations = Vec::with_capacity(1);
107 let mut offset = header.animation_index as usize;
108 loop {
109 let (animation, next_offset) = if header.animation_block == 0 {
110 read_animation(data, offset, header.frame_count as usize)?
111 } else {
112 todo!("read animation from animation block");
113 };
114 animations.push(animation);
115 if next_offset == 0 {
116 break;
117 }
118 offset += next_offset;
119 }
120
121 Ok(AnimationDescription {
122 name: read_single(data, header.name_offset)?,
123 fps: header.fps,
124 frame_count: header.frame_count as usize,
125 animations,
126 })
127 }
128}
129
130#[derive(Debug, Clone, Copy, Zeroable, Pod)]
131#[repr(C)]
132pub struct AnimationBlock {
133 start: i32,
134 end: i32,
135}
136
137impl ReadableRelative for AnimationBlock {}
138
139#[derive(Debug, Clone, Copy, Zeroable, Pod)]
140#[repr(C)]
141pub struct AnimationHeader {
142 bone: u8,
143 flags: AnimationFlags,
144 next_offset: u16,
145}
146
147#[derive(Zeroable, Pod, Copy, Clone, Debug)]
148#[repr(C)]
149pub struct AnimationFlags(u8);
150
151bitflags! {
152 impl AnimationFlags: u8 {
153 const STUDIO_ANIM_RAWPOS = 0x00000001;
155 const STUDIO_ANIM_RAWROT = 0x00000002;
157 const STUDIO_ANIM_ANIMPOS = 0x00000004;
159 const STUDIO_ANIM_ANIMROT = 0x00000008;
161 const STUDIO_ANIM_DELTA = 0x00000010;
162 const STUDIO_ANIM_RAWROT2 = 0x00000020;
164 }
165}
166
167#[derive(Zeroable, Pod, Copy, Clone, Debug)]
168#[repr(C)]
169struct AnimationValuePointer([u16; 3]);
170impl ReadableRelative for AnimationValuePointer {}
171
172#[derive(Zeroable, Pod, Copy, Clone, Debug, Default)]
173#[repr(C)]
174struct ValueHeader {
175 valid: u8,
176 total: u8,
177}
178impl ReadableRelative for ValueHeader {}
179
180fn read_animation_values(
181 data: &[u8], frame: usize,
183 base_pointers: AnimationValuePointer,
184) -> Result<[f32; 3], ModelError> {
185 let mut result = [0.0; 3];
186 for (out, base_pointer) in result.iter_mut().zip(base_pointers.0) {
187 if base_pointer == 0 {
188 *out = 0.0;
189 } else {
190 let header: ValueHeader = read_single(data, base_pointer)?;
191 let values = FrameValues {
192 header,
193 data: &data[base_pointer as usize..],
194 };
195 *out = values.get(frame as u8).map(|val| val as f32)?;
196 }
197 }
198 Ok(result)
199}
200
201struct FrameValues<'a> {
213 header: ValueHeader,
214 data: &'a [u8], }
216
217impl<'a> FrameValues<'a> {
218 pub fn get(&self, index: u8) -> Result<u16, ModelError> {
219 if self.header.total <= index {
220 let offset_count = self.header.valid + 1;
221 let offset = (offset_count as usize) * size_of::<u16>();
222 let next_header: ValueHeader = read_single(self.data, offset)?;
223 let next = FrameValues {
224 header: next_header,
225 data: &self.data[offset..],
226 };
227 if next_header.total == 0 {
228 return Ok(0);
229 }
230 next.get(index - self.header.total)
231 } else {
232 let offset_count = if self.header.valid > index {
233 index + 1
234 } else {
235 self.header.valid
236 };
237 let offset = (offset_count as usize) * size_of::<u16>();
238 read_single(self.data, offset)
239 }
240 }
241}
242
243#[derive(Clone, Debug)]
244pub enum RotationData {
245 Quaternion48(Quaternion),
246 Quaternion64(Quaternion),
247 Animated(Vec<RadianEuler>),
248 None,
249}
250
251impl From<Quaternion48> for RotationData {
252 fn from(value: Quaternion48) -> Self {
253 let q = Quaternion::from(value);
254 RotationData::Quaternion48(q)
255 }
256}
257
258impl From<Quaternion64> for RotationData {
259 fn from(value: Quaternion64) -> Self {
260 let q = Quaternion::from(value);
261 RotationData::Quaternion64(q)
262 }
263}
264
265impl From<Vec<RadianEuler>> for RotationData {
266 fn from(value: Vec<RadianEuler>) -> Self {
267 RotationData::Animated(value)
269 }
270}
271
272impl RotationData {
273 pub fn rotation(&self, frame: usize) -> Quaternion {
274 match self {
275 RotationData::Quaternion48(q) => *q,
276 RotationData::Quaternion64(q) => *q,
277 RotationData::Animated(values) => values
278 .get(frame)
279 .copied()
280 .unwrap_or_else(|| values.last().copied().unwrap_or_default())
281 .into(),
282 RotationData::None => Quaternion::default(),
283 }
284 }
285
286 pub fn size(&self) -> usize {
287 match self {
288 RotationData::Quaternion48(_) => size_of::<Quaternion48>(),
289 RotationData::Quaternion64(_) => size_of::<Quaternion64>(),
290 RotationData::Animated(_) => size_of::<AnimationValuePointer>(),
291 RotationData::None => 0,
292 }
293 }
294
295 fn set_scale(&mut self, scale: Vector) {
296 if let RotationData::Animated(values) = self {
297 values.iter_mut().for_each(|value| {
298 *value = RadianEuler {
300 y: value.x * scale.x,
301 z: value.y * scale.y,
302 x: value.z * scale.z,
303 }
304 });
305 }
306 }
307}
308
309#[derive(Clone, Debug)]
310pub enum PositionData {
311 Vector48(Vector48),
312 PositionValues(Vec<Vector>),
313 None,
314}
315
316impl PositionData {
317 pub fn position(&self, frame: usize) -> Vector {
318 match self {
319 PositionData::Vector48(vector) => Vector::from(*vector),
320 PositionData::PositionValues(values) => values.get(frame).copied().unwrap_or_default(),
321 PositionData::None => Vector::default(),
322 }
323 }
324
325 fn set_scale(&mut self, scale: Vector) {
326 if let PositionData::PositionValues(values) = self {
327 values.iter_mut().for_each(|value| {
328 *value = Vector {
329 x: value.x * scale.x,
330 y: value.y * scale.y,
331 z: value.z * scale.z,
332 }
333 });
334 }
335 }
336}
337
338#[derive(Clone, Debug)]
340pub struct Animation {
341 pub bone: u8,
342 pub flags: AnimationFlags,
343 rotation_data: RotationData,
344 position_data: PositionData,
345}
346
347impl Animation {
348 pub fn rotation(&self, frame: usize) -> Quaternion {
349 self.rotation_data.rotation(frame)
350 }
351
352 pub(crate) fn rotation_looks_valid(&self) -> bool {
353 true
354 }
355
356 pub fn position(&self, frame: usize) -> Vector {
357 self.position_data.position(frame)
358 }
359
360 pub(crate) fn set_scales(&mut self, bone: &Bone) {
361 self.rotation_data.set_scale(bone.rot_scale);
362 self.position_data.set_scale(bone.pos_scale);
363 }
364}
365
366fn read_animation(
367 data: &[u8],
368 header_offset: usize,
369 frames: usize,
370) -> Result<(Animation, usize), ModelError> {
371 let data = data
372 .get(header_offset..)
373 .ok_or_else(|| ModelError::OutOfBounds {
374 data: "animation data",
375 offset: header_offset,
376 })?;
377 let header = <AnimationHeader as Readable>::read(data)?;
378
379 let offset = size_of::<AnimationHeader>();
380
381 let rotation_data = if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWROT) {
382 RotationData::from(read_single::<Quaternion48, _>(data, offset)?)
383 } else if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWROT2) {
384 RotationData::from(read_single::<Quaternion64, _>(data, offset)?)
385 } else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMROT) {
386 let pointers: AnimationValuePointer = read_single(data, offset)?;
387 let value_data = &data[offset..];
388 let values: Vec<RadianEuler> = (0..frames)
389 .map(|frame| read_animation_values(value_data, frame, pointers))
390 .map(|r| r.map(|[x, y, z]| RadianEuler { x, z, y }))
391 .collect::<Result<_, ModelError>>()?;
392 RotationData::from(values)
393 } else {
394 RotationData::None
395 };
396
397 let position_offset = offset + rotation_data.size();
398 let position_data = if header.flags.contains(AnimationFlags::STUDIO_ANIM_RAWPOS) {
399 PositionData::Vector48(read_single(data, position_offset)?)
400 } else if header.flags.contains(AnimationFlags::STUDIO_ANIM_ANIMPOS) {
401 let pointers: AnimationValuePointer = read_single(data, position_offset)?;
402 let value_data = &data[position_offset..];
403 let values = (0..frames)
404 .map(|frame| read_animation_values(value_data, frame, pointers))
405 .map(|r| r.map(Vector::from))
406 .collect::<Result<_, ModelError>>()?;
407 PositionData::PositionValues(values)
408 } else {
409 PositionData::None
410 };
411
412 Ok((
413 Animation {
414 bone: header.bone,
415 flags: header.flags,
416 rotation_data,
417 position_data,
418 },
419 header.next_offset as usize,
420 ))
421}
422
423#[derive(Zeroable, Pod, Copy, Clone, Debug, Default)]
424#[repr(C)]
425pub struct AnimationSequenceHeader {
426 base: i32,
427 label_index: i32,
428 activity_name_index: i32,
429 flags: i32, activity: i32,
431 weight: i32,
432 event_count: i32,
433 event_offset: i32,
434 bounding_box_min: Vector,
435 bounding_box_max: Vector,
436 blend_count: i32,
437 animation_index_index: i32,
438 movement_index: i32,
439 group_size: [i32; 2],
440 param_index: [i32; 2],
441 param_start: [i32; 2],
442 param_end: [i32; 2],
443 param_parent: i32,
444
445 fade_in_time: f32,
446 fade_out_time: f32,
447
448 local_entry_node: i32,
449 local_exit_node: i32,
450 node_flags: i32,
451
452 entry_phase: f32,
453 exit_phase: f32,
454
455 last_frame: f32,
456
457 next_sequence: i32,
458 pose: i32,
459
460 ik_rule_count: i32,
461
462 auto_layer_count: i32,
463 auto_layer_offset: i32,
464
465 weight_list_offset: i32,
466
467 pose_key_offset: i32,
468
469 ik_lock_count: i32,
470 ik_lock_offset: i32,
471
472 key_value_offset: i32,
473 key_value_size: i32,
474
475 cycle_pose_offset: i32,
476
477 activity_modifiers_offset: i32,
478 activity_modifiers_count: i32,
479
480 _padding: [i32; 5],
481}
482
483impl AnimationSequenceHeader {
484 fn bone_weight_indices(&self) -> impl Iterator<Item = usize> {
485 let other_indices = [
488 self.pose_key_offset,
489 self.ik_lock_offset,
490 self.key_value_offset,
491 self.activity_modifiers_offset,
492 ];
493 let weight_count = if let Some(next_index) = other_indices
494 .iter()
495 .copied()
496 .find(|index| *index > self.weight_list_offset)
497 {
498 (next_index - self.weight_list_offset) as usize / size_of::<f32>()
499 } else {
500 0
501 };
502 index_range(
503 self.weight_list_offset,
504 weight_count as i32,
505 size_of::<f32>(),
506 )
507 }
508}
509
510#[derive(Debug, Clone)]
511pub struct AnimationSequence {
512 pub name: String,
513 pub label: String,
514 pub bone_weights: Vec<f32>,
515}
516
517impl ReadRelative for AnimationSequence {
518 type Header = AnimationSequenceHeader;
519
520 fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
521 Ok(AnimationSequence {
522 name: read_single(data, header.activity_name_index)?,
523 label: read_single(data, header.label_index)?,
524 bone_weights: read_relative(data, header.bone_weight_indices())?,
525 })
526 }
527}