concinnity_asset/
model.rs1use crate::{AssetId, MaterialHandle, MeshHandle, de_opt_material_handle, de_opt_mesh_handle};
4use alloc::vec::Vec;
5
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
8pub struct SubMeshRef {
9 #[serde(default, deserialize_with = "de_opt_mesh_handle")]
11 pub mesh: Option<MeshHandle>,
12 #[serde(default, deserialize_with = "de_opt_material_handle")]
14 pub material: Option<MaterialHandle>,
15}
16
17#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
26#[serde(default)]
27pub struct Model {
28 #[serde(skip)]
30 pub asset_id: AssetId,
31 pub meshes: Vec<SubMeshRef>,
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn a_blank_model_has_no_sub_meshes() {
41 let m = Model::default();
42 assert!(m.meshes.is_empty());
43 assert_eq!(m.asset_id, AssetId::default());
44 }
45
46 #[test]
47 fn a_sub_mesh_may_declare_geometry_without_a_material() {
48 crate::test_support::install_resolvers();
49 let s: SubMeshRef = serde_json::from_str(r#"{"mesh":"body"}"#).unwrap();
50 assert_eq!(s.mesh, Some(MeshHandle(4)));
51 assert_eq!(s.material, None);
53 let s: SubMeshRef = serde_json::from_str("{}").unwrap();
54 assert_eq!(s.mesh, None);
55 assert_eq!(s.material, None);
56 }
57
58 #[test]
59 fn an_imported_model_keeps_its_sub_mesh_order_through_postcard() {
60 crate::test_support::install_resolvers();
61 let m: Model = serde_json::from_str(
62 r#"{"meshes":[{"mesh":"body","material":"skin"},
63 {"mesh":"trim","material":"metal"}]}"#,
64 )
65 .unwrap();
66 assert_eq!(m.meshes.len(), 2);
67
68 let bytes = postcard::to_allocvec(&m).unwrap();
69 let back: Model = postcard::from_bytes(&bytes).unwrap();
70 assert_eq!(back.meshes[0].mesh, Some(MeshHandle(4)));
71 assert_eq!(back.meshes[0].material, Some(MaterialHandle(4)));
72 assert_eq!(back.meshes[1].mesh, Some(MeshHandle(4)));
73 assert_eq!(back.meshes[1].material, Some(MaterialHandle(5)));
74 assert_eq!(back.asset_id, AssetId::default());
75 }
76}