use crate::io_ext::{ReadExt, WriteExt};
use std::io::{Read, Seek, Write};
use crate::common::{M2Array, M2Parse, M2Vec};
use crate::error::Result;
use crate::version::M2Version;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum M2InterpolationType {
None = 0,
Linear = 1,
Bezier = 2,
Hermite = 3,
}
impl M2InterpolationType {
pub fn from_u16(value: u16) -> Option<Self> {
match value {
0 => Some(Self::None),
1 => Some(Self::Linear),
2 => Some(Self::Bezier),
3 => Some(Self::Hermite),
_ => None,
}
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct M2AnimationFlags: u16 {
const HAS_TRANSLATION = 0x1;
const HAS_ROTATION = 0x2;
const HAS_SCALING = 0x4;
const WORLD_SPACE = 0x8;
const BILLBOARD_ROTATION = 0x10;
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct M2Range {
pub minimum: f32,
pub maximum: f32,
}
impl M2Range {
pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
let minimum = reader.read_f32_le()?;
let maximum = reader.read_f32_le()?;
Ok(Self { minimum, maximum })
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_f32_le(self.minimum)?;
writer.write_f32_le(self.maximum)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct M2AnimationTrack<T: M2Parse> {
pub interpolation_type: M2InterpolationType,
pub global_sequence: i16,
pub interpolation_ranges: M2Array<(u32, u32)>,
pub timestamps: M2Array<u32>,
pub values: M2Vec<T>,
}
impl<T: M2Parse> Default for M2AnimationTrack<T> {
fn default() -> Self {
Self {
interpolation_type: M2InterpolationType::None,
global_sequence: -1,
interpolation_ranges: M2Array::new(0, 0),
timestamps: M2Array::new(0, 0),
values: M2Vec::new(),
}
}
}
impl<T: M2Parse> M2AnimationTrack<T> {
pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let interpolation_type_raw = reader.read_u16_le()?;
let interpolation_type = M2InterpolationType::from_u16(interpolation_type_raw)
.unwrap_or(M2InterpolationType::None);
let global_sequence = reader.read_i16_le()?;
let interpolation_ranges = M2Array::parse(reader)?;
let timestamps = M2Array::parse(reader)?;
let values = M2Vec::<T>::parse(reader)?;
Ok(Self {
interpolation_type,
global_sequence,
interpolation_ranges,
timestamps,
values,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u16_le(self.interpolation_type as u16)?;
writer.write_i16_le(self.global_sequence)?;
self.interpolation_ranges.write(writer)?;
self.timestamps.write(writer)?;
self.values.write(writer)?;
Ok(())
}
}
#[derive(Debug, Default, Clone)]
pub struct M2AnimationBlock<T: M2Parse> {
pub track: M2AnimationTrack<T>,
_phantom: std::marker::PhantomData<T>,
}
impl<T: M2Parse> M2AnimationBlock<T> {
pub fn new(track: M2AnimationTrack<T>) -> Self {
Self {
track,
_phantom: std::marker::PhantomData,
}
}
pub fn parse<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let track = M2AnimationTrack::parse(reader)?;
Ok(Self {
track,
_phantom: std::marker::PhantomData,
})
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
self.track.write(writer)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct M2Animation {
pub animation_id: u16,
pub sub_animation_id: u16,
pub start_timestamp: u32,
pub end_timestamp: Option<u32>,
pub movement_speed: f32,
pub flags: u32,
pub frequency: i16,
pub padding: u16,
pub replay: Option<M2Range>,
pub minimum_extent: Option<[f32; 3]>,
pub maximum_extent: Option<[f32; 3]>,
pub extent_radius: Option<f32>,
pub next_animation: Option<i16>,
pub aliasing: Option<u16>,
}
impl M2Animation {
pub fn parse<R: Read>(reader: &mut R, version: u32) -> Result<Self> {
let animation_id = reader.read_u16_le()?;
let sub_animation_id = reader.read_u16_le()?;
if version <= 256 {
let start_timestamp = reader.read_u32_le()?;
let end_timestamp = reader.read_u32_le()?;
let movement_speed = reader.read_f32_le()?;
let flags = reader.read_u32_le()?;
let frequency = reader.read_i16_le()?;
let padding = reader.read_u16_le()?;
let replay = M2Range::parse(reader)?;
Ok(Self {
animation_id,
sub_animation_id,
start_timestamp,
end_timestamp: Some(end_timestamp),
movement_speed,
flags,
frequency,
padding,
replay: Some(replay),
minimum_extent: None,
maximum_extent: None,
extent_radius: None,
next_animation: None,
aliasing: None,
})
} else {
let duration = reader.read_u32_le()?;
let movement_speed = reader.read_f32_le()?;
let flags = reader.read_u32_le()?;
let frequency = reader.read_i16_le()?;
let padding = reader.read_u16_le()?;
let mut minimum_extent = [0.0; 3];
let mut maximum_extent = [0.0; 3];
for item in &mut minimum_extent {
*item = reader.read_f32_le()?;
}
for item in &mut maximum_extent {
*item = reader.read_f32_le()?;
}
let extent_radius = reader.read_f32_le()?;
let next_animation = reader.read_i16_le()?;
let aliasing = reader.read_u16_le()?;
Ok(Self {
animation_id,
sub_animation_id,
start_timestamp: duration, end_timestamp: None,
movement_speed,
flags,
frequency,
padding,
replay: None,
minimum_extent: Some(minimum_extent),
maximum_extent: Some(maximum_extent),
extent_radius: Some(extent_radius),
next_animation: Some(next_animation),
aliasing: Some(aliasing),
})
}
}
pub fn write<W: Write>(&self, writer: &mut W, version: u32) -> Result<()> {
writer.write_u16_le(self.animation_id)?;
writer.write_u16_le(self.sub_animation_id)?;
if version <= 256 {
writer.write_u32_le(self.start_timestamp)?;
writer.write_u32_le(self.end_timestamp.unwrap_or(self.start_timestamp + 1000))?;
writer.write_f32_le(self.movement_speed)?;
writer.write_u32_le(self.flags)?;
writer.write_i16_le(self.frequency)?;
writer.write_u16_le(self.padding)?;
if let Some(replay) = &self.replay {
replay.write(writer)?;
} else {
M2Range {
minimum: 0.0,
maximum: 1.0,
}
.write(writer)?;
}
} else {
writer.write_u32_le(self.start_timestamp)?; writer.write_f32_le(self.movement_speed)?;
writer.write_u32_le(self.flags)?;
writer.write_i16_le(self.frequency)?;
writer.write_u16_le(self.padding)?;
let minimum_extent = self.minimum_extent.unwrap_or([0.0, 0.0, 0.0]);
let maximum_extent = self.maximum_extent.unwrap_or([0.0, 0.0, 0.0]);
for &value in &minimum_extent {
writer.write_f32_le(value)?;
}
for &value in &maximum_extent {
writer.write_f32_le(value)?;
}
writer.write_f32_le(self.extent_radius.unwrap_or(0.0))?;
writer.write_i16_le(self.next_animation.unwrap_or(-1))?;
writer.write_u16_le(self.aliasing.unwrap_or(0))?;
}
Ok(())
}
pub fn convert(&self, _target_version: M2Version) -> Self {
self.clone()
}
}