primase_core/
material_parameters.rs1use crate::{Component, Entity, Geometry, JsonError, World};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
12pub struct MaterialParameters(pub BTreeMap<String, Vec<f32>>);
13
14impl Component for MaterialParameters {
15 const NAME: &'static str = "core.material_parameters";
16 const GEOMETRY: Geometry = Geometry::None;
17 fn to_json(&self) -> Result<Option<Value>, JsonError> {
18 crate::json::to_value(self).map(Some)
19 }
20 fn apply_linear(&self, _l: &crate::Linear) -> crate::world::Linearity<Self> {
21 crate::world::Linearity::Invariant
22 }
23 fn verify(&self, _e: Entity, _w: &World) -> Result<(), String> {
24 for (name, values) in &self.0 {
25 if name.trim().is_empty() {
26 return Err("empty material parameter name".into());
27 }
28 if !(1..=4).contains(&values.len()) {
29 return Err(format!("{name}: {} values, expected 1 to 4", values.len()));
30 }
31 if !values.iter().all(|value| value.is_finite()) {
32 return Err(format!("{name}: non-finite value"));
33 }
34 }
35 Ok(())
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 #[test]
43 fn widths_are_one_to_four_and_values_are_finite() {
44 let mut w = World::new();
45 let e = w.spawn();
46 for width in 0..=5 {
47 let p = MaterialParameters(BTreeMap::from([("opaque name".into(), vec![1.; width])]));
48 assert_eq!(p.verify(e, &w).is_ok(), (1..=4).contains(&width));
49 }
50 for value in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
51 let p = MaterialParameters(BTreeMap::from([("value".into(), vec![value])]));
52 assert!(p.verify(e, &w).is_err());
53 assert!(p.to_json().is_err());
54 }
55 assert!(
56 MaterialParameters(BTreeMap::from([(" ".into(), vec![0.])]))
57 .verify(e, &w)
58 .is_err()
59 );
60 assert!(MaterialParameters::default().verify(e, &w).is_ok());
61 }
62 #[test]
63 fn json_round_trip_preserves_names_values_and_signed_zero() {
64 let p = MaterialParameters(BTreeMap::from([
65 ("a tint".into(), vec![0.1, 0.2, 0.3, 1.]),
66 ("a scalar".into(), vec![-0.]),
67 ]));
68 let restored: MaterialParameters = serde_json::from_value(p.to_json().unwrap().unwrap()).unwrap();
69 assert_eq!(restored, p);
70 assert_eq!(restored.0["a scalar"][0].to_bits(), (-0f32).to_bits());
71 assert!(p.remap_primitives(&[2, 0, 2]).is_none());
73 }
74}