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