wow_m2/chunks/
material.rs1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Write};
3
4use crate::error::Result;
5use crate::version::M2Version;
6
7bitflags::bitflags! {
8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10 pub struct M2RenderFlags: u16 {
11 const UNLIT = 0x01;
13 const UNFOGGED = 0x02;
15 const NO_BACKFACE_CULLING = 0x04;
17 const NO_ZBUFFER = 0x08;
19 const AFFECTED_BY_PROJECTION = 0x10;
21 const DEPTH_TEST = 0x20;
23 const DEPTH_WRITE = 0x40;
25 const UNUSED = 0x80;
27 const SHADOW_BATCH_1 = 0x100;
29 const SHADOW_BATCH_2 = 0x200;
31 const UNKNOWN_400 = 0x400;
33 const UNKNOWN_800 = 0x800;
35 }
36}
37
38bitflags::bitflags! {
39 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
41 pub struct M2BlendMode: u16 {
42 const OPAQUE = 0;
44 const ALPHA_KEY = 1;
46 const ALPHA = 2;
48 const NO_ALPHA_ADD = 3;
50 const ADD = 4;
52 const MOD = 5;
54 const MOD2X = 6;
56 const BLEND_ADD = 7;
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum M2TexTransformType {
64 None = 0,
66 Scroll = 1,
68 Rotate = 2,
70 Scale = 3,
72 Stretch = 4,
74 Camera = 5,
76}
77
78impl M2TexTransformType {
79 pub fn from_u16(value: u16) -> Option<Self> {
81 match value {
82 0 => Some(Self::None),
83 1 => Some(Self::Scroll),
84 2 => Some(Self::Rotate),
85 3 => Some(Self::Scale),
86 4 => Some(Self::Stretch),
87 5 => Some(Self::Camera),
88 _ => None,
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
96pub struct M2Material {
97 pub flags: M2RenderFlags,
99 pub blend_mode: M2BlendMode,
101}
102
103impl M2Material {
104 pub fn parse<R: Read>(reader: &mut R, _version: u32) -> Result<Self> {
106 let flags = M2RenderFlags::from_bits_retain(reader.read_u16_le()?);
107 let blend_mode_raw = reader.read_u16_le()?;
108 let blend_mode = M2BlendMode::from_bits_retain(blend_mode_raw);
109
110 Ok(Self { flags, blend_mode })
111 }
112
113 pub fn write<W: Write>(&self, writer: &mut W, _version: u32) -> Result<()> {
115 writer.write_u16_le(self.flags.bits())?;
116 writer.write_u16_le(self.blend_mode.bits())?;
117 Ok(())
118 }
119
120 pub fn convert(&self, _target_version: M2Version) -> Self {
122 self.clone()
124 }
125
126 pub fn new(blend_mode: M2BlendMode) -> Self {
128 Self {
129 flags: M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE,
130 blend_mode,
131 }
132 }
133
134 pub fn size_in_bytes(_version: M2Version) -> usize {
136 4 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use std::io::Cursor;
144
145 #[test]
146 fn test_material_parse() {
147 let mut data = Vec::new();
148
149 data.extend_from_slice(
151 &(M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE)
152 .bits()
153 .to_le_bytes(),
154 );
155
156 data.extend_from_slice(&M2BlendMode::ALPHA.bits().to_le_bytes());
158
159 let mut cursor = Cursor::new(data);
160 let material =
161 M2Material::parse(&mut cursor, M2Version::Classic.to_header_version()).unwrap();
162
163 assert_eq!(
164 material.flags,
165 M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE
166 );
167 assert_eq!(material.blend_mode, M2BlendMode::ALPHA);
168 }
169
170 #[test]
171 fn test_material_write() {
172 let material = M2Material {
173 flags: M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE,
174 blend_mode: M2BlendMode::ALPHA,
175 };
176
177 let mut data = Vec::new();
178 material
179 .write(&mut data, M2Version::Classic.to_header_version())
180 .unwrap();
181
182 assert_eq!(data.len(), 4);
184
185 assert_eq!(
187 data[0..2],
188 (M2RenderFlags::DEPTH_TEST | M2RenderFlags::DEPTH_WRITE)
189 .bits()
190 .to_le_bytes()
191 );
192 assert_eq!(data[2..4], M2BlendMode::ALPHA.bits().to_le_bytes());
193 }
194
195 #[test]
196 fn test_material_size() {
197 assert_eq!(M2Material::size_in_bytes(M2Version::Classic), 4);
198 assert_eq!(M2Material::size_in_bytes(M2Version::Cataclysm), 4);
199 assert_eq!(M2Material::size_in_bytes(M2Version::WoD), 4);
200 }
201}