wow_m2/chunks/
animation.rs

1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Seek, Write};
3
4use crate::common::{M2Array, M2Parse, M2Vec};
5use crate::error::Result;
6use crate::version::M2Version;
7
8/// Animation interpolation types
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum M2InterpolationType {
11    /// No interpolation
12    None = 0,
13    /// Linear interpolation
14    Linear = 1,
15    /// Bezier curve interpolation
16    Bezier = 2,
17    /// Hermite curve interpolation
18    Hermite = 3,
19}
20
21impl M2InterpolationType {
22    /// Parse from integer value
23    pub fn from_u16(value: u16) -> Option<Self> {
24        match value {
25            0 => Some(Self::None),
26            1 => Some(Self::Linear),
27            2 => Some(Self::Bezier),
28            3 => Some(Self::Hermite),
29            _ => None,
30        }
31    }
32}
33
34bitflags::bitflags! {
35    /// Animation flags as defined in the M2 format
36    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
37    pub struct M2AnimationFlags: u16 {
38        /// Animation has translation keyframes
39        const HAS_TRANSLATION = 0x1;
40        /// Animation has rotation keyframes
41        const HAS_ROTATION = 0x2;
42        /// Animation has scaling keyframes
43        const HAS_SCALING = 0x4;
44        /// Animation is in world space (instead of local model space)
45        const WORLD_SPACE = 0x8;
46        /// Animation has billboarded rotation keyframes
47        const BILLBOARD_ROTATION = 0x10;
48    }
49}
50
51/// Animation value ranges
52#[derive(Debug, Clone, Copy, PartialEq)]
53pub struct M2Range {
54    /// Minimum value
55    pub minimum: f32,
56    /// Maximum value
57    pub maximum: f32,
58}
59
60impl M2Range {
61    /// Parse a range from a reader
62    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
63        let minimum = reader.read_f32_le()?;
64        let maximum = reader.read_f32_le()?;
65
66        Ok(Self { minimum, maximum })
67    }
68
69    /// Write a range to a writer
70    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
71        writer.write_f32_le(self.minimum)?;
72        writer.write_f32_le(self.maximum)?;
73
74        Ok(())
75    }
76}
77
78/// An animation track header
79#[derive(Debug, Clone)]
80pub struct M2AnimationTrack<T: M2Parse> {
81    /// Interpolation type
82    pub interpolation_type: M2InterpolationType,
83    /// Global sequence ID or -1
84    pub global_sequence: i16,
85    // Interpolation ranges
86    pub interpolation_ranges: M2Array<(u32, u32)>,
87    /// Timestamps
88    pub timestamps: M2Array<u32>,
89    /// Values
90    pub values: M2Vec<T>,
91}
92
93impl<T: M2Parse> Default for M2AnimationTrack<T> {
94    fn default() -> Self {
95        Self {
96            interpolation_type: M2InterpolationType::None,
97            global_sequence: -1,
98            interpolation_ranges: M2Array::new(0, 0),
99            timestamps: M2Array::new(0, 0),
100            values: M2Vec::new(),
101        }
102    }
103}
104
105impl<T: M2Parse> M2AnimationTrack<T> {
106    /// Parse an animation track from a reader
107    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
108        let interpolation_type_raw = reader.read_u16_le()?;
109        let interpolation_type = M2InterpolationType::from_u16(interpolation_type_raw)
110            .unwrap_or(M2InterpolationType::None);
111
112        let global_sequence = reader.read_i16_le()?;
113        let interpolation_ranges = M2Array::parse(reader)?;
114        let timestamps = M2Array::parse(reader)?;
115        let values = M2Vec::<T>::parse(reader)?;
116
117        Ok(Self {
118            interpolation_type,
119            global_sequence,
120            interpolation_ranges,
121            timestamps,
122            values,
123        })
124    }
125
126    /// Write an animation track to a writer
127    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
128        writer.write_u16_le(self.interpolation_type as u16)?;
129        writer.write_i16_le(self.global_sequence)?;
130        self.interpolation_ranges.write(writer)?;
131        self.timestamps.write(writer)?;
132        self.values.write(writer)?;
133
134        Ok(())
135    }
136}
137
138/// Animation block for a specific animation type
139#[derive(Debug, Default, Clone)]
140pub struct M2AnimationBlock<T: M2Parse> {
141    /// Animation track
142    pub track: M2AnimationTrack<T>,
143    /// Data type (phantom data)
144    _phantom: std::marker::PhantomData<T>,
145}
146
147impl<T: M2Parse> M2AnimationBlock<T> {
148    /// Create a new animation block from a track
149    pub fn new(track: M2AnimationTrack<T>) -> Self {
150        Self {
151            track,
152            _phantom: std::marker::PhantomData,
153        }
154    }
155
156    /// Parse an animation block from a reader
157    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
158        let track = M2AnimationTrack::parse(reader)?;
159
160        Ok(Self {
161            track,
162            _phantom: std::marker::PhantomData,
163        })
164    }
165
166    /// Write an animation block to a writer
167    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
168        self.track.write(writer)?;
169
170        Ok(())
171    }
172}
173
174/// Animation data for a model
175#[derive(Debug, Clone)]
176pub struct M2Animation {
177    /// Animation ID
178    pub animation_id: u16,
179    /// Sub-animation ID (variation index)
180    pub sub_animation_id: u16,
181    /// Start timestamp (Classic) or Duration (BC+) in milliseconds
182    pub start_timestamp: u32,
183    /// End timestamp (Classic only)
184    pub end_timestamp: Option<u32>,
185    /// Movement speed
186    pub movement_speed: f32,
187    /// Flags
188    pub flags: u32,
189    /// Frequency/Probability (renamed in later versions)
190    pub frequency: i16,
191    /// Padding/Realignment
192    pub padding: u16,
193    /// Replay range (Classic only)
194    pub replay: Option<M2Range>,
195    /// Minimum extent (BC+ only)
196    pub minimum_extent: Option<[f32; 3]>,
197    /// Maximum extent (BC+ only)
198    pub maximum_extent: Option<[f32; 3]>,
199    /// Extent radius (BC+ only)
200    pub extent_radius: Option<f32>,
201    /// Next animation ID (BC+ only)
202    pub next_animation: Option<i16>,
203    /// Aliasing (BC+ only)
204    pub aliasing: Option<u16>,
205}
206
207impl M2Animation {
208    /// Parse an animation from a reader based on the M2 version
209    pub fn parse<R: Read>(reader: &mut R, version: u32) -> Result<Self> {
210        let animation_id = reader.read_u16_le()?;
211        let sub_animation_id = reader.read_u16_le()?;
212
213        // Version differences: Classic (256) vs BC+ (260+)
214        if version <= 256 {
215            // Vanilla format
216            let start_timestamp = reader.read_u32_le()?;
217            let end_timestamp = reader.read_u32_le()?;
218            let movement_speed = reader.read_f32_le()?;
219            let flags = reader.read_u32_le()?;
220            let frequency = reader.read_i16_le()?;
221            let padding = reader.read_u16_le()?;
222            let replay = M2Range::parse(reader)?;
223
224            Ok(Self {
225                animation_id,
226                sub_animation_id,
227                start_timestamp,
228                end_timestamp: Some(end_timestamp),
229                movement_speed,
230                flags,
231                frequency,
232                padding,
233                replay: Some(replay),
234                minimum_extent: None,
235                maximum_extent: None,
236                extent_radius: None,
237                next_animation: None,
238                aliasing: None,
239            })
240        } else {
241            // BC+ format
242            let duration = reader.read_u32_le()?;
243            let movement_speed = reader.read_f32_le()?;
244            let flags = reader.read_u32_le()?;
245            let frequency = reader.read_i16_le()?;
246            let padding = reader.read_u16_le()?;
247
248            let mut minimum_extent = [0.0; 3];
249            let mut maximum_extent = [0.0; 3];
250
251            for item in &mut minimum_extent {
252                *item = reader.read_f32_le()?;
253            }
254
255            for item in &mut maximum_extent {
256                *item = reader.read_f32_le()?;
257            }
258
259            let extent_radius = reader.read_f32_le()?;
260            let next_animation = reader.read_i16_le()?;
261            let aliasing = reader.read_u16_le()?;
262
263            Ok(Self {
264                animation_id,
265                sub_animation_id,
266                start_timestamp: duration, // In BC+, this field is duration
267                end_timestamp: None,
268                movement_speed,
269                flags,
270                frequency,
271                padding,
272                replay: None,
273                minimum_extent: Some(minimum_extent),
274                maximum_extent: Some(maximum_extent),
275                extent_radius: Some(extent_radius),
276                next_animation: Some(next_animation),
277                aliasing: Some(aliasing),
278            })
279        }
280    }
281
282    /// Write an animation to a writer
283    pub fn write<W: Write>(&self, writer: &mut W, version: u32) -> Result<()> {
284        writer.write_u16_le(self.animation_id)?;
285        writer.write_u16_le(self.sub_animation_id)?;
286
287        if version <= 256 {
288            // Vanilla format
289            writer.write_u32_le(self.start_timestamp)?;
290            writer.write_u32_le(self.end_timestamp.unwrap_or(self.start_timestamp + 1000))?;
291            writer.write_f32_le(self.movement_speed)?;
292            writer.write_u32_le(self.flags)?;
293            writer.write_i16_le(self.frequency)?;
294            writer.write_u16_le(self.padding)?;
295
296            if let Some(replay) = &self.replay {
297                replay.write(writer)?;
298            } else {
299                // Default replay range
300                M2Range {
301                    minimum: 0.0,
302                    maximum: 1.0,
303                }
304                .write(writer)?;
305            }
306        } else {
307            // BC+ format
308            writer.write_u32_le(self.start_timestamp)?; // This is duration in BC+
309            writer.write_f32_le(self.movement_speed)?;
310            writer.write_u32_le(self.flags)?;
311            writer.write_i16_le(self.frequency)?;
312            writer.write_u16_le(self.padding)?;
313
314            let minimum_extent = self.minimum_extent.unwrap_or([0.0, 0.0, 0.0]);
315            let maximum_extent = self.maximum_extent.unwrap_or([0.0, 0.0, 0.0]);
316
317            for &value in &minimum_extent {
318                writer.write_f32_le(value)?;
319            }
320
321            for &value in &maximum_extent {
322                writer.write_f32_le(value)?;
323            }
324
325            writer.write_f32_le(self.extent_radius.unwrap_or(0.0))?;
326            writer.write_i16_le(self.next_animation.unwrap_or(-1))?;
327            writer.write_u16_le(self.aliasing.unwrap_or(0))?;
328        }
329
330        Ok(())
331    }
332
333    /// Convert this animation to a different version (no version differences for animations yet)
334    pub fn convert(&self, _target_version: M2Version) -> Self {
335        self.clone()
336    }
337}