concinnity_asset/
instanced_prop.rs1use crate::{
4 AssetId, MaterialHandle, MeshHandle, TextureHandle, de_opt_material_handle, de_opt_mesh_handle,
5 de_opt_texture_handle,
6};
7use alloc::vec::Vec;
8
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
11#[serde(default)]
12pub struct InstanceTransform {
13 pub position: [f32; 3],
15 pub rotation_deg: [f32; 3],
17 pub scale: [f32; 3],
19}
20
21impl Default for InstanceTransform {
22 fn default() -> Self {
23 Self {
24 position: [0.0, 0.0, 0.0],
25 rotation_deg: [0.0, 0.0, 0.0],
26 scale: [1.0, 1.0, 1.0],
27 }
28 }
29}
30
31#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
40#[serde(default)]
41pub struct InstancedProp {
42 #[serde(skip)]
44 pub asset_id: AssetId,
45 #[serde(deserialize_with = "de_opt_mesh_handle")]
48 pub mesh: Option<MeshHandle>,
49 #[serde(deserialize_with = "de_opt_material_handle")]
51 pub material: Option<MaterialHandle>,
52 #[serde(deserialize_with = "de_opt_texture_handle")]
54 pub texture: Option<TextureHandle>,
55 pub instances: Vec<InstanceTransform>,
57 pub cull_distance: f32,
59}
60
61impl Default for InstancedProp {
62 fn default() -> Self {
63 Self {
64 asset_id: AssetId::default(),
65 mesh: None,
66 material: None,
67 texture: None,
68 instances: Vec::new(),
69 cull_distance: 0.0,
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn a_blank_instance_is_an_untransformed_copy() {
80 let t = InstanceTransform::default();
81 assert_eq!(t.position, [0.0, 0.0, 0.0]);
82 assert_eq!(t.rotation_deg, [0.0, 0.0, 0.0]);
83 assert_eq!(t.scale, [1.0, 1.0, 1.0]);
85 }
86
87 #[test]
88 fn a_blank_prop_draws_nothing_and_never_culls() {
89 let p = InstancedProp::default();
90 assert!(p.instances.is_empty());
91 assert!(p.mesh.is_none());
92 assert!(p.material.is_none());
93 assert!(p.texture.is_none());
94 assert_eq!(p.cull_distance, 0.0);
96 }
97
98 #[test]
99 fn an_authored_instance_list_parses_and_round_trips_through_postcard() {
100 crate::test_support::install_resolvers();
101 let p: InstancedProp = serde_json::from_str(
102 r#"{"mesh":"tree_mesh","material":"bark","texture":"bark_tex","cull_distance":120,
103 "instances":[{"position":[1,0,2]},{"position":[3,0,4],"scale":[2,2,2]}]}"#,
104 )
105 .unwrap();
106 assert_eq!(p.mesh, Some(MeshHandle(9)));
107 assert_eq!(p.material, Some(MaterialHandle(4)));
108 assert_eq!(p.texture, Some(TextureHandle(8)));
109 assert_eq!(p.instances.len(), 2);
110 assert_eq!(p.instances[0].scale, [1.0, 1.0, 1.0]);
112 assert_eq!(p.instances[1].scale, [2.0, 2.0, 2.0]);
113
114 let bytes = postcard::to_allocvec(&p).unwrap();
115 let back: InstancedProp = postcard::from_bytes(&bytes).unwrap();
116 assert_eq!(back.instances.len(), 2);
117 assert_eq!(back.instances[1].position, [3.0, 0.0, 4.0]);
118 assert_eq!(back.cull_distance, 120.0);
119 assert_eq!(back.asset_id, AssetId::default());
120 }
121}