use crate::io_ext::{ReadExt, WriteExt};
use std::io::{Read, Seek, Write};
use crate::chunks::animation::{M2AnimationBlock, M2AnimationTrack};
use crate::common::{C3Vector, M2Parse};
use crate::error::Result;
use crate::version::M2Version;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum M2TextureTransformType {
None = 0,
Scroll = 1,
Rotate = 2,
Scale = 3,
Matrix = 4,
}
impl M2TextureTransformType {
pub fn from_u16(value: u16) -> Option<Self> {
match value {
0 => Some(Self::None),
1 => Some(Self::Scroll),
2 => Some(Self::Rotate),
3 => Some(Self::Scale),
4 => Some(Self::Matrix),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct M2TextureTransform {
pub id: u32,
pub transform_type: M2TextureTransformType,
pub translation: M2AnimationBlock<C3Vector>,
pub rotation: M2AnimationBlock<C4Quaternion>,
pub scaling: M2AnimationBlock<C3Vector>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct C4Quaternion {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl M2Parse for C4Quaternion {
fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
C4Quaternion::parse(reader)
}
fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
self.write(writer)
}
}
impl C4Quaternion {
pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
let x = reader.read_f32_le()?;
let y = reader.read_f32_le()?;
let z = reader.read_f32_le()?;
let w = reader.read_f32_le()?;
Ok(Self { x, y, z, w })
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_f32_le(self.x)?;
writer.write_f32_le(self.y)?;
writer.write_f32_le(self.z)?;
writer.write_f32_le(self.w)?;
Ok(())
}
}
impl M2TextureTransform {
pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let id = reader.read_u32_le()?;
let transform_type_raw = reader.read_u16_le()?;
let transform_type = M2TextureTransformType::from_u16(transform_type_raw)
.unwrap_or(M2TextureTransformType::None);
reader.read_u16_le()?;
let translation = M2AnimationBlock::parse(reader)?;
let rotation = M2AnimationBlock::parse(reader)?;
let scaling = M2AnimationBlock::parse(reader)?;
Ok(Self {
id,
transform_type,
translation,
rotation,
scaling,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u32_le(self.id)?;
writer.write_u16_le(self.transform_type as u16)?;
writer.write_u16_le(0)?;
self.translation.write(writer)?;
self.rotation.write(writer)?;
self.scaling.write(writer)?;
Ok(())
}
pub fn convert(&self, _target_version: M2Version) -> Self {
self.clone()
}
pub fn new(id: u32, transform_type: M2TextureTransformType) -> Self {
Self {
id,
transform_type,
translation: M2AnimationBlock::new(M2AnimationTrack::default()),
rotation: M2AnimationBlock::new(M2AnimationTrack::default()),
scaling: M2AnimationBlock::new(M2AnimationTrack::default()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_c4quaternion_parse_write() {
let quat = C4Quaternion {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
};
let mut data = Vec::new();
quat.write(&mut data).unwrap();
let mut cursor = Cursor::new(data);
let parsed_quat = C4Quaternion::parse(&mut cursor).unwrap();
assert_eq!(parsed_quat.x, 0.0);
assert_eq!(parsed_quat.y, 0.0);
assert_eq!(parsed_quat.z, 0.0);
assert_eq!(parsed_quat.w, 1.0);
}
#[test]
fn test_texture_transform_type() {
assert_eq!(
M2TextureTransformType::from_u16(0),
Some(M2TextureTransformType::None)
);
assert_eq!(
M2TextureTransformType::from_u16(1),
Some(M2TextureTransformType::Scroll)
);
assert_eq!(
M2TextureTransformType::from_u16(2),
Some(M2TextureTransformType::Rotate)
);
assert_eq!(
M2TextureTransformType::from_u16(3),
Some(M2TextureTransformType::Scale)
);
assert_eq!(
M2TextureTransformType::from_u16(4),
Some(M2TextureTransformType::Matrix)
);
assert_eq!(M2TextureTransformType::from_u16(5), None);
}
}