dreamwell_engine/content/
import_scene.rs1use serde::{Deserialize, Serialize};
4
5use super::animation::ImportedAnimation;
6use super::mesh::ImportedMesh;
7use crate::game_object::Transform;
8use crate::material::LitMaterialDesc;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct SceneNode {
13 pub name: String,
15 pub transform: Transform,
17 pub mesh_index: Option<usize>,
19 pub material_index: Option<usize>,
21 pub children: Vec<usize>,
23}
24
25#[derive(Debug, Clone, Default, Serialize, Deserialize)]
27pub struct ImportedMaterial {
28 pub name: String,
30 pub desc: LitMaterialDesc,
32}
33
34#[derive(Debug, Clone, Default, Serialize, Deserialize)]
37pub struct ImportedScene {
38 pub name: String,
40 pub nodes: Vec<SceneNode>,
42 pub roots: Vec<usize>,
44 pub meshes: Vec<ImportedMesh>,
46 pub materials: Vec<ImportedMaterial>,
48 pub animations: Vec<ImportedAnimation>,
50}
51
52impl ImportedScene {
53 pub fn total_vertices(&self) -> usize {
55 self.meshes.iter().map(|m| m.vertex_count()).sum()
56 }
57
58 pub fn total_triangles(&self) -> usize {
60 self.meshes.iter().map(|m| m.triangle_count()).sum()
61 }
62
63 pub fn validate(&self) -> Result<(), String> {
65 let mesh_count = self.meshes.len();
66 let material_count = self.materials.len();
67 let node_count = self.nodes.len();
68
69 for (i, node) in self.nodes.iter().enumerate() {
70 if let Some(mi) = node.mesh_index {
71 if mi >= mesh_count {
72 return Err(format!(
73 "content_scene_mesh_oob:node[{i}] references mesh {mi}, only {mesh_count} meshes"
74 ));
75 }
76 }
77 if let Some(mi) = node.material_index {
78 if mi >= material_count {
79 return Err(format!(
80 "content_scene_material_oob:node[{i}] references material {mi}, only {material_count} materials"
81 ));
82 }
83 }
84 for &child in &node.children {
85 if child >= node_count {
86 return Err(format!(
87 "content_scene_child_oob:node[{i}] references child {child}, only {node_count} nodes"
88 ));
89 }
90 }
91 }
92
93 for (i, mesh) in self.meshes.iter().enumerate() {
94 mesh.validate().map_err(|e| format!("mesh[{i}] '{}': {e}", mesh.name))?;
95 }
96
97 for anim in &self.animations {
98 anim.validate()?;
99 }
100
101 Ok(())
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::content::mesh::ImportedMesh;
109
110 fn simple_scene() -> ImportedScene {
111 let mesh = ImportedMesh {
112 name: "Cube".into(),
113 positions: vec![[0.0; 3]; 3],
114 normals: vec![[0.0, 0.0, 1.0]; 3],
115 uvs: vec![[0.0; 2]; 3],
116 indices: vec![0, 1, 2],
117 };
118 let mat = ImportedMaterial {
119 name: "Default".into(),
120 desc: LitMaterialDesc::default(),
121 };
122 let node = SceneNode {
123 name: "Root".into(),
124 transform: Transform::default(),
125 mesh_index: Some(0),
126 material_index: Some(0),
127 children: vec![],
128 };
129 ImportedScene {
130 name: "TestScene".into(),
131 nodes: vec![node],
132 roots: vec![0],
133 meshes: vec![mesh],
134 materials: vec![mat],
135 animations: vec![],
136 }
137 }
138
139 #[test]
140 fn valid_scene() {
141 let scene = simple_scene();
142 assert!(scene.validate().is_ok());
143 assert_eq!(scene.total_vertices(), 3);
144 assert_eq!(scene.total_triangles(), 1);
145 }
146
147 #[test]
148 fn mesh_oob() {
149 let mut scene = simple_scene();
150 scene.nodes[0].mesh_index = Some(99);
151 assert!(scene.validate().unwrap_err().contains("mesh_oob"));
152 }
153
154 #[test]
155 fn material_oob() {
156 let mut scene = simple_scene();
157 scene.nodes[0].material_index = Some(99);
158 assert!(scene.validate().unwrap_err().contains("material_oob"));
159 }
160
161 #[test]
162 fn child_oob() {
163 let mut scene = simple_scene();
164 scene.nodes[0].children.push(99);
165 assert!(scene.validate().unwrap_err().contains("child_oob"));
166 }
167
168 #[test]
169 fn empty_scene() {
170 let scene = ImportedScene::default();
171 assert!(scene.validate().is_ok());
172 assert_eq!(scene.total_vertices(), 0);
173 }
174
175 #[test]
176 fn imported_material_default() {
177 let mat = ImportedMaterial::default();
178 assert!(mat.name.is_empty());
179 assert_eq!(mat.desc.roughness, 0.5);
180 }
181}