1use crate::core::renderer::{PipelineType, Vertex};
7use bytemuck::{Pod, Zeroable};
8use glam::{Mat4, Vec3, Vec4};
9use std::collections::HashMap;
10use std::sync::Arc;
11
12pub type NodeId = u64;
14
15#[derive(Debug, Clone)]
17pub struct SceneNode {
18 pub id: NodeId,
19 pub name: String,
20 pub transform: Mat4,
21 pub visible: bool,
22 pub cast_shadows: bool,
23 pub receive_shadows: bool,
24
25 pub axes_index: usize,
27
28 pub parent: Option<NodeId>,
30 pub children: Vec<NodeId>,
31
32 pub render_data: Option<RenderData>,
34
35 pub bounds: BoundingBox,
37
38 pub lod_levels: Vec<LodLevel>,
40 pub current_lod: usize,
41}
42
43#[derive(Debug, Clone)]
45pub struct RenderData {
46 pub pipeline_type: PipelineType,
47 pub vertices: Vec<Vertex>,
48 pub indices: Option<Vec<u32>>,
49 pub gpu_vertices: Option<GpuVertexBuffer>,
50 pub bounds: Option<BoundingBox>,
54 pub material: Material,
55 pub draw_calls: Vec<DrawCall>,
56 pub image: Option<ImageData>,
58}
59
60impl RenderData {
61 pub fn vertex_count(&self) -> usize {
62 if !self.vertices.is_empty() {
63 self.vertices.len()
64 } else if let Some(buffer) = &self.gpu_vertices {
65 buffer.vertex_count
66 } else {
67 0
68 }
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct GpuVertexBuffer {
75 pub buffer: Arc<wgpu::Buffer>,
76 pub vertex_count: usize,
77 pub indirect: Option<GpuIndirectDraw>,
82}
83
84impl GpuVertexBuffer {
85 pub fn new(buffer: Arc<wgpu::Buffer>, vertex_count: usize) -> Self {
86 Self {
87 buffer,
88 vertex_count,
89 indirect: None,
90 }
91 }
92
93 pub fn with_indirect(
94 buffer: Arc<wgpu::Buffer>,
95 vertex_count: usize,
96 indirect_args: Arc<wgpu::Buffer>,
97 ) -> Self {
98 Self {
99 buffer,
100 vertex_count,
101 indirect: Some(GpuIndirectDraw {
102 args: indirect_args,
103 offset: 0,
104 }),
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct GpuIndirectDraw {
112 pub args: Arc<wgpu::Buffer>,
113 pub offset: u64,
114}
115
116#[repr(C)]
121#[derive(Clone, Copy, Debug, Pod, Zeroable)]
122pub struct DrawIndirectArgsRaw {
123 pub vertex_count: u32,
124 pub instance_count: u32,
125 pub first_vertex: u32,
126 pub first_instance: u32,
127}
128
129#[derive(Debug, Clone)]
131pub enum ImageData {
132 Rgba8 {
134 width: u32,
135 height: u32,
136 data: Vec<u8>,
137 },
138}
139
140#[derive(Debug, Clone)]
142pub struct Material {
143 pub albedo: Vec4,
144 pub roughness: f32,
145 pub metallic: f32,
146 pub emissive: Vec4,
147 pub alpha_mode: AlphaMode,
148 pub double_sided: bool,
149}
150
151impl Default for Material {
152 fn default() -> Self {
153 Self {
154 albedo: Vec4::new(1.0, 1.0, 1.0, 1.0),
155 roughness: 0.5,
156 metallic: 0.0,
157 emissive: Vec4::ZERO,
158 alpha_mode: AlphaMode::Opaque,
159 double_sided: false,
160 }
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum AlphaMode {
167 Opaque,
168 Mask { cutoff: u8 },
169 Blend,
170}
171
172#[derive(Debug, Clone)]
174pub struct LodLevel {
175 pub distance: f32,
176 pub vertex_count: usize,
177 pub index_count: Option<usize>,
178 pub simplification_ratio: f32,
179}
180
181#[derive(Debug, Clone)]
183pub struct DrawCall {
184 pub vertex_offset: usize,
185 pub vertex_count: usize,
186 pub index_offset: Option<usize>,
187 pub index_count: Option<usize>,
188 pub instance_count: usize,
189}
190
191#[derive(Debug, Clone, Copy)]
193pub struct BoundingBox {
194 pub min: Vec3,
195 pub max: Vec3,
196}
197
198impl Default for BoundingBox {
199 fn default() -> Self {
200 Self {
201 min: Vec3::splat(f32::INFINITY),
202 max: Vec3::splat(f32::NEG_INFINITY),
203 }
204 }
205}
206
207impl BoundingBox {
208 pub fn new(min: Vec3, max: Vec3) -> Self {
209 Self { min, max }
210 }
211
212 pub fn from_points(points: &[Vec3]) -> Self {
213 if points.is_empty() {
214 return Self::default();
215 }
216
217 let mut min = points[0];
218 let mut max = points[0];
219
220 for &point in points.iter().skip(1) {
221 min = min.min(point);
222 max = max.max(point);
223 }
224
225 Self { min, max }
226 }
227
228 pub fn center(&self) -> Vec3 {
229 (self.min + self.max) / 2.0
230 }
231
232 pub fn size(&self) -> Vec3 {
233 self.max - self.min
234 }
235
236 pub fn expand(&mut self, point: Vec3) {
237 self.min = self.min.min(point);
238 self.max = self.max.max(point);
239 }
240
241 pub fn expand_by_box(&mut self, other: &BoundingBox) {
242 self.min = self.min.min(other.min);
243 self.max = self.max.max(other.max);
244 }
245
246 pub fn transform(&self, transform: &Mat4) -> Self {
247 let corners = [
248 Vec3::new(self.min.x, self.min.y, self.min.z),
249 Vec3::new(self.max.x, self.min.y, self.min.z),
250 Vec3::new(self.min.x, self.max.y, self.min.z),
251 Vec3::new(self.max.x, self.max.y, self.min.z),
252 Vec3::new(self.min.x, self.min.y, self.max.z),
253 Vec3::new(self.max.x, self.min.y, self.max.z),
254 Vec3::new(self.min.x, self.max.y, self.max.z),
255 Vec3::new(self.max.x, self.max.y, self.max.z),
256 ];
257
258 let transformed_corners: Vec<Vec3> = corners
259 .iter()
260 .map(|&corner| (*transform * corner.extend(1.0)).truncate())
261 .collect();
262
263 Self::from_points(&transformed_corners)
264 }
265
266 pub fn intersects(&self, other: &BoundingBox) -> bool {
267 self.min.x <= other.max.x
268 && self.max.x >= other.min.x
269 && self.min.y <= other.max.y
270 && self.max.y >= other.min.y
271 && self.min.z <= other.max.z
272 && self.max.z >= other.min.z
273 }
274
275 pub fn contains_point(&self, point: Vec3) -> bool {
276 point.x >= self.min.x
277 && point.x <= self.max.x
278 && point.y >= self.min.y
279 && point.y <= self.max.y
280 && point.z >= self.min.z
281 && point.z <= self.max.z
282 }
283
284 pub fn union(&self, other: &BoundingBox) -> BoundingBox {
286 BoundingBox {
287 min: Vec3::new(
288 self.min.x.min(other.min.x),
289 self.min.y.min(other.min.y),
290 self.min.z.min(other.min.z),
291 ),
292 max: Vec3::new(
293 self.max.x.max(other.max.x),
294 self.max.y.max(other.max.y),
295 self.max.z.max(other.max.z),
296 ),
297 }
298 }
299}
300
301pub struct Scene {
303 nodes: HashMap<NodeId, SceneNode>,
304 root_nodes: Vec<NodeId>,
305 next_id: NodeId,
306
307 world_bounds: BoundingBox,
309 bounds_dirty: bool,
310
311 frustum: Option<Frustum>,
313 camera_position: Vec3,
314}
315
316impl Default for Scene {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322impl Scene {
323 pub fn new() -> Self {
324 Self {
325 nodes: HashMap::new(),
326 root_nodes: Vec::new(),
327 next_id: 1,
328 world_bounds: BoundingBox::default(),
329 bounds_dirty: true,
330 frustum: None,
331 camera_position: Vec3::ZERO,
332 }
333 }
334
335 pub fn add_node(&mut self, mut node: SceneNode) -> NodeId {
337 let id = self.next_id;
338 self.next_id += 1;
339
340 node.id = id;
341
342 if let Some(parent_id) = node.parent {
344 if let Some(parent) = self.nodes.get_mut(&parent_id) {
345 parent.children.push(id);
346 }
347 } else {
348 self.root_nodes.push(id);
349 }
350
351 self.nodes.insert(id, node);
352 self.bounds_dirty = true;
353 id
354 }
355
356 pub fn remove_node(&mut self, id: NodeId) -> bool {
358 let (parent_id, children) = if let Some(node) = self.nodes.get(&id) {
360 (node.parent, node.children.clone())
361 } else {
362 return false;
363 };
364
365 if let Some(parent_id) = parent_id {
367 if let Some(parent) = self.nodes.get_mut(&parent_id) {
368 parent.children.retain(|&child_id| child_id != id);
369 }
370 } else {
371 self.root_nodes.retain(|&root_id| root_id != id);
372 }
373
374 for child_id in children {
376 self.remove_node(child_id);
377 }
378
379 self.nodes.remove(&id);
380 self.bounds_dirty = true;
381 true
382 }
383
384 pub fn get_node(&self, id: NodeId) -> Option<&SceneNode> {
386 self.nodes.get(&id)
387 }
388
389 pub fn get_node_mut(&mut self, id: NodeId) -> Option<&mut SceneNode> {
391 if self.nodes.contains_key(&id) {
392 self.bounds_dirty = true;
393 }
394 self.nodes.get_mut(&id)
395 }
396
397 pub fn update_transforms(&mut self, root_transform: Mat4) {
399 for &root_id in &self.root_nodes.clone() {
400 self.update_node_transform(root_id, root_transform);
401 }
402 }
403
404 fn update_node_transform(&mut self, node_id: NodeId, parent_transform: Mat4) {
405 if let Some(node) = self.nodes.get_mut(&node_id) {
406 let world_transform = parent_transform * node.transform;
407
408 if let Some(render_data) = &node.render_data {
410 let local_bounds = BoundingBox::from_points(
411 &render_data
412 .vertices
413 .iter()
414 .map(|v| Vec3::from_array(v.position))
415 .collect::<Vec<_>>(),
416 );
417 node.bounds = local_bounds.transform(&world_transform);
418 }
419
420 let children = node.children.clone();
422 for child_id in children {
423 self.update_node_transform(child_id, world_transform);
424 }
425 }
426 }
427
428 pub fn world_bounds(&mut self) -> BoundingBox {
430 if self.bounds_dirty {
431 self.update_world_bounds();
432 }
433 self.world_bounds
434 }
435
436 fn update_world_bounds(&mut self) {
437 self.world_bounds = BoundingBox::default();
438
439 for node in self.nodes.values() {
440 if node.visible {
441 self.world_bounds.expand_by_box(&node.bounds);
442 }
443 }
444
445 self.bounds_dirty = false;
446 }
447
448 pub fn set_camera_position(&mut self, position: Vec3) {
450 self.camera_position = position;
451 self.update_lod();
452 }
453
454 fn update_lod(&mut self) {
456 for node in self.nodes.values_mut() {
457 if !node.lod_levels.is_empty() {
458 let distance = node.bounds.center().distance(self.camera_position);
459
460 let mut lod_index = node.lod_levels.len() - 1;
462 for (i, lod) in node.lod_levels.iter().enumerate() {
463 if distance <= lod.distance {
464 lod_index = i;
465 break;
466 }
467 }
468
469 node.current_lod = lod_index;
470 }
471 }
472 }
473
474 pub fn get_visible_nodes(&self) -> Vec<&SceneNode> {
476 self.nodes
477 .values()
478 .filter(|node| {
479 node.visible && node.render_data.is_some() && self.is_node_in_frustum(node)
480 })
481 .collect()
482 }
483
484 fn is_node_in_frustum(&self, node: &SceneNode) -> bool {
485 if let Some(ref frustum) = self.frustum {
487 frustum.intersects_box(&node.bounds)
488 } else {
489 true
490 }
491 }
492
493 pub fn set_frustum(&mut self, frustum: Frustum) {
495 self.frustum = Some(frustum);
496 }
497
498 pub fn clear(&mut self) {
500 self.nodes.clear();
501 self.root_nodes.clear();
502 self.bounds_dirty = true;
503 }
504
505 pub fn statistics(&self) -> SceneStatistics {
507 let visible_nodes = self.nodes.values().filter(|n| n.visible).count();
508 let total_vertices: usize = self
509 .nodes
510 .values()
511 .filter_map(|n| n.render_data.as_ref())
512 .map(|rd| rd.vertices.len())
513 .sum();
514 let total_triangles: usize = self
515 .nodes
516 .values()
517 .filter_map(|n| n.render_data.as_ref())
518 .filter(|rd| rd.pipeline_type == PipelineType::Triangles)
519 .map(|rd| {
520 rd.indices
521 .as_ref()
522 .map_or(rd.vertices.len() / 3, |i| i.len() / 3)
523 })
524 .sum();
525
526 SceneStatistics {
527 total_nodes: self.nodes.len(),
528 visible_nodes,
529 total_vertices,
530 total_triangles,
531 }
532 }
533}
534
535#[derive(Debug, Clone)]
537pub struct Frustum {
538 pub planes: [Plane; 6], }
540
541impl Frustum {
542 pub fn from_view_proj(view_proj: Mat4) -> Self {
543 let m = view_proj.to_cols_array_2d();
544
545 let planes = [
547 Plane::new(
549 m[0][3] + m[0][0],
550 m[1][3] + m[1][0],
551 m[2][3] + m[2][0],
552 m[3][3] + m[3][0],
553 ),
554 Plane::new(
556 m[0][3] - m[0][0],
557 m[1][3] - m[1][0],
558 m[2][3] - m[2][0],
559 m[3][3] - m[3][0],
560 ),
561 Plane::new(
563 m[0][3] + m[0][1],
564 m[1][3] + m[1][1],
565 m[2][3] + m[2][1],
566 m[3][3] + m[3][1],
567 ),
568 Plane::new(
570 m[0][3] - m[0][1],
571 m[1][3] - m[1][1],
572 m[2][3] - m[2][1],
573 m[3][3] - m[3][1],
574 ),
575 Plane::new(
577 m[0][3] + m[0][2],
578 m[1][3] + m[1][2],
579 m[2][3] + m[2][2],
580 m[3][3] + m[3][2],
581 ),
582 Plane::new(
584 m[0][3] - m[0][2],
585 m[1][3] - m[1][2],
586 m[2][3] - m[2][2],
587 m[3][3] - m[3][2],
588 ),
589 ];
590
591 Self { planes }
592 }
593
594 pub fn intersects_box(&self, bbox: &BoundingBox) -> bool {
595 for plane in &self.planes {
596 if plane.distance_to_box(bbox) > 0.0 {
597 return false; }
599 }
600 true }
602}
603
604#[derive(Debug, Clone, Copy)]
606pub struct Plane {
607 pub normal: Vec3,
608 pub distance: f32,
609}
610
611impl Plane {
612 pub fn new(a: f32, b: f32, c: f32, d: f32) -> Self {
613 let normal = Vec3::new(a, b, c);
614 let length = normal.length();
615
616 Self {
617 normal: normal / length,
618 distance: d / length,
619 }
620 }
621
622 pub fn distance_to_point(&self, point: Vec3) -> f32 {
623 self.normal.dot(point) + self.distance
624 }
625
626 pub fn distance_to_box(&self, bbox: &BoundingBox) -> f32 {
627 let positive_vertex = Vec3::new(
629 if self.normal.x >= 0.0 {
630 bbox.max.x
631 } else {
632 bbox.min.x
633 },
634 if self.normal.y >= 0.0 {
635 bbox.max.y
636 } else {
637 bbox.min.y
638 },
639 if self.normal.z >= 0.0 {
640 bbox.max.z
641 } else {
642 bbox.min.z
643 },
644 );
645
646 self.distance_to_point(positive_vertex)
647 }
648}
649
650#[derive(Debug, Clone)]
652pub struct SceneStatistics {
653 pub total_nodes: usize,
654 pub visible_nodes: usize,
655 pub total_vertices: usize,
656 pub total_triangles: usize,
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn test_bounding_box_creation() {
665 let points = vec![
666 Vec3::new(-1.0, -1.0, -1.0),
667 Vec3::new(1.0, 1.0, 1.0),
668 Vec3::new(0.0, 0.0, 0.0),
669 ];
670
671 let bbox = BoundingBox::from_points(&points);
672 assert_eq!(bbox.min, Vec3::new(-1.0, -1.0, -1.0));
673 assert_eq!(bbox.max, Vec3::new(1.0, 1.0, 1.0));
674 assert_eq!(bbox.center(), Vec3::ZERO);
675 }
676
677 #[test]
678 fn test_scene_node_hierarchy() {
679 let mut scene = Scene::new();
680
681 let parent_node = SceneNode {
682 id: 0,
683 name: "Parent".to_string(),
684 transform: Mat4::IDENTITY,
685 visible: true,
686 cast_shadows: true,
687 receive_shadows: true,
688 axes_index: 0,
689 parent: None,
690 children: Vec::new(),
691 render_data: None,
692 bounds: BoundingBox::default(),
693 lod_levels: Vec::new(),
694 current_lod: 0,
695 };
696
697 let parent_id = scene.add_node(parent_node);
698
699 let child_node = SceneNode {
700 id: 0,
701 name: "Child".to_string(),
702 transform: Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0)),
703 visible: true,
704 cast_shadows: true,
705 receive_shadows: true,
706 axes_index: 0,
707 parent: Some(parent_id),
708 children: Vec::new(),
709 render_data: None,
710 bounds: BoundingBox::default(),
711 lod_levels: Vec::new(),
712 current_lod: 0,
713 };
714
715 let child_id = scene.add_node(child_node);
716
717 let parent = scene.get_node(parent_id).unwrap();
719 assert!(parent.children.contains(&child_id));
720
721 let child = scene.get_node(child_id).unwrap();
722 assert_eq!(child.parent, Some(parent_id));
723 }
724}