1use crate::math::{Mat4, Vec3};
6use crate::model::Quad;
7
8#[derive(Debug, Clone, Default, PartialEq)]
10pub struct Frame {
11 pub uuid: String,
12 pub entity_type: String,
14 pub partial: f32,
16 pub age: f32,
18 pub limb_swing: f32,
20 pub limb_amount: f32,
21 pub head_yaw: f32,
23 pub head_pitch: f32,
24 pub body_yaw: f32,
26 pub pitch: f32,
27 pub pos: [f64; 3],
28 pub velocity: Vec3,
30 pub flags: u32,
32 pub captures: Vec<Capture>,
34}
35
36#[derive(Debug, Clone, Default, PartialEq)]
38pub struct Capture {
39 pub source: String,
41 pub meshes: Vec<Mesh>,
42}
43
44#[derive(Debug, Clone, Default, PartialEq)]
46pub struct Mesh {
47 pub texture: String,
48 pub quads: Vec<Quad>,
49}
50
51#[derive(Debug, Clone, PartialEq)]
53pub enum Anchor {
54 Entity,
56 Model,
58 Bone(String),
60 Block,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
66pub enum Mode {
67 #[default]
69 Cutout,
70 Translucent,
72 Emissive,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Default)]
78pub struct BoneOp {
79 pub rotation: Vec3,
81 pub set_rotation: bool,
82 pub position: Vec3,
84 pub set_position: bool,
85 pub scale: Option<Vec3>,
86 pub visible: Option<bool>,
88}
89
90#[derive(Debug, Clone, Default, PartialEq)]
92pub struct Ops(pub Vec<u8>);
93
94const ROOT: u8 = 1;
95const BONE: u8 = 2;
96const MESH: u8 = 3;
97const DISGUISE: u8 = 4;
98const HIDE: u8 = 5;
99const CAPTURE: u8 = 6;
100
101impl Ops {
102 pub fn is_empty(&self) -> bool {
103 self.0.is_empty()
104 }
105
106 pub fn root(&mut self, m: &Mat4) {
108 self.0.push(ROOT);
109 for f in m.0 {
110 put_f32(&mut self.0, f);
111 }
112 }
113
114 pub fn bone(&mut self, bone: &str, op: &BoneOp) {
116 let b = &mut self.0;
117 b.push(BONE);
118 put_str(b, bone);
119 let mut flags = 0u8;
120 if op.set_rotation {
121 flags |= 1;
122 }
123 if op.set_position {
124 flags |= 2;
125 }
126 if op.scale.is_some() {
127 flags |= 4;
128 }
129 match op.visible {
130 Some(false) => flags |= 8,
131 Some(true) => flags |= 16,
132 None => {}
133 }
134 b.push(flags);
135 for v in [op.rotation, op.position, op.scale.unwrap_or(Vec3::ONE)] {
136 put_vec(b, v);
137 }
138 }
139
140 pub fn mesh(&mut self, anchor: &Anchor, texture: &str, mode: Mode, glint: bool, argb: u32, quads: &[Quad]) {
143 if quads.is_empty() {
144 return;
145 }
146 let b = &mut self.0;
147 b.push(MESH);
148 let (a, bone) = match anchor {
149 Anchor::Entity => (0u8, ""),
150 Anchor::Model => (1, ""),
151 Anchor::Bone(n) => (2, n.as_str()),
152 Anchor::Block => (3, ""),
153 };
154 b.push(a);
155 put_str(b, bone);
156 put_str(b, texture);
157 b.push(match mode {
158 Mode::Cutout => 0,
159 Mode::Translucent => 1,
160 Mode::Emissive => 2,
161 });
162 b.push(glint as u8);
163 b.extend_from_slice(&argb.to_le_bytes());
164 put_quads(b, quads);
165 }
166
167 pub fn disguise(&mut self, entity_type: &str) {
169 self.0.push(DISGUISE);
170 put_str(&mut self.0, entity_type);
171 }
172
173 pub fn hide(&mut self) {
175 self.0.push(HIDE);
176 }
177
178 pub fn capture(&mut self, source: &str) {
181 self.0.push(CAPTURE);
182 put_str(&mut self.0, source);
183 }
184}
185
186fn put_f32(b: &mut Vec<u8>, f: f32) {
189 b.extend_from_slice(&f.to_le_bytes());
190}
191
192fn put_vec(b: &mut Vec<u8>, v: Vec3) {
193 put_f32(b, v.x);
194 put_f32(b, v.y);
195 put_f32(b, v.z);
196}
197
198fn put_str(b: &mut Vec<u8>, s: &str) {
199 let s = &s.as_bytes()[..s.len().min(u16::MAX as usize)];
200 b.extend_from_slice(&(s.len() as u16).to_le_bytes());
201 b.extend_from_slice(s);
202}
203
204fn put_quads(b: &mut Vec<u8>, quads: &[Quad]) {
206 b.extend_from_slice(&((quads.len() * 4) as u32).to_le_bytes());
207 for q in quads {
208 for i in 0..4 {
209 put_vec(b, q.pos[i]);
210 put_f32(b, q.uv[i][0]);
211 put_f32(b, q.uv[i][1]);
212 put_vec(b, q.normal);
213 }
214 }
215}
216
217struct Reader<'a> {
218 b: &'a [u8],
219}
220
221impl Reader<'_> {
222 fn take(&mut self, n: usize) -> Option<&[u8]> {
223 if self.b.len() < n {
224 return None;
225 }
226 let (h, t) = self.b.split_at(n);
227 self.b = t;
228 Some(h)
229 }
230 fn u8(&mut self) -> Option<u8> {
231 self.take(1).map(|b| b[0])
232 }
233 fn u16(&mut self) -> Option<u16> {
234 self.take(2).map(|b| u16::from_le_bytes([b[0], b[1]]))
235 }
236 fn u32(&mut self) -> Option<u32> {
237 self.take(4).map(|b| u32::from_le_bytes(b.try_into().unwrap()))
238 }
239 fn f32(&mut self) -> Option<f32> {
240 self.take(4).map(|b| f32::from_le_bytes(b.try_into().unwrap()))
241 }
242 fn f64(&mut self) -> Option<f64> {
243 self.take(8).map(|b| f64::from_le_bytes(b.try_into().unwrap()))
244 }
245 fn vec(&mut self) -> Option<Vec3> {
246 Some(Vec3::new(self.f32()?, self.f32()?, self.f32()?))
247 }
248 fn str(&mut self) -> Option<String> {
249 let n = self.u16()? as usize;
250 String::from_utf8(self.take(n)?.to_vec()).ok()
251 }
252 fn quads(&mut self) -> Option<Vec<Quad>> {
253 let n = self.u32()? as usize / 4;
254 let mut out = Vec::with_capacity(n.min(1 << 16));
255 for _ in 0..n {
256 let mut q = Quad { pos: [Vec3::ZERO; 4], uv: [[0.0; 2]; 4], normal: Vec3::ZERO };
257 for i in 0..4 {
258 q.pos[i] = self.vec()?;
259 q.uv[i] = [self.f32()?, self.f32()?];
260 q.normal = self.vec()?;
261 }
262 out.push(q);
263 }
264 Some(out)
265 }
266}
267
268impl Frame {
269 pub fn decode(bytes: &[u8]) -> Option<Frame> {
271 let mut r = Reader { b: bytes };
272 if r.u8()? != 1 {
273 return None;
274 }
275 let mut f = Frame {
276 uuid: r.str()?,
277 entity_type: r.str()?,
278 partial: r.f32()?,
279 age: r.f32()?,
280 limb_swing: r.f32()?,
281 limb_amount: r.f32()?,
282 head_yaw: r.f32()?,
283 head_pitch: r.f32()?,
284 body_yaw: r.f32()?,
285 pitch: r.f32()?,
286 pos: [r.f64()?, r.f64()?, r.f64()?],
287 velocity: r.vec()?,
288 flags: r.u32()?,
289 captures: Vec::new(),
290 };
291 for _ in 0..r.u16()? {
292 let source = r.str()?;
293 let mut meshes = Vec::new();
294 for _ in 0..r.u16()? {
295 meshes.push(Mesh { texture: r.str()?, quads: r.quads()? });
296 }
297 f.captures.push(Capture { source, meshes });
298 }
299 Some(f)
300 }
301
302 pub fn encode(&self) -> Vec<u8> {
304 let mut b = vec![1u8];
305 put_str(&mut b, &self.uuid);
306 put_str(&mut b, &self.entity_type);
307 for f in [self.partial, self.age, self.limb_swing, self.limb_amount, self.head_yaw, self.head_pitch, self.body_yaw, self.pitch] {
308 put_f32(&mut b, f);
309 }
310 for p in self.pos {
311 b.extend_from_slice(&p.to_le_bytes());
312 }
313 put_vec(&mut b, self.velocity);
314 b.extend_from_slice(&self.flags.to_le_bytes());
315 b.extend_from_slice(&(self.captures.len() as u16).to_le_bytes());
316 for c in &self.captures {
317 put_str(&mut b, &c.source);
318 b.extend_from_slice(&(c.meshes.len() as u16).to_le_bytes());
319 for m in &c.meshes {
320 put_str(&mut b, &m.texture);
321 put_quads(&mut b, &m.quads);
322 }
323 }
324 b
325 }
326
327 pub fn has(&self, flag: u32) -> bool {
328 self.flags & flag != 0
329 }
330
331 pub fn capture(&self, source: &str) -> Option<&Capture> {
333 self.captures.iter().find(|c| c.source == source)
334 }
335
336 pub fn seconds(&self) -> f32 {
338 self.age / 20.0
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn a_frame_reads_back() {
348 let q = Quad { pos: [Vec3::new(1.0, 2.0, 3.0); 4], uv: [[0.5, 0.25]; 4], normal: Vec3::new(0.0, 1.0, 0.0) };
349 let f = Frame {
350 uuid: "u".into(),
351 entity_type: "minecraft:player".into(),
352 age: 12.5,
353 pos: [1.0, 2.0, 3.0],
354 flags: 5,
355 captures: vec![Capture { source: "self".into(), meshes: vec![Mesh { texture: "t".into(), quads: vec![q] }] }],
356 ..Default::default()
357 };
358 assert_eq!(Frame::decode(&f.encode()), Some(f));
359 }
360
361 #[test]
362 fn ops_are_records() {
363 let mut o = Ops::default();
364 o.hide();
365 o.disguise("minecraft:zombie");
366 assert_eq!(o.0[0], HIDE);
367 assert_eq!(o.0[1], DISGUISE);
368 assert_eq!(&o.0[2..4], &16u16.to_le_bytes());
369 }
370}