use std::collections::HashMap;
use crate::{
animation::Animation,
audio::{AudioEmitter, AudioEmitterId, AudioSource, AudioSourceId},
camera::Camera,
light::Light,
material::Material,
mesh::Mesh,
skin::Skeleton,
skin::Skin,
texture::Texture,
};
macro_rules! id_newtype {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $name(pub u32);
};
}
id_newtype!(
NodeId
);
id_newtype!(
MeshId
);
id_newtype!(
MaterialId
);
id_newtype!(
TextureId
);
id_newtype!(
SkeletonId
);
id_newtype!(
SkinId
);
id_newtype!(
CameraId
);
id_newtype!(
LightId
);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Axis {
PosX,
NegX,
PosY,
NegY,
PosZ,
NegZ,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Unit {
Metres,
Centimetres,
Millimetres,
Inches,
Feet,
Yards,
}
impl Unit {
pub fn to_metres(self) -> f32 {
match self {
Self::Metres => 1.0,
Self::Centimetres => 0.01,
Self::Millimetres => 0.001,
Self::Inches => 0.0254,
Self::Feet => 0.3048,
Self::Yards => 0.9144,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Transform {
Matrix([[f32; 4]; 4]),
Trs {
translation: [f32; 3],
rotation: [f32; 4],
scale: [f32; 3],
},
}
impl Transform {
pub fn identity() -> Self {
Self::Trs {
translation: [0.0; 3],
rotation: [0.0, 0.0, 0.0, 1.0],
scale: [1.0, 1.0, 1.0],
}
}
pub fn to_matrix(&self) -> [[f32; 4]; 4] {
match *self {
Self::Matrix(m) => m,
Self::Trs {
translation,
rotation,
scale,
} => trs_to_matrix(translation, rotation, scale),
}
}
pub fn from_matrix(m: [[f32; 4]; 4]) -> Self {
let translation = [m[0][3], m[1][3], m[2][3]];
let cx = [m[0][0], m[1][0], m[2][0]];
let cy = [m[0][1], m[1][1], m[2][1]];
let cz = [m[0][2], m[1][2], m[2][2]];
let sx = vec3_len(cx);
let sy = vec3_len(cy);
let sz = vec3_len(cz);
let inv_sx = if sx > f32::EPSILON { 1.0 / sx } else { 1.0 };
let inv_sy = if sy > f32::EPSILON { 1.0 / sy } else { 1.0 };
let inv_sz = if sz > f32::EPSILON { 1.0 / sz } else { 1.0 };
let r00 = cx[0] * inv_sx;
let r10 = cx[1] * inv_sx;
let r20 = cx[2] * inv_sx;
let r01 = cy[0] * inv_sy;
let r11 = cy[1] * inv_sy;
let r21 = cy[2] * inv_sy;
let r02 = cz[0] * inv_sz;
let r12 = cz[1] * inv_sz;
let r22 = cz[2] * inv_sz;
let rotation = rot_matrix_to_quat([[r00, r01, r02], [r10, r11, r12], [r20, r21, r22]]);
Self::Trs {
translation,
rotation,
scale: [sx, sy, sz],
}
}
}
fn vec3_len(v: [f32; 3]) -> f32 {
(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
}
fn trs_to_matrix(t: [f32; 3], r: [f32; 4], s: [f32; 3]) -> [[f32; 4]; 4] {
let (x, y, z, w) = (r[0], r[1], r[2], r[3]);
let xx = x * x;
let yy = y * y;
let zz = z * z;
let xy = x * y;
let xz = x * z;
let yz = y * z;
let wx = w * x;
let wy = w * y;
let wz = w * z;
let r00 = 1.0 - 2.0 * (yy + zz);
let r01 = 2.0 * (xy - wz);
let r02 = 2.0 * (xz + wy);
let r10 = 2.0 * (xy + wz);
let r11 = 1.0 - 2.0 * (xx + zz);
let r12 = 2.0 * (yz - wx);
let r20 = 2.0 * (xz - wy);
let r21 = 2.0 * (yz + wx);
let r22 = 1.0 - 2.0 * (xx + yy);
[
[r00 * s[0], r01 * s[1], r02 * s[2], t[0]],
[r10 * s[0], r11 * s[1], r12 * s[2], t[1]],
[r20 * s[0], r21 * s[1], r22 * s[2], t[2]],
[0.0, 0.0, 0.0, 1.0],
]
}
fn rot_matrix_to_quat(m: [[f32; 3]; 3]) -> [f32; 4] {
let trace = m[0][0] + m[1][1] + m[2][2];
if trace > 0.0 {
let s = (trace + 1.0).sqrt() * 2.0;
let w = 0.25 * s;
let x = (m[2][1] - m[1][2]) / s;
let y = (m[0][2] - m[2][0]) / s;
let z = (m[1][0] - m[0][1]) / s;
[x, y, z, w]
} else if m[0][0] > m[1][1] && m[0][0] > m[2][2] {
let s = (1.0 + m[0][0] - m[1][1] - m[2][2]).sqrt() * 2.0;
let w = (m[2][1] - m[1][2]) / s;
let x = 0.25 * s;
let y = (m[0][1] + m[1][0]) / s;
let z = (m[0][2] + m[2][0]) / s;
[x, y, z, w]
} else if m[1][1] > m[2][2] {
let s = (1.0 + m[1][1] - m[0][0] - m[2][2]).sqrt() * 2.0;
let w = (m[0][2] - m[2][0]) / s;
let x = (m[0][1] + m[1][0]) / s;
let y = 0.25 * s;
let z = (m[1][2] + m[2][1]) / s;
[x, y, z, w]
} else {
let s = (1.0 + m[2][2] - m[0][0] - m[1][1]).sqrt() * 2.0;
let w = (m[1][0] - m[0][1]) / s;
let x = (m[0][2] + m[2][0]) / s;
let y = (m[1][2] + m[2][1]) / s;
let z = 0.25 * s;
[x, y, z, w]
}
}
#[derive(Clone, Debug)]
pub struct Node {
pub name: Option<String>,
pub transform: Transform,
pub children: Vec<NodeId>,
pub mesh: Option<MeshId>,
pub camera: Option<CameraId>,
pub light: Option<LightId>,
pub skin: Option<SkinId>,
pub audio_emitter: Option<AudioEmitterId>,
pub extras: HashMap<String, serde_json::Value>,
}
impl Node {
pub fn new() -> Self {
Self {
name: None,
transform: Transform::identity(),
children: Vec::new(),
mesh: None,
camera: None,
light: None,
skin: None,
audio_emitter: None,
extras: HashMap::new(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_transform(mut self, transform: Transform) -> Self {
self.transform = transform;
self
}
pub fn with_mesh(mut self, mesh: MeshId) -> Self {
self.mesh = Some(mesh);
self
}
pub fn with_audio_emitter(mut self, emitter: AudioEmitterId) -> Self {
self.audio_emitter = Some(emitter);
self
}
}
impl Default for Node {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct Scene3D {
pub nodes: Vec<Node>,
pub roots: Vec<NodeId>,
pub meshes: Vec<Mesh>,
pub materials: Vec<Material>,
pub textures: Vec<Texture>,
pub skeletons: Vec<Skeleton>,
pub skins: Vec<Skin>,
pub animations: Vec<Animation>,
pub cameras: Vec<Camera>,
pub lights: Vec<Light>,
pub audio_sources: Vec<AudioSource>,
pub audio_emitters: Vec<AudioEmitter>,
pub up_axis: Axis,
pub front_axis: Axis,
pub unit: Unit,
pub extras: HashMap<String, serde_json::Value>,
}
impl Scene3D {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
roots: Vec::new(),
meshes: Vec::new(),
materials: Vec::new(),
textures: Vec::new(),
skeletons: Vec::new(),
skins: Vec::new(),
animations: Vec::new(),
cameras: Vec::new(),
lights: Vec::new(),
audio_sources: Vec::new(),
audio_emitters: Vec::new(),
up_axis: Axis::PosY,
front_axis: Axis::NegZ,
unit: Unit::Metres,
extras: HashMap::new(),
}
}
pub fn add_node(&mut self, node: Node) -> NodeId {
let id = NodeId(self.nodes.len() as u32);
self.nodes.push(node);
id
}
pub fn add_mesh(&mut self, mesh: Mesh) -> MeshId {
let id = MeshId(self.meshes.len() as u32);
self.meshes.push(mesh);
id
}
pub fn add_material(&mut self, material: Material) -> MaterialId {
let id = MaterialId(self.materials.len() as u32);
self.materials.push(material);
id
}
pub fn add_texture(&mut self, texture: Texture) -> TextureId {
let id = TextureId(self.textures.len() as u32);
self.textures.push(texture);
id
}
pub fn add_skeleton(&mut self, skeleton: Skeleton) -> SkeletonId {
let id = SkeletonId(self.skeletons.len() as u32);
self.skeletons.push(skeleton);
id
}
pub fn add_skin(&mut self, skin: Skin) -> SkinId {
let id = SkinId(self.skins.len() as u32);
self.skins.push(skin);
id
}
pub fn add_animation(&mut self, animation: Animation) -> usize {
let idx = self.animations.len();
self.animations.push(animation);
idx
}
pub fn add_camera(&mut self, camera: Camera) -> CameraId {
let id = CameraId(self.cameras.len() as u32);
self.cameras.push(camera);
id
}
pub fn add_light(&mut self, light: Light) -> LightId {
let id = LightId(self.lights.len() as u32);
self.lights.push(light);
id
}
pub fn add_audio_source(&mut self, source: AudioSource) -> AudioSourceId {
let id = AudioSourceId(self.audio_sources.len() as u32);
self.audio_sources.push(source);
id
}
pub fn add_audio_emitter(&mut self, emitter: AudioEmitter) -> AudioEmitterId {
let id = AudioEmitterId(self.audio_emitters.len() as u32);
self.audio_emitters.push(emitter);
id
}
pub fn audio_source(&self, id: AudioSourceId) -> Option<&AudioSource> {
self.audio_sources.get(id.0 as usize)
}
pub fn audio_emitter(&self, id: AudioEmitterId) -> Option<&AudioEmitter> {
self.audio_emitters.get(id.0 as usize)
}
pub fn add_root(&mut self, node: NodeId) {
self.roots.push(node);
}
pub fn node(&self, id: NodeId) -> Option<&Node> {
self.nodes.get(id.0 as usize)
}
pub fn node_mut(&mut self, id: NodeId) -> Option<&mut Node> {
self.nodes.get_mut(id.0 as usize)
}
pub fn mesh(&self, id: MeshId) -> Option<&Mesh> {
self.meshes.get(id.0 as usize)
}
pub fn triangle_count(&self) -> usize {
self.meshes
.iter()
.flat_map(|m| m.primitives.iter())
.map(|p| p.triangle_count())
.sum()
}
pub fn vertex_count(&self) -> usize {
self.meshes
.iter()
.flat_map(|m| m.primitives.iter())
.map(|p| p.positions.len())
.sum()
}
}
impl Default for Scene3D {
fn default() -> Self {
Self::new()
}
}