wow_m2/chunks/
animation.rs

1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Write};
3
4use crate::common::M2Array;
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 {
81    /// Interpolation type
82    pub interpolation_type: M2InterpolationType,
83    /// Global sequence ID or -1
84    pub global_sequence: i16,
85    /// Timestamps
86    pub timestamps: M2Array<u32>,
87    /// Values
88    pub values: M2Array<f32>,
89}
90
91impl M2AnimationTrack {
92    /// Parse an animation track from a reader
93    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
94        let interpolation_type_raw = reader.read_u16_le()?;
95        let interpolation_type = M2InterpolationType::from_u16(interpolation_type_raw)
96            .unwrap_or(M2InterpolationType::None);
97
98        let global_sequence = reader.read_i16_le()?;
99        let timestamps = M2Array::parse(reader)?;
100        let values = M2Array::parse(reader)?;
101
102        Ok(Self {
103            interpolation_type,
104            global_sequence,
105            timestamps,
106            values,
107        })
108    }
109
110    /// Write an animation track to a writer
111    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
112        writer.write_u16_le(self.interpolation_type as u16)?;
113        writer.write_i16_le(self.global_sequence)?;
114        self.timestamps.write(writer)?;
115        self.values.write(writer)?;
116
117        Ok(())
118    }
119}
120
121/// Animation block for a specific animation type
122#[derive(Debug, Clone)]
123pub struct M2AnimationBlock<T> {
124    /// Animation track
125    pub track: M2AnimationTrack,
126    /// Data type (phantom data)
127    _phantom: std::marker::PhantomData<T>,
128}
129
130impl<T> M2AnimationBlock<T> {
131    /// Create a new animation block from a track
132    pub fn new(track: M2AnimationTrack) -> Self {
133        Self {
134            track,
135            _phantom: std::marker::PhantomData,
136        }
137    }
138
139    /// Parse an animation block from a reader
140    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
141        let track = M2AnimationTrack::parse(reader)?;
142
143        Ok(Self {
144            track,
145            _phantom: std::marker::PhantomData,
146        })
147    }
148
149    /// Write an animation block to a writer
150    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
151        self.track.write(writer)?;
152
153        Ok(())
154    }
155}
156
157/// Animation data for a model
158#[derive(Debug, Clone)]
159pub struct M2Animation {
160    /// Animation ID
161    pub animation_id: u16,
162    /// Sub-animation ID (variation index)
163    pub sub_animation_id: u16,
164    /// Start timestamp (Classic) or Duration (BC+) in milliseconds
165    pub start_timestamp: u32,
166    /// End timestamp (Classic only)
167    pub end_timestamp: Option<u32>,
168    /// Movement speed
169    pub movement_speed: f32,
170    /// Flags
171    pub flags: u32,
172    /// Frequency/Probability (renamed in later versions)
173    pub frequency: i16,
174    /// Padding/Realignment
175    pub padding: u16,
176    /// Replay range (Classic only)
177    pub replay: Option<M2Range>,
178    /// Minimum extent (BC+ only)
179    pub minimum_extent: Option<[f32; 3]>,
180    /// Maximum extent (BC+ only)
181    pub maximum_extent: Option<[f32; 3]>,
182    /// Extent radius (BC+ only)
183    pub extent_radius: Option<f32>,
184    /// Next animation ID (BC+ only)
185    pub next_animation: Option<i16>,
186    /// Aliasing (BC+ only)
187    pub aliasing: Option<u16>,
188}
189
190impl M2Animation {
191    /// Parse an animation from a reader based on the M2 version
192    pub fn parse<R: Read>(reader: &mut R, version: u32) -> Result<Self> {
193        let animation_id = reader.read_u16_le()?;
194        let sub_animation_id = reader.read_u16_le()?;
195
196        // Version differences: Classic (256) vs BC+ (260+)
197        if version <= 256 {
198            // Classic format
199            let start_timestamp = reader.read_u32_le()?;
200            let end_timestamp = reader.read_u32_le()?;
201            let movement_speed = reader.read_f32_le()?;
202            let flags = reader.read_u32_le()?;
203            let frequency = reader.read_i16_le()?;
204            let padding = reader.read_u16_le()?;
205            let replay = M2Range::parse(reader)?;
206
207            Ok(Self {
208                animation_id,
209                sub_animation_id,
210                start_timestamp,
211                end_timestamp: Some(end_timestamp),
212                movement_speed,
213                flags,
214                frequency,
215                padding,
216                replay: Some(replay),
217                minimum_extent: None,
218                maximum_extent: None,
219                extent_radius: None,
220                next_animation: None,
221                aliasing: None,
222            })
223        } else {
224            // BC+ format
225            let duration = reader.read_u32_le()?;
226            let movement_speed = reader.read_f32_le()?;
227            let flags = reader.read_u32_le()?;
228            let frequency = reader.read_i16_le()?;
229            let padding = reader.read_u16_le()?;
230
231            let mut minimum_extent = [0.0; 3];
232            let mut maximum_extent = [0.0; 3];
233
234            for item in &mut minimum_extent {
235                *item = reader.read_f32_le()?;
236            }
237
238            for item in &mut maximum_extent {
239                *item = reader.read_f32_le()?;
240            }
241
242            let extent_radius = reader.read_f32_le()?;
243            let next_animation = reader.read_i16_le()?;
244            let aliasing = reader.read_u16_le()?;
245
246            Ok(Self {
247                animation_id,
248                sub_animation_id,
249                start_timestamp: duration, // In BC+, this field is duration
250                end_timestamp: None,
251                movement_speed,
252                flags,
253                frequency,
254                padding,
255                replay: None,
256                minimum_extent: Some(minimum_extent),
257                maximum_extent: Some(maximum_extent),
258                extent_radius: Some(extent_radius),
259                next_animation: Some(next_animation),
260                aliasing: Some(aliasing),
261            })
262        }
263    }
264
265    /// Write an animation to a writer
266    pub fn write<W: Write>(&self, writer: &mut W, version: u32) -> Result<()> {
267        writer.write_u16_le(self.animation_id)?;
268        writer.write_u16_le(self.sub_animation_id)?;
269
270        if version <= 256 {
271            // Classic format
272            writer.write_u32_le(self.start_timestamp)?;
273            writer.write_u32_le(self.end_timestamp.unwrap_or(self.start_timestamp + 1000))?;
274            writer.write_f32_le(self.movement_speed)?;
275            writer.write_u32_le(self.flags)?;
276            writer.write_i16_le(self.frequency)?;
277            writer.write_u16_le(self.padding)?;
278
279            if let Some(replay) = &self.replay {
280                replay.write(writer)?;
281            } else {
282                // Default replay range
283                M2Range {
284                    minimum: 0.0,
285                    maximum: 1.0,
286                }
287                .write(writer)?;
288            }
289        } else {
290            // BC+ format
291            writer.write_u32_le(self.start_timestamp)?; // This is duration in BC+
292            writer.write_f32_le(self.movement_speed)?;
293            writer.write_u32_le(self.flags)?;
294            writer.write_i16_le(self.frequency)?;
295            writer.write_u16_le(self.padding)?;
296
297            let minimum_extent = self.minimum_extent.unwrap_or([0.0, 0.0, 0.0]);
298            let maximum_extent = self.maximum_extent.unwrap_or([0.0, 0.0, 0.0]);
299
300            for &value in &minimum_extent {
301                writer.write_f32_le(value)?;
302            }
303
304            for &value in &maximum_extent {
305                writer.write_f32_le(value)?;
306            }
307
308            writer.write_f32_le(self.extent_radius.unwrap_or(0.0))?;
309            writer.write_i16_le(self.next_animation.unwrap_or(-1))?;
310            writer.write_u16_le(self.aliasing.unwrap_or(0))?;
311        }
312
313        Ok(())
314    }
315
316    /// Convert this animation to a different version (no version differences for animations yet)
317    pub fn convert(&self, _target_version: M2Version) -> Self {
318        self.clone()
319    }
320}