primase_core/
draw_groups.rs1use crate::{Component, Entity, Geometry, JsonError, Mesh, World};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(deny_unknown_fields)]
11pub struct DrawGroups {
12 pub scope: String,
13 pub groups: Vec<Option<u32>>,
14}
15
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(deny_unknown_fields)]
21pub struct DrawGroupDefaults {
22 pub scope: String,
23 pub shown: u64,
24}
25
26impl Component for DrawGroups {
27 const NAME: &'static str = "core.draw_groups";
28 const GEOMETRY: Geometry = Geometry::PerPrimitive;
29 fn to_json(&self) -> Result<Option<Value>, JsonError> {
30 crate::json::to_value(self).map(Some)
31 }
32 fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
33 crate::world::Linearity::Invariant
34 }
35 fn remap_primitives(&self, from: &[usize]) -> Option<Result<Self, String>> {
36 Some(crate::remap_list(&self.groups, from).map(|groups| Self {
37 scope: self.scope.clone(),
38 groups,
39 }))
40 }
41 fn verify(&self, e: Entity, w: &World) -> Result<(), String> {
42 if self.scope.trim().is_empty() {
43 return Err("empty draw-group scope".into());
44 }
45 let mesh = w
46 .get::<Mesh>(e)
47 .ok_or("draw groups on an entity without core.mesh")?;
48 if self.groups.len() != mesh.primitives().len() {
49 return Err(format!(
50 "{} groups for {} primitives",
51 self.groups.len(),
52 mesh.primitives().len()
53 ));
54 }
55 if let Some((primitive, group)) = self
56 .groups
57 .iter()
58 .enumerate()
59 .find_map(|(i, group)| group.filter(|group| *group >= 64).map(|group| (i, group)))
60 {
61 return Err(format!(
62 "primitive {primitive}: group {group} is outside the 64-bit default mask"
63 ));
64 }
65 Ok(())
66 }
67}
68
69impl Component for DrawGroupDefaults {
70 const NAME: &'static str = "core.draw_group_defaults";
71 const GEOMETRY: Geometry = Geometry::None;
72 fn to_json(&self) -> Result<Option<Value>, JsonError> {
73 crate::json::to_value(self).map(Some)
74 }
75 fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
76 crate::world::Linearity::Invariant
77 }
78 fn verify(&self, _e: Entity, _w: &World) -> Result<(), String> {
79 if self.scope.trim().is_empty() {
80 return Err("empty draw-group default scope".into());
81 }
82 Ok(())
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::{Primitive, Topology};
90 fn mesh() -> (World, Entity) {
91 let mut w = World::new();
92 let e = w.spawn();
93 w.insert(
94 e,
95 Mesh::new(
96 (0..3)
97 .map(|_| Primitive {
98 topology: Topology::Triangles,
99 streams: Default::default(),
100 indices: None,
101 material: None,
102 })
103 .collect(),
104 ),
105 );
106 (w, e)
107 }
108 fn groups() -> DrawGroups {
109 DrawGroups {
110 scope: "character".into(),
111 groups: vec![Some(0), None, Some(63)],
112 }
113 }
114 #[test]
115 fn groups_validate_count_scope_and_mask_width() {
116 let (w, e) = mesh();
117 let mut g = groups();
118 assert!(g.verify(e, &w).is_ok());
119 g.groups[2] = Some(64);
120 assert!(g.verify(e, &w).is_err());
121 g.groups[2] = Some(63);
122 g.groups.pop();
123 assert!(g.verify(e, &w).is_err());
124 let mut g = groups();
125 g.scope.clear();
126 assert!(g.verify(e, &w).is_err());
127 let mut empty = World::new();
128 let e = empty.spawn();
129 assert!(groups().verify(e, &empty).is_err());
130 }
131 #[test]
132 fn primitive_remap_reorders_repeats_drops_and_refuses_out_of_range() {
133 let g = groups();
134 let mapped = g.remap_primitives(&[2, 0, 2, 1]).unwrap().unwrap();
135 assert_eq!(mapped.groups, vec![Some(63), Some(0), Some(63), None]);
136 assert_eq!(mapped.scope, g.scope);
137 assert!(g.remap_primitives(&[]).unwrap().unwrap().groups.is_empty());
138 assert!(g.remap_primitives(&[3]).unwrap().is_err());
139 assert_eq!(g.groups, vec![Some(0), None, Some(63)]);
140 }
141 #[test]
142 fn defaults_preserve_every_bit_and_belong_to_a_scope() {
143 let (w, e) = mesh();
144 let mut d = DrawGroupDefaults {
145 scope: "character".into(),
146 shown: 1u64 << 63,
147 };
148 assert!(d.verify(e, &w).is_ok());
149 assert!(d.remap_primitives(&[2, 0]).is_none());
150 let json = d.to_json().unwrap().unwrap();
151 assert_eq!(serde_json::from_value::<DrawGroupDefaults>(json).unwrap(), d);
152 d.scope = " ".into();
153 assert!(d.verify(e, &w).is_err());
154 }
155 #[test]
156 fn group_json_round_trip_keeps_none_and_order() {
157 let g = groups();
158 let json = g.to_json().unwrap().unwrap();
159 assert_eq!(
160 json,
161 serde_json::json!({"scope":"character","groups":[0,null,63]})
162 );
163 assert_eq!(serde_json::from_value::<DrawGroups>(json).unwrap(), g);
164 }
165}