Skip to main content

yog_body/
wire.rs

1//! What the host and a mod say each frame about one entity (see
2//! `docs/design/yog-body.md`): the [`Frame`] the host sends, the [`Ops`]
3//! the mod answers with. Both are Yog Data, carried by the bus's
4//! `client/render/body` event; the hosts read and write them through the
5//! records generated from them.
6
7use yog_data::Schema;
8use yog_data::with::Raw;
9use yog_graphics::Color;
10use yog_ids::time::TICKS_PER_SECOND as TICKS;
11
12use crate::math::{Mat4, Vec3};
13use crate::model::Quad;
14
15/// Floats a vertex takes on the wire: x y z, u v, nx ny nz.
16const VERTEX_FLOATS: usize = 8;
17/// Vertices a quad has.
18const QUAD_VERTICES: usize = 4;
19
20/// One entity, this frame.
21#[derive(Schema, Debug, Clone, Default, PartialEq)]
22#[data(rename = "BodyFrame")]
23pub struct Frame {
24    pub uuid: String,
25    /// `minecraft:player`, `minecraft:zombie`…
26    pub entity_type: String,
27    /// Between the last tick and the next (0–1).
28    pub partial: f32,
29    /// Ticks it has lived, with `partial`.
30    pub age: f32,
31    /// How far its legs have walked, and how fast (vanilla's limb swing).
32    pub limb_swing: f32,
33    pub limb_amount: f32,
34    /// The head's turn from the body, and its pitch (degrees).
35    pub head_yaw: f32,
36    pub head_pitch: f32,
37    /// Degrees.
38    pub body_yaw: f32,
39    pub pitch: f32,
40    pub pos: [f64; 3],
41    /// Blocks per tick.
42    pub velocity: Vec3,
43    /// `yog_core::query::entity_flags` bits.
44    pub flags: u32,
45    /// The world's time in ticks, with `partial` — the same on every
46    /// client, so clips timed by it play alike for everyone.
47    pub game_time: f64,
48    /// What was captured last frame (see [`Ops::capture`]).
49    #[data(default)]
50    pub captures: Vec<Capture>,
51    /// What the entity keeps by key (`entity::SetData`), as this client was told.
52    #[data(default)]
53    pub data: Vec<Kept>,
54}
55
56/// A value the entity keeps, by key.
57#[derive(Schema, Debug, Clone, Default, PartialEq)]
58#[data(rename = "BodyKept")]
59pub struct Kept {
60    pub key: String,
61    #[data(with = "Raw")]
62    pub value: Vec<u8>,
63}
64
65/// The posed geometry of an entity (entity space, see the design doc).
66#[derive(Schema, Debug, Clone, Default, PartialEq)]
67#[data(rename = "BodyCapture")]
68pub struct Capture {
69    /// `self`, or the entity type captured.
70    pub source: String,
71    pub meshes: Vec<Mesh>,
72}
73
74/// Quads with one texture: [`VERTEX_FLOATS`] floats a vertex, four vertices a quad.
75#[derive(Schema, Debug, Clone, Default, PartialEq)]
76#[data(rename = "BodyMesh")]
77pub struct Mesh {
78    pub texture: String,
79    pub verts: Vec<f32>,
80}
81
82impl Mesh {
83    pub fn new(texture: &str, quads: &[Quad]) -> Mesh {
84        Mesh {
85            texture: texture.to_string(),
86            verts: verts(quads),
87        }
88    }
89
90    /// Its quads (the whole ones).
91    pub fn quads(&self) -> Vec<Quad> {
92        self.verts
93            .as_chunks::<{ VERTEX_FLOATS * QUAD_VERTICES }>()
94            .0
95            .iter()
96            .map(|q| {
97                let vertex = |i: usize| &q[i * VERTEX_FLOATS..(i + 1) * VERTEX_FLOATS];
98                Quad {
99                    pos: std::array::from_fn(|i| {
100                        Vec3::new(vertex(i)[0], vertex(i)[1], vertex(i)[2])
101                    }),
102                    uv: std::array::from_fn(|i| [vertex(i)[3], vertex(i)[4]]),
103                    normal: Vec3::new(vertex(0)[5], vertex(0)[6], vertex(0)[7]),
104                }
105            })
106            .collect()
107    }
108}
109
110/// `quads` as the wire's vertices.
111fn verts(quads: &[Quad]) -> Vec<f32> {
112    let mut out = Vec::with_capacity(quads.len() * QUAD_VERTICES * VERTEX_FLOATS);
113    for q in quads {
114        for i in 0..QUAD_VERTICES {
115            out.extend([q.pos[i].x, q.pos[i].y, q.pos[i].z, q.uv[i][0], q.uv[i][1]]);
116            out.extend([q.normal.x, q.normal.y, q.normal.z]);
117        }
118    }
119    out
120}
121
122/// Where a mesh's vertices are.
123#[derive(Debug, Clone, PartialEq)]
124pub enum Anchor {
125    /// Blocks, y up, feet at the origin, after the body's yaw.
126    Entity,
127    /// The vanilla model's space: pixels, y down.
128    Model,
129    /// A vanilla part's space (pixels), wherever the part is posed.
130    Bone(String),
131    /// A block's space: pixels 0–16, y up, as block models (block bodies).
132    Block,
133}
134
135/// [`Anchor`] as the wire names it; a bone's name goes beside it.
136#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq, Default)]
137#[data(rename = "BodyAnchor")]
138pub enum AnchorKind {
139    #[default]
140    Entity,
141    Model,
142    Bone,
143    Block,
144}
145
146/// How a mesh looks: how it's drawn, whether it shines, its tint (alpha
147/// counts in [`Mode::Translucent`]).
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub struct Look {
150    pub mode: Mode,
151    /// The enchantment shine.
152    pub glint: bool,
153    pub tint: Color,
154}
155
156impl Default for Look {
157    fn default() -> Self {
158        Look {
159            mode: Mode::Cutout,
160            glint: false,
161            tint: Color::WHITE,
162        }
163    }
164}
165
166/// How a mesh is drawn.
167#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq, Default)]
168#[data(rename = "BodyMode")]
169pub enum Mode {
170    /// Opaque, transparent pixels cut out, both sides.
171    #[default]
172    Cutout,
173    /// Blended by its alpha.
174    Translucent,
175    /// Glows: full bright, added.
176    Emissive,
177}
178
179/// What to change about a vanilla part.
180#[derive(Schema, Debug, Clone, Copy, PartialEq, Default)]
181#[data(rename = "BodyBoneOp")]
182pub struct BoneOp {
183    /// Radians; replaces vanilla's turn if `set_rotation`, else adds.
184    pub rotation: Vec3,
185    pub set_rotation: bool,
186    /// Pixels; replaces vanilla's offset if `set_position`, else adds.
187    pub position: Vec3,
188    pub set_position: bool,
189    pub scale: Option<Vec3>,
190    /// `Some(false)` hides it, `Some(true)` shows it.
191    pub visible: Option<bool>,
192}
193
194/// A vanilla part to change, and how.
195#[derive(Schema, Debug, Clone, PartialEq, Default)]
196#[data(rename = "BodyBone")]
197pub struct Bone {
198    pub name: String,
199    pub op: BoneOp,
200}
201
202/// A mesh to draw: where, with what texture, how it looks ([`Look`] as
203/// numbers: a tint is ARGB).
204#[derive(Schema, Debug, Clone, PartialEq, Default)]
205#[data(rename = "BodyDraw")]
206pub struct Draw {
207    pub anchor: AnchorKind,
208    /// The vanilla part, for [`AnchorKind::Bone`].
209    pub bone: String,
210    pub mesh: Mesh,
211    pub mode: Mode,
212    pub glint: bool,
213    pub tint: u32,
214}
215
216/// A mod's answer — every handler's add up, in order.
217#[derive(Schema, Debug, Clone, Default, PartialEq)]
218#[data(rename = "BodyOps")]
219pub struct Ops {
220    /// The whole body's turn in entity space (after the body's yaw), column-major.
221    pub root: Option<Vec<f32>>,
222    #[data(default)]
223    pub bones: Vec<Bone>,
224    #[data(default)]
225    pub draws: Vec<Draw>,
226    /// Drawn as one of this entity type instead.
227    pub disguise: Option<String>,
228    /// The entity itself isn't drawn (its model, layers and name).
229    #[data(default)]
230    pub hide: bool,
231    /// Sources to capture for the next frame.
232    #[data(default)]
233    pub captures: Vec<String>,
234}
235
236impl Ops {
237    pub fn is_empty(&self) -> bool {
238        *self == Ops::default()
239    }
240
241    /// Turns the whole body by `m` in entity space (after the body's yaw).
242    pub fn root(&mut self, m: &Mat4) {
243        let turned = match self
244            .root
245            .as_deref()
246            .and_then(|r| <[f32; 16]>::try_from(r).ok())
247        {
248            Some(r) => Mat4(r) * *m,
249            None => *m,
250        };
251        self.root = Some(turned.0.to_vec());
252    }
253
254    /// Poses the vanilla part `bone` (after vanilla animated it).
255    pub fn bone(&mut self, bone: &str, op: &BoneOp) {
256        self.bones.push(Bone {
257            name: bone.to_string(),
258            op: *op,
259        });
260    }
261
262    /// Draws `quads` with `texture` at `anchor`, looking `look`.
263    pub fn mesh(&mut self, anchor: &Anchor, texture: &str, look: Look, quads: &[Quad]) {
264        if quads.is_empty() {
265            return;
266        }
267        let (anchor, bone) = match anchor {
268            Anchor::Entity => (AnchorKind::Entity, ""),
269            Anchor::Model => (AnchorKind::Model, ""),
270            Anchor::Bone(n) => (AnchorKind::Bone, n.as_str()),
271            Anchor::Block => (AnchorKind::Block, ""),
272        };
273        let mesh = Mesh::new(texture, quads);
274        self.draws.push(Draw {
275            anchor,
276            bone: bone.to_string(),
277            mesh,
278            mode: look.mode,
279            glint: look.glint,
280            tint: look.tint.0,
281        });
282    }
283
284    /// Draws the entity as one of `entity_type`, animated like it.
285    pub fn disguise(&mut self, entity_type: &str) {
286        self.disguise = Some(entity_type.to_string());
287    }
288
289    /// Doesn't draw the entity itself (its model, layers and name).
290    pub fn hide(&mut self) {
291        self.hide = true;
292    }
293
294    /// Asks for the posed geometry of the entity (`"self"`) or of a type
295    /// posed like it; it comes in the next frame's [`Frame::captures`].
296    pub fn capture(&mut self, source: &str) {
297        self.captures.push(source.to_string());
298    }
299
300    /// `other`'s ops after these (another handler's): turns compose, the
301    /// rest adds up, a later disguise wins.
302    pub fn add(&mut self, other: Ops) {
303        if let Some(r) = other
304            .root
305            .as_deref()
306            .and_then(|r| <[f32; 16]>::try_from(r).ok())
307        {
308            self.root(&Mat4(r));
309        }
310        self.bones.extend(other.bones);
311        self.draws.extend(other.draws);
312        self.disguise = other.disguise.or(self.disguise.take());
313        self.hide |= other.hide;
314        self.captures.extend(other.captures);
315    }
316}
317
318impl Frame {
319    pub fn has(&self, flag: u32) -> bool {
320        self.flags & flag != 0
321    }
322
323    /// What was captured from `source` last frame.
324    pub fn capture(&self, source: &str) -> Option<&Capture> {
325        self.captures.iter().find(|c| c.source == source)
326    }
327
328    /// The entity's age in seconds (differs between clients: each counts
329    /// from when it saw the entity).
330    pub fn seconds(&self) -> f32 {
331        self.age / TICKS as f32
332    }
333
334    /// The world's time in seconds — for clips everyone sees alike.
335    pub fn world_seconds(&self) -> f32 {
336        (self.game_time / f64::from(TICKS)) as f32
337    }
338
339    /// The value the entity keeps under `key`.
340    pub fn data(&self, key: &str) -> Option<&[u8]> {
341        self.data
342            .iter()
343            .find(|k| k.key == key)
344            .map(|k| k.value.as_slice())
345    }
346}
347
348#[cfg(test)]
349mod tests;