1use crate::core::renderer::{PipelineType, Vertex};
7use glam::{Mat4, Vec3, Vec4};
8use std::collections::HashMap;
9
10pub type NodeId = u64;
12
13#[derive(Debug, Clone)]
15pub struct SceneNode {
16 pub id: NodeId,
17 pub name: String,
18 pub transform: Mat4,
19 pub visible: bool,
20 pub cast_shadows: bool,
21 pub receive_shadows: bool,
22
23 pub parent: Option<NodeId>,
25 pub children: Vec<NodeId>,
26
27 pub render_data: Option<RenderData>,
29
30 pub bounds: BoundingBox,
32
33 pub lod_levels: Vec<LodLevel>,
35 pub current_lod: usize,
36}
37
38#[derive(Debug, Clone)]
40pub struct RenderData {
41 pub pipeline_type: PipelineType,
42 pub vertices: Vec<Vertex>,
43 pub indices: Option<Vec<u32>>,
44 pub material: Material,
45 pub draw_calls: Vec<DrawCall>,
46}
47
48#[derive(Debug, Clone)]
50pub struct Material {
51 pub albedo: Vec4,
52 pub roughness: f32,
53 pub metallic: f32,
54 pub emissive: Vec4,
55 pub alpha_mode: AlphaMode,
56 pub double_sided: bool,
57}
58
59impl Default for Material {
60 fn default() -> Self {
61 Self {
62 albedo: Vec4::new(1.0, 1.0, 1.0, 1.0),
63 roughness: 0.5,
64 metallic: 0.0,
65 emissive: Vec4::ZERO,
66 alpha_mode: AlphaMode::Opaque,
67 double_sided: false,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum AlphaMode {
75 Opaque,
76 Mask { cutoff: u8 },
77 Blend,
78}
79
80#[derive(Debug, Clone)]
82pub struct LodLevel {
83 pub distance: f32,
84 pub vertex_count: usize,
85 pub index_count: Option<usize>,
86 pub simplification_ratio: f32,
87}
88
89#[derive(Debug, Clone)]
91pub struct DrawCall {
92 pub vertex_offset: usize,
93 pub vertex_count: usize,
94 pub index_offset: Option<usize>,
95 pub index_count: Option<usize>,
96 pub instance_count: usize,
97}
98
99#[derive(Debug, Clone, Copy)]
101pub struct BoundingBox {
102 pub min: Vec3,
103 pub max: Vec3,
104}
105
106impl Default for BoundingBox {
107 fn default() -> Self {
108 Self {
109 min: Vec3::splat(f32::INFINITY),
110 max: Vec3::splat(f32::NEG_INFINITY),
111 }
112 }
113}
114
115impl BoundingBox {
116 pub fn new(min: Vec3, max: Vec3) -> Self {
117 Self { min, max }
118 }
119
120 pub fn from_points(points: &[Vec3]) -> Self {
121 if points.is_empty() {
122 return Self::default();
123 }
124
125 let mut min = points[0];
126 let mut max = points[0];
127
128 for &point in points.iter().skip(1) {
129 min = min.min(point);
130 max = max.max(point);
131 }
132
133 Self { min, max }
134 }
135
136 pub fn center(&self) -> Vec3 {
137 (self.min + self.max) / 2.0
138 }
139
140 pub fn size(&self) -> Vec3 {
141 self.max - self.min
142 }
143
144 pub fn expand(&mut self, point: Vec3) {
145 self.min = self.min.min(point);
146 self.max = self.max.max(point);
147 }
148
149 pub fn expand_by_box(&mut self, other: &BoundingBox) {
150 self.min = self.min.min(other.min);
151 self.max = self.max.max(other.max);
152 }
153
154 pub fn transform(&self, transform: &Mat4) -> Self {
155 let corners = [
156 Vec3::new(self.min.x, self.min.y, self.min.z),
157 Vec3::new(self.max.x, self.min.y, self.min.z),
158 Vec3::new(self.min.x, self.max.y, self.min.z),
159 Vec3::new(self.max.x, self.max.y, self.min.z),
160 Vec3::new(self.min.x, self.min.y, self.max.z),
161 Vec3::new(self.max.x, self.min.y, self.max.z),
162 Vec3::new(self.min.x, self.max.y, self.max.z),
163 Vec3::new(self.max.x, self.max.y, self.max.z),
164 ];
165
166 let transformed_corners: Vec<Vec3> = corners
167 .iter()
168 .map(|&corner| (*transform * corner.extend(1.0)).truncate())
169 .collect();
170
171 Self::from_points(&transformed_corners)
172 }
173
174 pub fn intersects(&self, other: &BoundingBox) -> bool {
175 self.min.x <= other.max.x
176 && self.max.x >= other.min.x
177 && self.min.y <= other.max.y
178 && self.max.y >= other.min.y
179 && self.min.z <= other.max.z
180 && self.max.z >= other.min.z
181 }
182
183 pub fn contains_point(&self, point: Vec3) -> bool {
184 point.x >= self.min.x
185 && point.x <= self.max.x
186 && point.y >= self.min.y
187 && point.y <= self.max.y
188 && point.z >= self.min.z
189 && point.z <= self.max.z
190 }
191
192 pub fn union(&self, other: &BoundingBox) -> BoundingBox {
194 BoundingBox {
195 min: Vec3::new(
196 self.min.x.min(other.min.x),
197 self.min.y.min(other.min.y),
198 self.min.z.min(other.min.z),
199 ),
200 max: Vec3::new(
201 self.max.x.max(other.max.x),
202 self.max.y.max(other.max.y),
203 self.max.z.max(other.max.z),
204 ),
205 }
206 }
207}
208
209pub struct Scene {
211 nodes: HashMap<NodeId, SceneNode>,
212 root_nodes: Vec<NodeId>,
213 next_id: NodeId,
214
215 world_bounds: BoundingBox,
217 bounds_dirty: bool,
218
219 frustum: Option<Frustum>,
221 camera_position: Vec3,
222}
223
224impl Default for Scene {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230impl Scene {
231 pub fn new() -> Self {
232 Self {
233 nodes: HashMap::new(),
234 root_nodes: Vec::new(),
235 next_id: 1,
236 world_bounds: BoundingBox::default(),
237 bounds_dirty: true,
238 frustum: None,
239 camera_position: Vec3::ZERO,
240 }
241 }
242
243 pub fn add_node(&mut self, mut node: SceneNode) -> NodeId {
245 let id = self.next_id;
246 self.next_id += 1;
247
248 node.id = id;
249
250 if let Some(parent_id) = node.parent {
252 if let Some(parent) = self.nodes.get_mut(&parent_id) {
253 parent.children.push(id);
254 }
255 } else {
256 self.root_nodes.push(id);
257 }
258
259 self.nodes.insert(id, node);
260 self.bounds_dirty = true;
261 id
262 }
263
264 pub fn remove_node(&mut self, id: NodeId) -> bool {
266 let (parent_id, children) = if let Some(node) = self.nodes.get(&id) {
268 (node.parent, node.children.clone())
269 } else {
270 return false;
271 };
272
273 if let Some(parent_id) = parent_id {
275 if let Some(parent) = self.nodes.get_mut(&parent_id) {
276 parent.children.retain(|&child_id| child_id != id);
277 }
278 } else {
279 self.root_nodes.retain(|&root_id| root_id != id);
280 }
281
282 for child_id in children {
284 self.remove_node(child_id);
285 }
286
287 self.nodes.remove(&id);
288 self.bounds_dirty = true;
289 true
290 }
291
292 pub fn get_node(&self, id: NodeId) -> Option<&SceneNode> {
294 self.nodes.get(&id)
295 }
296
297 pub fn get_node_mut(&mut self, id: NodeId) -> Option<&mut SceneNode> {
299 if self.nodes.contains_key(&id) {
300 self.bounds_dirty = true;
301 }
302 self.nodes.get_mut(&id)
303 }
304
305 pub fn update_transforms(&mut self, root_transform: Mat4) {
307 for &root_id in &self.root_nodes.clone() {
308 self.update_node_transform(root_id, root_transform);
309 }
310 }
311
312 fn update_node_transform(&mut self, node_id: NodeId, parent_transform: Mat4) {
313 if let Some(node) = self.nodes.get_mut(&node_id) {
314 let world_transform = parent_transform * node.transform;
315
316 if let Some(render_data) = &node.render_data {
318 let local_bounds = BoundingBox::from_points(
319 &render_data
320 .vertices
321 .iter()
322 .map(|v| Vec3::from_array(v.position))
323 .collect::<Vec<_>>(),
324 );
325 node.bounds = local_bounds.transform(&world_transform);
326 }
327
328 let children = node.children.clone();
330 for child_id in children {
331 self.update_node_transform(child_id, world_transform);
332 }
333 }
334 }
335
336 pub fn world_bounds(&mut self) -> BoundingBox {
338 if self.bounds_dirty {
339 self.update_world_bounds();
340 }
341 self.world_bounds
342 }
343
344 fn update_world_bounds(&mut self) {
345 self.world_bounds = BoundingBox::default();
346
347 for node in self.nodes.values() {
348 if node.visible {
349 self.world_bounds.expand_by_box(&node.bounds);
350 }
351 }
352
353 self.bounds_dirty = false;
354 }
355
356 pub fn set_camera_position(&mut self, position: Vec3) {
358 self.camera_position = position;
359 self.update_lod();
360 }
361
362 fn update_lod(&mut self) {
364 for node in self.nodes.values_mut() {
365 if !node.lod_levels.is_empty() {
366 let distance = node.bounds.center().distance(self.camera_position);
367
368 let mut lod_index = node.lod_levels.len() - 1;
370 for (i, lod) in node.lod_levels.iter().enumerate() {
371 if distance <= lod.distance {
372 lod_index = i;
373 break;
374 }
375 }
376
377 node.current_lod = lod_index;
378 }
379 }
380 }
381
382 pub fn get_visible_nodes(&self) -> Vec<&SceneNode> {
384 self.nodes
385 .values()
386 .filter(|node| {
387 node.visible && node.render_data.is_some() && self.is_node_in_frustum(node)
388 })
389 .collect()
390 }
391
392 fn is_node_in_frustum(&self, node: &SceneNode) -> bool {
393 if let Some(ref frustum) = self.frustum {
395 frustum.intersects_box(&node.bounds)
396 } else {
397 true
398 }
399 }
400
401 pub fn set_frustum(&mut self, frustum: Frustum) {
403 self.frustum = Some(frustum);
404 }
405
406 pub fn clear(&mut self) {
408 self.nodes.clear();
409 self.root_nodes.clear();
410 self.bounds_dirty = true;
411 }
412
413 pub fn statistics(&self) -> SceneStatistics {
415 let visible_nodes = self.nodes.values().filter(|n| n.visible).count();
416 let total_vertices: usize = self
417 .nodes
418 .values()
419 .filter_map(|n| n.render_data.as_ref())
420 .map(|rd| rd.vertices.len())
421 .sum();
422 let total_triangles: usize = self
423 .nodes
424 .values()
425 .filter_map(|n| n.render_data.as_ref())
426 .filter(|rd| rd.pipeline_type == PipelineType::Triangles)
427 .map(|rd| {
428 rd.indices
429 .as_ref()
430 .map_or(rd.vertices.len() / 3, |i| i.len() / 3)
431 })
432 .sum();
433
434 SceneStatistics {
435 total_nodes: self.nodes.len(),
436 visible_nodes,
437 total_vertices,
438 total_triangles,
439 }
440 }
441}
442
443#[derive(Debug, Clone)]
445pub struct Frustum {
446 pub planes: [Plane; 6], }
448
449impl Frustum {
450 pub fn from_view_proj(view_proj: Mat4) -> Self {
451 let m = view_proj.to_cols_array_2d();
452
453 let planes = [
455 Plane::new(
457 m[0][3] + m[0][0],
458 m[1][3] + m[1][0],
459 m[2][3] + m[2][0],
460 m[3][3] + m[3][0],
461 ),
462 Plane::new(
464 m[0][3] - m[0][0],
465 m[1][3] - m[1][0],
466 m[2][3] - m[2][0],
467 m[3][3] - m[3][0],
468 ),
469 Plane::new(
471 m[0][3] + m[0][1],
472 m[1][3] + m[1][1],
473 m[2][3] + m[2][1],
474 m[3][3] + m[3][1],
475 ),
476 Plane::new(
478 m[0][3] - m[0][1],
479 m[1][3] - m[1][1],
480 m[2][3] - m[2][1],
481 m[3][3] - m[3][1],
482 ),
483 Plane::new(
485 m[0][3] + m[0][2],
486 m[1][3] + m[1][2],
487 m[2][3] + m[2][2],
488 m[3][3] + m[3][2],
489 ),
490 Plane::new(
492 m[0][3] - m[0][2],
493 m[1][3] - m[1][2],
494 m[2][3] - m[2][2],
495 m[3][3] - m[3][2],
496 ),
497 ];
498
499 Self { planes }
500 }
501
502 pub fn intersects_box(&self, bbox: &BoundingBox) -> bool {
503 for plane in &self.planes {
504 if plane.distance_to_box(bbox) > 0.0 {
505 return false; }
507 }
508 true }
510}
511
512#[derive(Debug, Clone, Copy)]
514pub struct Plane {
515 pub normal: Vec3,
516 pub distance: f32,
517}
518
519impl Plane {
520 pub fn new(a: f32, b: f32, c: f32, d: f32) -> Self {
521 let normal = Vec3::new(a, b, c);
522 let length = normal.length();
523
524 Self {
525 normal: normal / length,
526 distance: d / length,
527 }
528 }
529
530 pub fn distance_to_point(&self, point: Vec3) -> f32 {
531 self.normal.dot(point) + self.distance
532 }
533
534 pub fn distance_to_box(&self, bbox: &BoundingBox) -> f32 {
535 let positive_vertex = Vec3::new(
537 if self.normal.x >= 0.0 {
538 bbox.max.x
539 } else {
540 bbox.min.x
541 },
542 if self.normal.y >= 0.0 {
543 bbox.max.y
544 } else {
545 bbox.min.y
546 },
547 if self.normal.z >= 0.0 {
548 bbox.max.z
549 } else {
550 bbox.min.z
551 },
552 );
553
554 self.distance_to_point(positive_vertex)
555 }
556}
557
558#[derive(Debug, Clone)]
560pub struct SceneStatistics {
561 pub total_nodes: usize,
562 pub visible_nodes: usize,
563 pub total_vertices: usize,
564 pub total_triangles: usize,
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570
571 #[test]
572 fn test_bounding_box_creation() {
573 let points = vec![
574 Vec3::new(-1.0, -1.0, -1.0),
575 Vec3::new(1.0, 1.0, 1.0),
576 Vec3::new(0.0, 0.0, 0.0),
577 ];
578
579 let bbox = BoundingBox::from_points(&points);
580 assert_eq!(bbox.min, Vec3::new(-1.0, -1.0, -1.0));
581 assert_eq!(bbox.max, Vec3::new(1.0, 1.0, 1.0));
582 assert_eq!(bbox.center(), Vec3::ZERO);
583 }
584
585 #[test]
586 fn test_scene_node_hierarchy() {
587 let mut scene = Scene::new();
588
589 let parent_node = SceneNode {
590 id: 0,
591 name: "Parent".to_string(),
592 transform: Mat4::IDENTITY,
593 visible: true,
594 cast_shadows: true,
595 receive_shadows: true,
596 parent: None,
597 children: Vec::new(),
598 render_data: None,
599 bounds: BoundingBox::default(),
600 lod_levels: Vec::new(),
601 current_lod: 0,
602 };
603
604 let parent_id = scene.add_node(parent_node);
605
606 let child_node = SceneNode {
607 id: 0,
608 name: "Child".to_string(),
609 transform: Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0)),
610 visible: true,
611 cast_shadows: true,
612 receive_shadows: true,
613 parent: Some(parent_id),
614 children: Vec::new(),
615 render_data: None,
616 bounds: BoundingBox::default(),
617 lod_levels: Vec::new(),
618 current_lod: 0,
619 };
620
621 let child_id = scene.add_node(child_node);
622
623 let parent = scene.get_node(parent_id).unwrap();
625 assert!(parent.children.contains(&child_id));
626
627 let child = scene.get_node(child_id).unwrap();
628 assert_eq!(child.parent, Some(parent_id));
629 }
630}