use crate::game_object::Transform;
use crate::hierarchy::SceneHierarchy;
use crate::transform::GlobalTransform;
#[derive(Debug, Clone)]
pub struct SceneTransforms {
pub local: Vec<Transform>,
pub world: Vec<GlobalTransform>,
pub dirty: Vec<bool>,
}
impl SceneTransforms {
pub fn with_capacity(capacity: usize) -> Self {
Self {
local: vec![Transform::default(); capacity],
world: vec![GlobalTransform::identity(); capacity],
dirty: vec![true; capacity],
}
}
pub fn mark_dirty(&mut self, entity: u32) {
let idx = entity as usize;
if idx < self.dirty.len() {
self.dirty[idx] = true;
}
}
pub fn propagate_from_roots(&mut self, hierarchy: &SceneHierarchy) {
let roots = hierarchy.roots();
let mut stack: Vec<u32> = roots;
while let Some(entity) = stack.pop() {
let idx = entity as usize;
if idx >= self.local.len() {
continue;
}
let local_matrix = GlobalTransform(self.local[idx].to_matrix());
let parent_world = hierarchy.parent[idx]
.map(|p| self.world[p as usize])
.unwrap_or_default();
self.world[idx] = parent_world.mul(&local_matrix);
self.dirty[idx] = false;
for &child in hierarchy.children_of(entity) {
stack.push(child);
}
}
}
pub fn len(&self) -> usize {
self.local.len()
}
pub fn is_empty(&self) -> bool {
self.local.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_capacity_creates_identity() {
let st = SceneTransforms::with_capacity(5);
assert_eq!(st.len(), 5);
assert_eq!(st.world[0], GlobalTransform::identity());
}
#[test]
fn mark_dirty() {
let mut st = SceneTransforms::with_capacity(3);
st.dirty[0] = false;
st.mark_dirty(0);
assert!(st.dirty[0]);
}
#[test]
fn propagate_identity_roots() {
let mut st = SceneTransforms::with_capacity(3);
let h = SceneHierarchy::with_capacity(3);
st.propagate_from_roots(&h);
for i in 0..3 {
assert_eq!(st.world[i], GlobalTransform::identity());
assert!(!st.dirty[i]);
}
}
#[test]
fn propagate_parent_translation() {
let mut st = SceneTransforms::with_capacity(3);
let mut h = SceneHierarchy::with_capacity(3);
st.local[0].position = [5.0, 0.0, 0.0];
st.local[1].position = [3.0, 0.0, 0.0];
h.set_parent(1, Some(0));
st.propagate_from_roots(&h);
assert!((st.world[0].translation()[0] - 5.0).abs() < 1e-6);
assert!((st.world[1].translation()[0] - 8.0).abs() < 1e-6);
}
#[test]
fn propagate_chain_of_three() {
let mut st = SceneTransforms::with_capacity(3);
let mut h = SceneHierarchy::with_capacity(3);
st.local[0].position = [1.0, 0.0, 0.0];
st.local[1].position = [2.0, 0.0, 0.0];
st.local[2].position = [3.0, 0.0, 0.0];
h.set_parent(1, Some(0));
h.set_parent(2, Some(1));
st.propagate_from_roots(&h);
assert!((st.world[0].translation()[0] - 1.0).abs() < 1e-6);
assert!((st.world[1].translation()[0] - 3.0).abs() < 1e-6);
assert!((st.world[2].translation()[0] - 6.0).abs() < 1e-6);
}
#[test]
fn propagate_clears_dirty() {
let mut st = SceneTransforms::with_capacity(2);
let h = SceneHierarchy::with_capacity(2);
assert!(st.dirty[0]);
st.propagate_from_roots(&h);
assert!(!st.dirty[0]);
}
}