1use crate::{Component, Entity, Geometry, JsonError, Mesh, World};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[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}