wow_m2/chunks/
m2_track_resolver.rs1use crate::{
2 Result,
3 chunks::m2_track::{M2CompQuat, M2Track, M2TrackQuat, M2TrackVec3},
4 common::{C3Vector, read_array},
5 io_ext::ReadExt,
6};
7use std::io::{Read, Seek};
8
9pub struct M2TrackResolver;
12
13impl M2TrackResolver {
14 pub fn resolve_timestamps<R: Read + Seek>(
16 reader: &mut R,
17 track: &M2Track<impl Clone>,
18 ) -> Result<Vec<u32>> {
19 if track.timestamps.is_empty() {
20 return Ok(Vec::new());
21 }
22
23 read_array(reader, &track.timestamps, |r| Ok(r.read_u32_le()?))
24 }
25
26 pub fn resolve_vec3_values<R: Read + Seek>(
28 reader: &mut R,
29 track: &M2TrackVec3,
30 ) -> Result<Vec<C3Vector>> {
31 if track.values.is_empty() {
32 return Ok(Vec::new());
33 }
34
35 read_array(reader, &track.values, |r| C3Vector::parse(r))
36 }
37
38 pub fn resolve_quat_values<R: Read + Seek>(
40 reader: &mut R,
41 track: &M2TrackQuat,
42 ) -> Result<Vec<M2CompQuat>> {
43 if track.values.is_empty() {
44 return Ok(Vec::new());
45 }
46
47 read_array(reader, &track.values, |r| M2CompQuat::parse(r))
48 }
49
50 pub fn resolve_ranges<R: Read + Seek>(
53 reader: &mut R,
54 track: &M2Track<impl Clone>,
55 ) -> Result<Option<Vec<u32>>> {
56 if let Some(ref ranges) = track.ranges {
57 if ranges.is_empty() {
58 return Ok(Some(Vec::new()));
59 }
60
61 let range_data = read_array(reader, ranges, |r| Ok(r.read_u32_le()?))?;
63
64 Ok(Some(range_data))
65 } else {
66 Ok(None)
67 }
68 }
69}
70
71type Vec3TrackData = (Vec<u32>, Vec<C3Vector>, Option<Vec<u32>>);
73type QuatTrackData = (Vec<u32>, Vec<M2CompQuat>, Option<Vec<u32>>);
74
75pub trait M2TrackVec3Ext {
77 fn resolve_data<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec3TrackData>;
79}
80
81impl M2TrackVec3Ext for M2TrackVec3 {
82 fn resolve_data<R: Read + Seek>(&self, reader: &mut R) -> Result<Vec3TrackData> {
83 let timestamps = M2TrackResolver::resolve_timestamps(reader, self)?;
84 let values = M2TrackResolver::resolve_vec3_values(reader, self)?;
85 let ranges = M2TrackResolver::resolve_ranges(reader, self)?;
86 Ok((timestamps, values, ranges))
87 }
88}
89
90pub trait M2TrackQuatExt {
92 fn resolve_data<R: Read + Seek>(&self, reader: &mut R) -> Result<QuatTrackData>;
94}
95
96impl M2TrackQuatExt for M2TrackQuat {
97 fn resolve_data<R: Read + Seek>(&self, reader: &mut R) -> Result<QuatTrackData> {
98 let timestamps = M2TrackResolver::resolve_timestamps(reader, self)?;
99 let values = M2TrackResolver::resolve_quat_values(reader, self)?;
100 let ranges = M2TrackResolver::resolve_ranges(reader, self)?;
101 Ok((timestamps, values, ranges))
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::chunks::m2_track::M2TrackBase;
109 use crate::common::M2Array;
110 use std::io::Cursor;
111
112 #[test]
113 fn test_resolve_empty_track() {
114 let track = M2TrackVec3 {
115 base: M2TrackBase {
116 interpolation_type: crate::chunks::animation::M2InterpolationType::None,
117 global_sequence: 0xFFFF,
118 },
119 ranges: None,
120 timestamps: M2Array::new(0, 0),
121 values: M2Array::new(0, 0),
122 };
123
124 let data = vec![0u8; 100];
125 let mut cursor = Cursor::new(data);
126
127 let (timestamps, values, ranges) = track.resolve_data(&mut cursor).unwrap();
128 assert!(timestamps.is_empty());
129 assert!(values.is_empty());
130 assert_eq!(ranges, None);
131 }
132
133 #[test]
134 fn test_resolve_track_with_data() {
135 let mut data = vec![0u8; 1000];
137
138 let timestamp_offset = 100;
140 data[timestamp_offset..timestamp_offset + 4].copy_from_slice(&10u32.to_le_bytes());
141 data[timestamp_offset + 4..timestamp_offset + 8].copy_from_slice(&20u32.to_le_bytes());
142 data[timestamp_offset + 8..timestamp_offset + 12].copy_from_slice(&30u32.to_le_bytes());
143
144 let value_offset = 200;
146 data[value_offset..value_offset + 4].copy_from_slice(&1.0f32.to_le_bytes());
147 data[value_offset + 4..value_offset + 8].copy_from_slice(&2.0f32.to_le_bytes());
148 data[value_offset + 8..value_offset + 12].copy_from_slice(&3.0f32.to_le_bytes());
149
150 data[value_offset + 12..value_offset + 16].copy_from_slice(&4.0f32.to_le_bytes());
151 data[value_offset + 16..value_offset + 20].copy_from_slice(&5.0f32.to_le_bytes());
152 data[value_offset + 20..value_offset + 24].copy_from_slice(&6.0f32.to_le_bytes());
153
154 data[value_offset + 24..value_offset + 28].copy_from_slice(&7.0f32.to_le_bytes());
155 data[value_offset + 28..value_offset + 32].copy_from_slice(&8.0f32.to_le_bytes());
156 data[value_offset + 32..value_offset + 36].copy_from_slice(&9.0f32.to_le_bytes());
157
158 let track = M2TrackVec3 {
159 base: M2TrackBase {
160 interpolation_type: crate::chunks::animation::M2InterpolationType::None,
161 global_sequence: 0xFFFF,
162 },
163 ranges: None,
164 timestamps: M2Array::new(3, timestamp_offset as u32),
165 values: M2Array::new(3, value_offset as u32),
166 };
167
168 let mut cursor = Cursor::new(data);
169 let (timestamps, values, _ranges) = track.resolve_data(&mut cursor).unwrap();
170
171 assert_eq!(timestamps, vec![10, 20, 30]);
172 assert_eq!(values.len(), 3);
173 assert_eq!(
174 values[0],
175 C3Vector {
176 x: 1.0,
177 y: 2.0,
178 z: 3.0
179 }
180 );
181 assert_eq!(
182 values[1],
183 C3Vector {
184 x: 4.0,
185 y: 5.0,
186 z: 6.0
187 }
188 );
189 assert_eq!(
190 values[2],
191 C3Vector {
192 x: 7.0,
193 y: 8.0,
194 z: 9.0
195 }
196 );
197 }
198}