use std::sync::Arc;
use glam::{Vec3, Quat, Mat4};
use crate::geometry::Mesh;
use crate::material::Material;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Transform {
pub translation: Vec3,
pub rotation: Quat,
pub scale: Vec3,
}
impl Transform {
pub const IDENTITY: Self = Self {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
};
pub fn from_translation(t: Vec3) -> Self { Self { translation: t, ..Self::IDENTITY } }
pub fn from_rotation(r: Quat) -> Self { Self { rotation: r, ..Self::IDENTITY } }
pub fn from_scale(s: Vec3) -> Self { Self { scale: s, ..Self::IDENTITY } }
pub fn from_trs(translation: Vec3, rotation: Quat, scale: Vec3) -> Self {
Self { translation, rotation, scale }
}
pub fn matrix(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
}
pub fn global_matrix(&self, parent: &Mat4) -> Mat4 {
*parent * self.matrix()
}
pub fn forward(&self) -> Vec3 { self.rotation * -Vec3::Z }
pub fn right(&self) -> Vec3 { self.rotation * Vec3::X }
pub fn up(&self) -> Vec3 { self.rotation * Vec3::Y }
pub fn look_at(&mut self, target: Vec3, world_up: Vec3) {
let dir = (target - self.translation).normalize();
if dir.length_squared() < 1e-8 { return; }
let mat = Mat4::look_at_rh(self.translation, target, world_up);
let (_, rot, _) = mat.inverse().to_scale_rotation_translation();
self.rotation = rot;
}
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
translation: self.translation.lerp(other.translation, t),
rotation: self.rotation.slerp(other.rotation, t),
scale: self.scale.lerp(other.scale, t),
}
}
}
impl Default for Transform {
fn default() -> Self { Self::IDENTITY }
}
pub type NodeId = usize;
#[derive(Debug, Clone)]
pub struct SceneNode {
pub name: String,
pub transform: Transform,
pub mesh: Option<Arc<Mesh>>,
pub material: Option<Arc<Material>>,
pub visible: bool,
pub children: Vec<NodeId>,
pub parent: Option<NodeId>,
}
impl SceneNode {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
transform: Transform::IDENTITY,
mesh: None,
material: None,
visible: true,
children: Vec::new(),
parent: None,
}
}
pub fn with_mesh(mut self, mesh: Arc<Mesh>) -> Self { self.mesh = Some(mesh); self }
pub fn with_material(mut self, mat: Arc<Material>) -> Self { self.material = Some(mat); self }
pub fn with_transform(mut self, t: Transform) -> Self { self.transform = t; self }
}
#[derive(Debug, Default)]
pub struct Scene {
nodes: Vec<SceneNode>,
roots: Vec<NodeId>,
}
impl Scene {
pub fn new() -> Self { Self::default() }
pub fn add_node(&mut self, node: SceneNode) -> NodeId {
let id = self.nodes.len();
self.nodes.push(node);
id
}
pub fn add_root(&mut self, id: NodeId) { self.roots.push(id); }
pub fn add_child(&mut self, parent: NodeId, child: NodeId) {
self.nodes[child].parent = Some(parent);
self.nodes[parent].children.push(child);
}
pub fn node(&self, id: NodeId) -> &SceneNode { &self.nodes[id] }
pub fn node_mut(&mut self, id: NodeId) -> &mut SceneNode { &mut self.nodes[id] }
pub fn roots(&self) -> &[NodeId] { &self.roots }
pub fn global_transform(&self, id: NodeId) -> Mat4 {
let node = &self.nodes[id];
match node.parent {
None => node.transform.matrix(),
Some(parent_id) => self.global_transform(parent_id) * node.transform.matrix(),
}
}
pub fn collect_render_items(&self) -> Vec<(NodeId, Mat4)> {
let mut out = Vec::new();
for &root in &self.roots {
self.collect_recursive(root, Mat4::IDENTITY, &mut out);
}
out
}
fn collect_recursive(&self, id: NodeId, parent_mat: Mat4, out: &mut Vec<(NodeId, Mat4)>) {
let node = &self.nodes[id];
if !node.visible { return; }
let mat = parent_mat * node.transform.matrix();
if node.mesh.is_some() {
out.push((id, mat));
}
for &child in &node.children {
self.collect_recursive(child, mat, out);
}
}
pub fn spawn_mesh(&mut self, name: impl Into<String>, mesh: Arc<Mesh>, material: Arc<Material>, transform: Transform) -> NodeId {
let node = SceneNode::new(name)
.with_mesh(mesh)
.with_material(material)
.with_transform(transform);
let id = self.add_node(node);
self.add_root(id);
id
}
}