dreamwell_engine/
scene.rs1use crate::game_object::Transform;
8use crate::hierarchy::SceneHierarchy;
9use crate::transform::GlobalTransform;
10
11#[derive(Debug, Clone)]
13pub struct SceneTransforms {
14 pub local: Vec<Transform>,
16 pub world: Vec<GlobalTransform>,
18 pub dirty: Vec<bool>,
20}
21
22impl SceneTransforms {
23 pub fn with_capacity(capacity: usize) -> Self {
25 Self {
26 local: vec![Transform::default(); capacity],
27 world: vec![GlobalTransform::identity(); capacity],
28 dirty: vec![true; capacity],
29 }
30 }
31
32 pub fn mark_dirty(&mut self, entity: u32) {
34 let idx = entity as usize;
35 if idx < self.dirty.len() {
36 self.dirty[idx] = true;
37 }
38 }
39
40 pub fn propagate_from_roots(&mut self, hierarchy: &SceneHierarchy) {
44 let roots = hierarchy.roots();
45 let mut stack: Vec<u32> = roots;
46
47 while let Some(entity) = stack.pop() {
48 let idx = entity as usize;
49 if idx >= self.local.len() {
50 continue;
51 }
52
53 let local_matrix = GlobalTransform(self.local[idx].to_matrix());
54
55 let parent_world = hierarchy.parent[idx]
56 .map(|p| self.world[p as usize])
57 .unwrap_or_default();
58
59 self.world[idx] = parent_world.mul(&local_matrix);
60 self.dirty[idx] = false;
61
62 for &child in hierarchy.children_of(entity) {
64 stack.push(child);
65 }
66 }
67 }
68
69 pub fn len(&self) -> usize {
71 self.local.len()
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.local.is_empty()
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn with_capacity_creates_identity() {
86 let st = SceneTransforms::with_capacity(5);
87 assert_eq!(st.len(), 5);
88 assert_eq!(st.world[0], GlobalTransform::identity());
89 }
90
91 #[test]
92 fn mark_dirty() {
93 let mut st = SceneTransforms::with_capacity(3);
94 st.dirty[0] = false;
95 st.mark_dirty(0);
96 assert!(st.dirty[0]);
97 }
98
99 #[test]
100 fn propagate_identity_roots() {
101 let mut st = SceneTransforms::with_capacity(3);
102 let h = SceneHierarchy::with_capacity(3);
103 st.propagate_from_roots(&h);
104 for i in 0..3 {
106 assert_eq!(st.world[i], GlobalTransform::identity());
107 assert!(!st.dirty[i]);
108 }
109 }
110
111 #[test]
112 fn propagate_parent_translation() {
113 let mut st = SceneTransforms::with_capacity(3);
114 let mut h = SceneHierarchy::with_capacity(3);
115 st.local[0].position = [5.0, 0.0, 0.0];
117 st.local[1].position = [3.0, 0.0, 0.0];
119 h.set_parent(1, Some(0));
120
121 st.propagate_from_roots(&h);
122
123 assert!((st.world[0].translation()[0] - 5.0).abs() < 1e-6);
125 assert!((st.world[1].translation()[0] - 8.0).abs() < 1e-6);
127 }
128
129 #[test]
130 fn propagate_chain_of_three() {
131 let mut st = SceneTransforms::with_capacity(3);
132 let mut h = SceneHierarchy::with_capacity(3);
133 st.local[0].position = [1.0, 0.0, 0.0];
134 st.local[1].position = [2.0, 0.0, 0.0];
135 st.local[2].position = [3.0, 0.0, 0.0];
136 h.set_parent(1, Some(0));
137 h.set_parent(2, Some(1));
138
139 st.propagate_from_roots(&h);
140
141 assert!((st.world[0].translation()[0] - 1.0).abs() < 1e-6);
142 assert!((st.world[1].translation()[0] - 3.0).abs() < 1e-6);
143 assert!((st.world[2].translation()[0] - 6.0).abs() < 1e-6);
144 }
145
146 #[test]
147 fn propagate_clears_dirty() {
148 let mut st = SceneTransforms::with_capacity(2);
149 let h = SceneHierarchy::with_capacity(2);
150 assert!(st.dirty[0]);
151 st.propagate_from_roots(&h);
152 assert!(!st.dirty[0]);
153 }
154}