Skip to main content

wow_m2/chunks/
texture_transform.rs

1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Seek, Write};
3
4use crate::chunks::animation::{M2AnimationBlock, M2AnimationTrack};
5use crate::common::{C3Vector, M2Parse};
6use crate::error::Result;
7use crate::version::M2Version;
8
9/// Transform type enum
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum M2TextureTransformType {
12    /// No transformation
13    None = 0,
14    /// Texture scrolling
15    Scroll = 1,
16    /// Texture rotation
17    Rotate = 2,
18    /// Texture scaling
19    Scale = 3,
20    /// Texture matrix transformation
21    Matrix = 4,
22}
23
24impl M2TextureTransformType {
25    /// Parse from integer value
26    pub fn from_u16(value: u16) -> Option<Self> {
27        match value {
28            0 => Some(Self::None),
29            1 => Some(Self::Scroll),
30            2 => Some(Self::Rotate),
31            3 => Some(Self::Scale),
32            4 => Some(Self::Matrix),
33            _ => None,
34        }
35    }
36}
37
38/// Represents a texture transform in an M2 model
39/// Introduced in Legion (7.x)
40#[derive(Debug, Clone)]
41pub struct M2TextureTransform {
42    /// Transform ID
43    pub id: u32,
44    /// Transform type
45    pub transform_type: M2TextureTransformType,
46    /// Translation animation (for scroll type)
47    pub translation: M2AnimationBlock<C3Vector>,
48    /// Rotation animation (for rotate type)
49    pub rotation: M2AnimationBlock<C4Quaternion>,
50    /// Scaling animation (for scale type)
51    pub scaling: M2AnimationBlock<C3Vector>,
52}
53
54/// A quaternion for rotations and texture transforms
55#[derive(Debug, Clone, Copy, PartialEq)]
56pub struct C4Quaternion {
57    pub x: f32,
58    pub y: f32,
59    pub z: f32,
60    pub w: f32,
61}
62
63impl M2Parse for C4Quaternion {
64    fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
65        C4Quaternion::parse(reader)
66    }
67
68    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
69        self.write(writer)
70    }
71}
72
73impl C4Quaternion {
74    /// Parse a quaternion from a reader
75    pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
76        let x = reader.read_f32_le()?;
77        let y = reader.read_f32_le()?;
78        let z = reader.read_f32_le()?;
79        let w = reader.read_f32_le()?;
80
81        Ok(Self { x, y, z, w })
82    }
83
84    /// Write a quaternion to a writer
85    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
86        writer.write_f32_le(self.x)?;
87        writer.write_f32_le(self.y)?;
88        writer.write_f32_le(self.z)?;
89        writer.write_f32_le(self.w)?;
90
91        Ok(())
92    }
93}
94
95impl M2TextureTransform {
96    /// Parse a texture transform from a reader
97    pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
98        let id = reader.read_u32_le()?;
99
100        let transform_type_raw = reader.read_u16_le()?;
101        let transform_type = M2TextureTransformType::from_u16(transform_type_raw)
102            .unwrap_or(M2TextureTransformType::None);
103
104        // Skip 2 bytes of padding
105        reader.read_u16_le()?;
106
107        let translation = M2AnimationBlock::parse(reader)?;
108        let rotation = M2AnimationBlock::parse(reader)?;
109        let scaling = M2AnimationBlock::parse(reader)?;
110
111        Ok(Self {
112            id,
113            transform_type,
114            translation,
115            rotation,
116            scaling,
117        })
118    }
119
120    /// Write a texture transform to a writer
121    pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
122        writer.write_u32_le(self.id)?;
123        writer.write_u16_le(self.transform_type as u16)?;
124
125        // Write 2 bytes of padding
126        writer.write_u16_le(0)?;
127
128        self.translation.write(writer)?;
129        self.rotation.write(writer)?;
130        self.scaling.write(writer)?;
131
132        Ok(())
133    }
134
135    /// Convert this texture transform to a different version
136    pub fn convert(&self, _target_version: M2Version) -> Self {
137        // No version-specific differences yet
138        self.clone()
139    }
140
141    /// Create a new texture transform with default values
142    pub fn new(id: u32, transform_type: M2TextureTransformType) -> Self {
143        Self {
144            id,
145            transform_type,
146            translation: M2AnimationBlock::new(M2AnimationTrack::default()),
147            rotation: M2AnimationBlock::new(M2AnimationTrack::default()),
148            scaling: M2AnimationBlock::new(M2AnimationTrack::default()),
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use std::io::Cursor;
157
158    #[test]
159    fn test_c4quaternion_parse_write() {
160        let quat = C4Quaternion {
161            x: 0.0,
162            y: 0.0,
163            z: 0.0,
164            w: 1.0,
165        };
166
167        let mut data = Vec::new();
168        quat.write(&mut data).unwrap();
169
170        let mut cursor = Cursor::new(data);
171        let parsed_quat = C4Quaternion::parse(&mut cursor).unwrap();
172
173        assert_eq!(parsed_quat.x, 0.0);
174        assert_eq!(parsed_quat.y, 0.0);
175        assert_eq!(parsed_quat.z, 0.0);
176        assert_eq!(parsed_quat.w, 1.0);
177    }
178
179    #[test]
180    fn test_texture_transform_type() {
181        assert_eq!(
182            M2TextureTransformType::from_u16(0),
183            Some(M2TextureTransformType::None)
184        );
185        assert_eq!(
186            M2TextureTransformType::from_u16(1),
187            Some(M2TextureTransformType::Scroll)
188        );
189        assert_eq!(
190            M2TextureTransformType::from_u16(2),
191            Some(M2TextureTransformType::Rotate)
192        );
193        assert_eq!(
194            M2TextureTransformType::from_u16(3),
195            Some(M2TextureTransformType::Scale)
196        );
197        assert_eq!(
198            M2TextureTransformType::from_u16(4),
199            Some(M2TextureTransformType::Matrix)
200        );
201        assert_eq!(M2TextureTransformType::from_u16(5), None);
202    }
203}