use crate::io_ext::{ReadExt, WriteExt};
use std::io::{Read, Write};
use crate::error::Result;
use crate::version::M2Version;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct M2RenderFlags: u16 {
const UNLIT = 0x01;
const UNFOGGED = 0x02;
const NO_BACKFACE_CULLING = 0x04;
const NO_ZBUFFER = 0x08;
const AFFECTED_BY_PROJECTION = 0x10;
const DEPTH_TEST = 0x20;
const DEPTH_WRITE = 0x40;
const UNUSED = 0x80;
const SHADOW_BATCH_1 = 0x100;
const SHADOW_BATCH_2 = 0x200;
const UNKNOWN_400 = 0x400;
const UNKNOWN_800 = 0x800;
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct M2BlendMode: u16 {
const OPAQUE = 0;
const ALPHA_KEY = 1;
const ALPHA = 2;
const NO_ALPHA_ADD = 3;
const ADD = 4;
const MOD = 5;
const MOD2X = 6;
const BLEND_ADD = 7;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum M2TexTransformType {
None = 0,
Scroll = 1,
Rotate = 2,
Scale = 3,
Stretch = 4,
Camera = 5,
}
impl M2TexTransformType {
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::Stretch),
5 => Some(Self::Camera),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct M2Material {
pub flags: M2RenderFlags,
pub blend_mode: M2BlendMode,
}
impl M2Material {
pub fn parse<R: Read>(reader: &mut R, _version: u32) -> Result<Self> {
let flags = M2RenderFlags::from_bits_retain(reader.read_u16_le()?);
let blend_mode_raw = reader.read_u16_le()?;
let blend_mode = M2BlendMode::from_bits_retain(blend_mode_raw);
Ok(Self { flags, blend_mode })
}
pub fn write<W: Write>(&self, writer: &mut W, _version: u32) -> Result<()> {
writer.write_u16_le(self.flags.bits())?;
writer.write_u16_le(self.blend_mode.bits())?;
Ok(())
}
pub fn convert(&self, _target_version: M2Version) -> Self {
self.clone()
}
pub fn new(blend_mode: M2BlendMode) -> Self {
Self {
flags: M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE,
blend_mode,
}
}
pub fn size_in_bytes(_version: M2Version) -> usize {
4 }
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_material_parse() {
let mut data = Vec::new();
data.extend_from_slice(
&(M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE)
.bits()
.to_le_bytes(),
);
data.extend_from_slice(&M2BlendMode::ALPHA.bits().to_le_bytes());
let mut cursor = Cursor::new(data);
let material =
M2Material::parse(&mut cursor, M2Version::Vanilla.to_header_version()).unwrap();
assert_eq!(
material.flags,
M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE
);
assert_eq!(material.blend_mode, M2BlendMode::ALPHA);
}
#[test]
fn test_material_write() {
let material = M2Material {
flags: M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE,
blend_mode: M2BlendMode::ALPHA,
};
let mut data = Vec::new();
material
.write(&mut data, M2Version::Vanilla.to_header_version())
.unwrap();
assert_eq!(data.len(), 4);
assert_eq!(
data[0..2],
(M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE)
.bits()
.to_le_bytes()
);
assert_eq!(data[2..4], M2BlendMode::ALPHA.bits().to_le_bytes());
}
#[test]
fn test_material_size() {
assert_eq!(M2Material::size_in_bytes(M2Version::Vanilla), 4);
assert_eq!(M2Material::size_in_bytes(M2Version::Cataclysm), 4);
assert_eq!(M2Material::size_in_bytes(M2Version::WoD), 4);
}
}