1use crate::io_ext::{ReadExt, WriteExt};
2use std::io::{Read, Seek, Write};
3
4use crate::chunks::animation::{M2AnimationBlock, M2AnimationTrack};
5use crate::common::C3Vector;
6use crate::error::Result;
7use crate::version::M2Version;
8
9bitflags::bitflags! {
10 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12 pub struct M2CameraFlags: u16 {
13 const CUSTOM_UV = 0x01;
15 const AUTO_GENERATED = 0x02;
17 const GLOBAL_POSITION = 0x04;
19 }
20}
21
22#[derive(Debug, Clone)]
32pub struct M2Camera {
33 pub camera_type: u32,
35 pub fov: f32,
37 pub far_clip: f32,
39 pub near_clip: f32,
41 pub position_animation: M2AnimationBlock<C3Vector>,
43 pub position_base: C3Vector,
45 pub target_position_animation: M2AnimationBlock<C3Vector>,
47 pub target_position_base: C3Vector,
49 pub roll_animation: M2AnimationBlock<f32>,
51 pub id: u32,
53 pub flags: M2CameraFlags,
55}
56
57impl M2Camera {
58 pub fn parse<R: Read + Seek>(reader: &mut R, version: u32) -> Result<Self> {
64 let camera_type = reader.read_u32_le()?;
65 let fov = reader.read_f32_le()?;
66 let far_clip = reader.read_f32_le()?;
67 let near_clip = reader.read_f32_le()?;
68
69 let position_animation = M2AnimationBlock::parse(reader)?;
71 let position_base = C3Vector::parse(reader)?;
72
73 let target_position_animation = M2AnimationBlock::parse(reader)?;
75 let target_position_base = C3Vector::parse(reader)?;
76
77 let roll_animation = M2AnimationBlock::parse(reader)?;
79
80 let (id, flags) = if version >= 264 {
82 let id = reader.read_u32_le()?;
83 let flags = M2CameraFlags::from_bits_retain(reader.read_u16_le()?);
84 reader.read_u16_le()?; (id, flags)
86 } else {
87 (0, M2CameraFlags::empty())
89 };
90
91 Ok(Self {
92 camera_type,
93 fov,
94 far_clip,
95 near_clip,
96 position_animation,
97 position_base,
98 target_position_animation,
99 target_position_base,
100 roll_animation,
101 id,
102 flags,
103 })
104 }
105
106 pub fn write<W: Write>(&self, writer: &mut W, version: u32) -> Result<()> {
108 writer.write_u32_le(self.camera_type)?;
109 writer.write_f32_le(self.fov)?;
110 writer.write_f32_le(self.far_clip)?;
111 writer.write_f32_le(self.near_clip)?;
112
113 self.position_animation.write(writer)?;
115 self.position_base.write(writer)?;
116
117 self.target_position_animation.write(writer)?;
119 self.target_position_base.write(writer)?;
120
121 self.roll_animation.write(writer)?;
123
124 if version >= 264 {
126 writer.write_u32_le(self.id)?;
127 writer.write_u16_le(self.flags.bits())?;
128 writer.write_u16_le(0)?; }
130
131 Ok(())
132 }
133
134 pub fn convert(&self, _target_version: M2Version) -> Self {
136 self.clone()
137 }
138
139 pub fn new(id: u32) -> Self {
141 Self {
142 camera_type: 0,
143 fov: 0.8726646, far_clip: 100.0,
145 near_clip: 0.1,
146 position_animation: M2AnimationBlock::new(M2AnimationTrack::default()),
147 position_base: C3Vector::default(),
148 target_position_animation: M2AnimationBlock::new(M2AnimationTrack::default()),
149 target_position_base: C3Vector::default(),
150 roll_animation: M2AnimationBlock::new(M2AnimationTrack::default()),
151 id,
152 flags: M2CameraFlags::empty(),
153 }
154 }
155
156 pub fn size(version: u32) -> usize {
158 if version >= 264 {
159 108
163 } else {
164 124
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use std::io::Cursor;
176
177 #[test]
178 fn test_camera_parse_write_vanilla() {
179 let camera = M2Camera::new(1);
180 let version = M2Version::Vanilla.to_header_version();
181
182 let mut data = Vec::new();
184 camera.write(&mut data, version).unwrap();
185
186 assert_eq!(data.len(), 124);
189
190 let mut cursor = Cursor::new(data);
192 let parsed = M2Camera::parse(&mut cursor, version).unwrap();
193
194 assert_eq!(parsed.camera_type, 0);
195 assert_eq!(parsed.id, 0);
197 assert_eq!(parsed.flags, M2CameraFlags::empty());
198 }
199
200 #[test]
201 fn test_camera_parse_write_wotlk() {
202 let mut camera = M2Camera::new(5);
203 camera.flags = M2CameraFlags::CUSTOM_UV;
204 let version = M2Version::WotLK.to_header_version();
205
206 let mut data = Vec::new();
208 camera.write(&mut data, version).unwrap();
209
210 assert_eq!(data.len(), 132);
214
215 let mut cursor = Cursor::new(data);
217 let parsed = M2Camera::parse(&mut cursor, version).unwrap();
218
219 assert_eq!(parsed.camera_type, 0);
220 assert_eq!(parsed.id, 5);
221 assert_eq!(parsed.flags, M2CameraFlags::CUSTOM_UV);
222 }
223
224 #[test]
225 fn test_camera_flags() {
226 let flags = M2CameraFlags::CUSTOM_UV | M2CameraFlags::AUTO_GENERATED;
227 assert!(flags.contains(M2CameraFlags::CUSTOM_UV));
228 assert!(flags.contains(M2CameraFlags::AUTO_GENERATED));
229 assert!(!flags.contains(M2CameraFlags::GLOBAL_POSITION));
230 }
231
232 #[test]
233 fn test_camera_size() {
234 assert_eq!(M2Camera::size(256), 124); assert_eq!(M2Camera::size(263), 124); assert_eq!(M2Camera::size(264), 108); assert_eq!(M2Camera::size(272), 108); }
240}