Skip to main content

primase_core/
primitive_keys.rs

1//! Values a shader's variant key reads from each primitive, independent of the game's mesh component.
2use crate::{Component, Entity, Geometry, JsonError, Mesh, World};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7/// `core.primitive_keys`, on a mesh: one u32 per primitive for every named field. A
8/// `shader::VariantKey::field` names one of these exact keys; core does not interpret its namespace.
9#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
10pub struct PrimitiveKeys(pub BTreeMap<String, Vec<u32>>);
11
12impl Component for PrimitiveKeys {
13    const NAME: &'static str = "core.primitive_keys";
14    const GEOMETRY: Geometry = Geometry::PerPrimitive;
15    fn to_json(&self) -> Result<Option<Value>, JsonError> {
16        crate::json::to_value(self).map(Some)
17    }
18    fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
19        crate::world::Linearity::Invariant
20    }
21    fn remap_primitives(&self, from: &[usize]) -> Option<Result<Self, String>> {
22        Some(
23            self.0
24                .iter()
25                .map(|(field, values)| {
26                    crate::remap_list(values, from)
27                        .map(|values| (field.clone(), values))
28                        .map_err(|error| format!("{field}: {error}"))
29                })
30                .collect::<Result<BTreeMap<_, _>, _>>()
31                .map(Self),
32        )
33    }
34    fn verify(&self, e: Entity, w: &World) -> Result<(), String> {
35        let mesh = w
36            .get::<Mesh>(e)
37            .ok_or("primitive keys on an entity without core.mesh")?;
38        for (field, values) in &self.0 {
39            if field.trim().is_empty() {
40                return Err("empty primitive-key field".into());
41            }
42            if values.len() != mesh.primitives().len() {
43                return Err(format!(
44                    "{field}: {} values for {} primitives",
45                    values.len(),
46                    mesh.primitives().len()
47                ));
48            }
49        }
50        Ok(())
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use crate::{Primitive, Topology};
58    fn fixture() -> PrimitiveKeys {
59        PrimitiveKeys(BTreeMap::from([
60            ("first.variant".into(), vec![0, 1, 2]),
61            ("another.layout".into(), vec![3, 4, u32::MAX]),
62        ]))
63    }
64    #[test]
65    fn each_field_matches_the_mesh_and_names_are_not_empty() {
66        let mut w = World::new();
67        let e = w.spawn();
68        let mut keys = fixture();
69        assert!(keys.verify(e, &w).is_err());
70        w.insert(
71            e,
72            Mesh::new(
73                (0..3)
74                    .map(|_| Primitive {
75                        topology: Topology::Triangles,
76                        streams: Default::default(),
77                        indices: None,
78                        material: None,
79                    })
80                    .collect(),
81            ),
82        );
83        assert!(keys.verify(e, &w).is_ok());
84        keys.0.insert(" ".into(), vec![0; 3]);
85        assert!(keys.verify(e, &w).is_err());
86        keys.0.remove(" ");
87        keys.0.insert("short".into(), vec![0]);
88        assert!(keys.verify(e, &w).is_err());
89    }
90    #[test]
91    fn every_field_remaps_together_without_partial_results() {
92        let keys = fixture();
93        let mapped = keys.remap_primitives(&[2, 0, 2]).unwrap().unwrap();
94        assert_eq!(mapped.0["first.variant"], vec![2, 0, 2]);
95        assert_eq!(mapped.0["another.layout"], vec![u32::MAX, 3, u32::MAX]);
96        assert!(keys.remap_primitives(&[3]).unwrap().is_err());
97        assert!(
98            keys.remap_primitives(&[])
99                .unwrap()
100                .unwrap()
101                .0
102                .values()
103                .all(Vec::is_empty)
104        );
105        assert_eq!(keys, fixture());
106    }
107    #[test]
108    fn json_round_trip_keeps_fields_order_and_full_u32_values() {
109        let keys = fixture();
110        let json = keys.to_json().unwrap().unwrap();
111        assert_eq!(serde_json::from_value::<PrimitiveKeys>(json).unwrap(), keys);
112    }
113    #[test]
114    fn rebuilding_a_mesh_carries_both_key_fields_and_draw_groups_atomically() {
115        let mut w = World::new();
116        let e = w.spawn();
117        w.insert(
118            e,
119            Mesh::new(
120                (0..3)
121                    .map(|_| Primitive {
122                        topology: Topology::Triangles,
123                        streams: Default::default(),
124                        indices: None,
125                        material: None,
126                    })
127                    .collect(),
128            ),
129        );
130        w.insert(e, fixture());
131        w.insert(
132            e,
133            crate::DrawGroups {
134                scope: "character".into(),
135                groups: vec![Some(0), None, Some(63)],
136            },
137        );
138        w.rebuild_primitives(e, |mesh| {
139            Ok((
140                vec![mesh.primitives()[2].clone(), mesh.primitives()[0].clone()],
141                vec![2, 0],
142            ))
143        })
144        .unwrap();
145        assert_eq!(w.get::<PrimitiveKeys>(e).unwrap().0["first.variant"], vec![2, 0]);
146        assert_eq!(
147            w.get::<PrimitiveKeys>(e).unwrap().0["another.layout"],
148            vec![u32::MAX, 3]
149        );
150        assert_eq!(
151            w.get::<crate::DrawGroups>(e).unwrap().groups,
152            vec![Some(63), Some(0)]
153        );
154        assert!(w.get::<PrimitiveKeys>(e).unwrap().verify(e, &w).is_ok());
155        assert!(w.get::<crate::DrawGroups>(e).unwrap().verify(e, &w).is_ok());
156        let before_keys = w.get::<PrimitiveKeys>(e).unwrap().clone();
157        let before_groups = w.get::<crate::DrawGroups>(e).unwrap().clone();
158        assert!(
159            w.rebuild_primitives(e, |mesh| Ok((vec![mesh.primitives()[0].clone()], vec![2])))
160                .is_err()
161        );
162        assert_eq!(w.get::<Mesh>(e).unwrap().primitives().len(), 2);
163        assert_eq!(w.get::<PrimitiveKeys>(e), Some(&before_keys));
164        assert_eq!(w.get::<crate::DrawGroups>(e), Some(&before_groups));
165    }
166}