use std::ops::{Index, IndexMut};
use std::sync::Arc;
use glam::{Mat4, Quat, Vec3, Vec4};
use crate::math::{Aabb, euler};
use crate::{Blending, Environment, Geometry, Light, Material, Rgb};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeId(pub u32);
impl NodeId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Clone)]
pub enum Object {
Group,
Mesh(Mesh),
Light(Light),
Particles(Particles),
}
#[derive(Debug, Clone)]
pub struct Mesh {
pub geometry: Arc<Geometry>,
pub material: Material,
pub cast_shadow: bool,
pub receive_shadow: bool,
pub skin: Option<Skin>,
pub morph_weights: Vec<f32>,
pub instances: Option<Arc<Instances>>,
}
impl Mesh {
pub fn bounds(&self, m: &Mat4) -> Aabb {
let local = self.geometry.bounds();
match &self.instances {
Some(instances) => instances.transforms.iter().fold(Aabb::EMPTY, |aabb, t| {
aabb.union(local.transformed(&(*m * *t)))
}),
None => local.transformed(m),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Instances {
pub transforms: Vec<Mat4>,
pub colors: Vec<Rgb>,
}
impl Instances {
pub fn new(transforms: impl IntoIterator<Item = Mat4>) -> Self {
Self {
transforms: transforms.into_iter().collect(),
colors: Vec::new(),
}
}
pub fn colors(mut self, colors: impl IntoIterator<Item = Rgb>) -> Self {
self.colors = colors.into_iter().collect();
self
}
pub(crate) fn color(&self, i: usize) -> Vec4 {
self.colors.get(i).map_or(Vec4::ONE, |c| c.0.extend(1.0))
}
}
#[derive(Debug, Clone)]
pub struct Skin {
pub joints: Vec<NodeId>,
pub inverse_bind_matrices: Arc<Vec<Mat4>>,
}
#[derive(Debug, Clone, Default)]
pub struct Particles {
pub items: Vec<Particle>,
pub blending: Blending,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Particle {
pub position: Vec3,
pub size: f32,
pub color: Rgb,
pub opacity: f32,
}
impl Particle {
pub fn new(position: Vec3, size: f32, color: impl Into<Rgb>) -> Self {
Self {
position,
size,
color: color.into(),
opacity: 1.0,
}
}
pub fn opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity;
self
}
}
#[derive(Debug, Clone)]
pub struct Node {
pub name: Option<Arc<str>>,
pub position: Vec3,
pub rotation: Quat,
pub scale: Vec3,
pub visible: bool,
pub object: Object,
pub(crate) parent: Option<NodeId>,
pub(crate) children: Vec<NodeId>,
}
impl Node {
pub fn new(object: Object) -> Self {
Self {
name: None,
position: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
visible: true,
object,
parent: None,
children: Vec::new(),
}
}
pub fn group() -> Self {
Self::new(Object::Group)
}
pub fn mesh(geometry: impl Into<Arc<Geometry>>, material: Material) -> Self {
Self::new(Object::Mesh(Mesh {
geometry: geometry.into(),
material,
cast_shadow: true,
receive_shadow: true,
skin: None,
morph_weights: Vec::new(),
instances: None,
}))
}
pub fn instanced(
geometry: impl Into<Arc<Geometry>>,
material: Material,
instances: impl Into<Arc<Instances>>,
) -> Self {
let mut node = Self::mesh(geometry, material);
if let Some(mesh) = node.mesh_mut() {
mesh.instances = Some(instances.into());
}
node
}
pub fn light(light: Light) -> Self {
Self::new(Object::Light(light))
}
pub fn particles(items: Vec<Particle>, blending: Blending) -> Self {
Self::new(Object::Particles(Particles { items, blending }))
}
pub fn named(mut self, name: &str) -> Self {
self.name = Some(name.into());
self
}
pub fn at(self, x: f32, y: f32, z: f32) -> Self {
self.with_position(Vec3::new(x, y, z))
}
pub fn with_position(mut self, position: Vec3) -> Self {
self.position = position;
self
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.rotation = rotation;
self
}
pub fn euler(self, x: f32, y: f32, z: f32) -> Self {
self.with_rotation(euler(x, y, z))
}
pub fn rotate_x(mut self, angle: f32) -> Self {
self.rotation *= Quat::from_rotation_x(angle);
self
}
pub fn rotate_y(mut self, angle: f32) -> Self {
self.rotation *= Quat::from_rotation_y(angle);
self
}
pub fn rotate_z(mut self, angle: f32) -> Self {
self.rotation *= Quat::from_rotation_z(angle);
self
}
pub fn scaled(self, scale: f32) -> Self {
self.scaled3(scale, scale, scale)
}
pub fn scaled3(mut self, x: f32, y: f32, z: f32) -> Self {
self.scale = Vec3::new(x, y, z);
self
}
pub fn hidden(mut self) -> Self {
self.visible = false;
self
}
pub fn shadows(mut self, cast: bool, receive: bool) -> Self {
if let Some(mesh) = self.mesh_mut() {
mesh.cast_shadow = cast;
mesh.receive_shadow = receive;
}
self
}
pub fn local_matrix(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.position)
}
pub fn parent(&self) -> Option<NodeId> {
self.parent
}
pub fn children(&self) -> &[NodeId] {
&self.children
}
pub fn as_mesh(&self) -> Option<&Mesh> {
match &self.object {
Object::Mesh(mesh) => Some(mesh),
_ => None,
}
}
pub fn mesh_mut(&mut self) -> Option<&mut Mesh> {
match &mut self.object {
Object::Mesh(mesh) => Some(mesh),
_ => None,
}
}
pub fn material_mut(&mut self) -> Option<&mut Material> {
self.mesh_mut().map(|mesh| &mut mesh.material)
}
pub fn light_mut(&mut self) -> Option<&mut Light> {
match &mut self.object {
Object::Light(light) => Some(light),
_ => None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub enum Background {
#[default]
Transparent,
Color(Rgb),
Environment { blur: f32 },
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Fog {
Linear { color: Rgb, near: f32, far: f32 },
Exp2 { color: Rgb, density: f32 },
}
impl Fog {
pub(crate) fn factor(&self, distance: f32) -> f32 {
match *self {
Fog::Linear { near, far, .. } => {
((distance - near) / (far - near).max(1e-6)).clamp(0.0, 1.0)
}
Fog::Exp2 { density, .. } => 1.0 - (-(density * distance).powi(2)).exp(),
}
}
pub(crate) fn color(&self) -> Rgb {
match *self {
Fog::Linear { color, .. } | Fog::Exp2 { color, .. } => color,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Scene {
nodes: Vec<Node>,
pub background: Background,
pub environment: Environment,
pub fog: Option<Fog>,
pub reflector: Option<Reflector>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Reflector {
pub point: Vec3,
pub normal: Vec3,
pub scale: f32,
}
impl Reflector {
pub fn new(point: Vec3, normal: Vec3) -> Self {
Self {
point,
normal: normal.normalize_or(Vec3::Y),
scale: 0.5,
}
}
pub fn floor() -> Self {
Self::new(Vec3::ZERO, Vec3::Y)
}
pub fn scale(mut self, scale: f32) -> Self {
self.scale = scale.clamp(0.1, 1.0);
self
}
pub(crate) fn plane(&self) -> Vec4 {
self.normal.extend(-self.normal.dot(self.point))
}
pub(crate) fn matrix(&self) -> Mat4 {
let n = self.normal;
let mirror = |v: Vec3| v - 2.0 * n * n.dot(v);
Mat4::from_cols(
mirror(Vec3::X).extend(0.0),
mirror(Vec3::Y).extend(0.0),
mirror(Vec3::Z).extend(0.0),
(2.0 * n * n.dot(self.point)).extend(1.0),
)
}
}
impl Scene {
pub fn new() -> Self {
Self::default()
}
pub fn background(mut self, background: Background) -> Self {
self.background = background;
self
}
pub fn environment(mut self, environment: Environment) -> Self {
self.environment = environment;
self
}
pub fn reflector(mut self, reflector: Reflector) -> Self {
self.reflector = Some(reflector);
self
}
pub fn fog(mut self, fog: Fog) -> Self {
self.fog = Some(fog);
self
}
pub fn add(&mut self, node: Node) -> NodeId {
self.insert(None, node)
}
pub fn add_to(&mut self, parent: NodeId, node: Node) -> NodeId {
self.insert(Some(parent), node)
}
pub(crate) fn insert(&mut self, parent: Option<NodeId>, mut node: Node) -> NodeId {
let id = NodeId(self.nodes.len() as u32);
node.parent = parent;
node.children.clear();
if let Some(parent) = parent {
self.nodes[parent.index()].children.push(id);
}
self.nodes.push(node);
id
}
pub fn append(&mut self, parent: Option<NodeId>, other: &Scene) -> u32 {
let offset = self.nodes.len() as u32;
let shift = |id: &mut NodeId| id.0 += offset;
for node in &other.nodes {
let mut node = node.clone();
node.children.iter_mut().for_each(shift);
if let Some(skin) = node.mesh_mut().and_then(|mesh| mesh.skin.as_mut()) {
skin.joints.iter_mut().for_each(shift);
}
if let Some(own) = &mut node.parent {
shift(own);
} else if let Some(parent) = parent {
node.parent = Some(parent);
let id = NodeId(self.nodes.len() as u32);
self.nodes[parent.index()].children.push(id);
}
self.nodes.push(node);
}
offset
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn nodes(&self) -> impl Iterator<Item = (NodeId, &Node)> {
(0..).map(NodeId).zip(&self.nodes)
}
pub fn get(&self, id: NodeId) -> Option<&Node> {
self.nodes.get(id.index())
}
pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
self.nodes.get_mut(id.index())
}
pub fn find(&self, name: &str) -> Option<NodeId> {
self.nodes()
.find(|(_, n)| n.name.as_deref() == Some(name))
.map(|(id, _)| id)
}
pub fn find_mut(&mut self, name: &str) -> Option<&mut Node> {
self.nodes
.iter_mut()
.find(|n| n.name.as_deref() == Some(name))
}
pub fn descendants(&self, id: NodeId) -> Vec<NodeId> {
let mut out = vec![id];
let mut i = 0;
while i < out.len() {
out.extend_from_slice(&self.nodes[out[i].index()].children);
i += 1;
}
out
}
pub fn world_matrix(&self, id: NodeId) -> Mat4 {
let node = &self.nodes[id.index()];
match node.parent {
Some(parent) => self.world_matrix(parent) * node.local_matrix(),
None => node.local_matrix(),
}
}
pub fn world_position(&self, id: NodeId) -> Vec3 {
self.world_matrix(id).w_axis.truncate()
}
pub fn local_to_world(&self, id: NodeId, local: Vec3) -> Vec3 {
self.world_matrix(id).transform_point3(local)
}
pub fn world_matrices(&self) -> Vec<Mat4> {
let mut out: Vec<Mat4> = Vec::with_capacity(self.nodes.len());
for node in &self.nodes {
let local = node.local_matrix();
let world = match node.parent {
Some(parent) => out[parent.index()] * local,
None => local,
};
out.push(world);
}
out
}
pub fn visibilities(&self) -> Vec<bool> {
let mut out: Vec<bool> = Vec::with_capacity(self.nodes.len());
for node in &self.nodes {
let parent_visible = node.parent.is_none_or(|p| out[p.index()]);
out.push(parent_visible && node.visible);
}
out
}
pub fn bounds(&self, id: NodeId) -> Aabb {
let worlds = self.world_matrices();
let visible = self.visibilities();
let mut aabb = Aabb::EMPTY;
for n in self.descendants(id) {
if visible[n.index()]
&& let Some(mesh) = self[n].as_mesh()
{
aabb = aabb.union(mesh.bounds(&worlds[n.index()]));
}
}
aabb
}
}
impl Index<NodeId> for Scene {
type Output = Node;
fn index(&self, id: NodeId) -> &Node {
&self.nodes[id.index()]
}
}
impl IndexMut<NodeId> for Scene {
fn index_mut(&mut self, id: NodeId) -> &mut Node {
&mut self.nodes[id.index()]
}
}