use crate::mesh::{Mesh, Primitive};
use crate::scene::{mat4_mul, NodeId, Scene3D};
fn mat4_madd(out: &mut [[f32; 4]; 4], w: f32, m: &[[f32; 4]; 4]) {
for (or, mr) in out.iter_mut().zip(m.iter()) {
for (oc, mc) in or.iter_mut().zip(mr.iter()) {
*oc += w * mc;
}
}
}
fn linear_det(m: &[[f32; 4]; 4]) -> f32 {
let (a, b, c) = (m[0][0], m[0][1], m[0][2]);
let (d, e, f) = (m[1][0], m[1][1], m[1][2]);
let (g, h, i) = (m[2][0], m[2][1], m[2][2]);
a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g)
}
fn linear_inverse_transpose(m: &[[f32; 4]; 4]) -> Option<[[f32; 3]; 3]> {
let det = linear_det(m);
if !det.is_finite() || det == 0.0 {
return None;
}
let inv_det = 1.0 / det;
let (a, b, c) = (m[0][0], m[0][1], m[0][2]);
let (d, e, f) = (m[1][0], m[1][1], m[1][2]);
let (g, h, i) = (m[2][0], m[2][1], m[2][2]);
let out = [
[
(e * i - f * h) * inv_det,
(f * g - d * i) * inv_det,
(d * h - e * g) * inv_det,
],
[
(c * h - b * i) * inv_det,
(a * i - c * g) * inv_det,
(b * g - a * h) * inv_det,
],
[
(b * f - c * e) * inv_det,
(c * d - a * f) * inv_det,
(a * e - b * d) * inv_det,
],
];
if out.iter().flatten().all(|v| v.is_finite()) {
Some(out)
} else {
None
}
}
fn mul3(l: [[f32; 3]; 3], v: [f32; 3]) -> [f32; 3] {
[
l[0][0] * v[0] + l[0][1] * v[1] + l[0][2] * v[2],
l[1][0] * v[0] + l[1][1] * v[1] + l[1][2] * v[2],
l[2][0] * v[0] + l[2][1] * v[1] + l[2][2] * v[2],
]
}
fn normalize3(v: [f32; 3]) -> Option<[f32; 3]> {
let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
if len.is_finite() && len > 0.0 {
Some([v[0] / len, v[1] / len, v[2] / len])
} else {
None
}
}
const IDENTITY: [[f32; 4]; 4] = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
];
impl Scene3D {
pub fn joint_matrices(&self, node: NodeId) -> Option<Vec<[[f32; 4]; 4]>> {
self.joint_matrices_with(node, &self.world_node_transforms())
}
pub fn joint_matrices_with(
&self,
node: NodeId,
worlds: &[Option<[[f32; 4]; 4]>],
) -> Option<Vec<[[f32; 4]; 4]>> {
let node = self.node(node)?;
let skin = self.skins.get(node.skin?.0 as usize)?;
let skeleton = self.skeletons.get(skin.skeleton.0 as usize)?;
let n_joints = skeleton.joints.len();
let ibms = &skeleton.inverse_bind_matrices;
if !ibms.is_empty() && ibms.len() < n_joints {
return None;
}
let mut out = Vec::with_capacity(n_joints);
for (j, joint) in skeleton.joints.iter().enumerate() {
let world = *worlds.get(joint.0 as usize)?;
let world = world?;
let ibm = if ibms.is_empty() { IDENTITY } else { ibms[j] };
out.push(mat4_mul(world, ibm));
}
Some(out)
}
}
impl Scene3D {
pub fn world_mesh(&self, node: NodeId) -> Option<Mesh> {
self.world_mesh_with(node, &self.world_node_transforms())
}
pub fn effective_morph_weights(&self, node: NodeId) -> Option<&[f32]> {
let n = self.node(node)?;
let mesh = self.meshes.get(n.mesh?.0 as usize)?;
Some(if n.weights.is_empty() {
&mesh.weights
} else {
&n.weights
})
}
pub fn skin_root(&self, skin: crate::scene::SkinId) -> Option<NodeId> {
let skin = self.skins.get(skin.0 as usize)?;
if let Some(r) = skin.root_node {
return ((r.0 as usize) < self.nodes.len()).then_some(r);
}
let skeleton = self.skeletons.get(skin.skeleton.0 as usize)?;
let joints = &skeleton.joints;
if joints.is_empty() {
return None;
}
let n = self.nodes.len();
let parents = self.parents();
let depth = |mut node: NodeId| -> Option<usize> {
if (node.0 as usize) >= n {
return None;
}
let mut d = 0usize;
while let Some(p) = parents.get(node.0 as usize).copied().flatten() {
node = p;
d += 1;
if d > n {
break;
}
}
Some(d)
};
let mut lca = *joints.first()?;
let mut lca_depth = depth(lca)?;
for &joint in &joints[1..] {
let mut a = lca;
let mut da = lca_depth;
let mut b = joint;
let mut db = depth(joint)?;
while da > db {
a = parents[a.0 as usize]?;
da -= 1;
}
while db > da {
b = parents[b.0 as usize]?;
db -= 1;
}
while a != b {
match (parents[a.0 as usize], parents[b.0 as usize]) {
(Some(pa), Some(pb)) => {
a = pa;
b = pb;
da -= 1;
}
_ => return None,
}
}
lca = a;
lca_depth = da;
}
Some(lca)
}
pub fn world_mesh_with(&self, node: NodeId, worlds: &[Option<[[f32; 4]; 4]>]) -> Option<Mesh> {
self.world_mesh_impl(node, worlds, None)
}
pub(crate) fn world_mesh_impl(
&self,
node: NodeId,
worlds: &[Option<[[f32; 4]; 4]>],
weight_override: Option<&[f32]>,
) -> Option<Mesh> {
let n = self.node(node)?;
let mesh = self.meshes.get(n.mesh?.0 as usize)?;
let world = (*worlds.get(node.0 as usize)?)?;
let weights: &[f32] = match weight_override {
Some(w) => w,
None if !n.weights.is_empty() => &n.weights,
None => &mesh.weights,
};
let palette = if n.skin.is_some() {
Some(self.joint_matrices_with(node, worlds)?)
} else {
None
};
let mut out = mesh.clone();
for prim in &mut out.primitives {
let morphed = prim.morphed(weights);
let has_influences = morphed.joints.is_some() && morphed.weights.is_some();
*prim = match &palette {
Some(p) if has_influences => morphed.skinned(p),
_ => morphed.transformed(world),
};
}
out.weights = Vec::new();
out.target_names = Vec::new();
Some(out)
}
}
impl Primitive {
pub fn skinned(&self, palette: &[[[f32; 4]; 4]]) -> Primitive {
let (Some(joints), Some(weights)) = (self.joints.as_ref(), self.weights.as_ref()) else {
return self.clone();
};
let mut out = self.clone();
let n = out.positions.len();
for v in 0..n {
let (Some(jrow), Some(wrow)) = (joints.get(v), weights.get(v)) else {
continue;
};
let mut m = [[0.0f32; 4]; 4];
let mut any = false;
for i in 0..4 {
let w = wrow[i];
let j = jrow[i] as usize;
if !w.is_finite() || w <= 0.0 || j >= palette.len() {
continue;
}
mat4_madd(&mut m, w, &palette[j]);
any = true;
}
if !any {
continue;
}
let p = out.positions[v];
out.positions[v] = [
m[0][0] * p[0] + m[0][1] * p[1] + m[0][2] * p[2] + m[0][3],
m[1][0] * p[0] + m[1][1] * p[1] + m[1][2] * p[2] + m[1][3],
m[2][0] * p[0] + m[2][1] * p[1] + m[2][2] * p[2] + m[2][3],
];
if let Some(normals) = out.normals.as_mut() {
if let Some(nv) = normals.get_mut(v) {
if let Some(it) = linear_inverse_transpose(&m) {
if let Some(u) = normalize3(mul3(it, *nv)) {
*nv = u;
}
}
}
}
if let Some(tangents) = out.tangents.as_mut() {
if let Some(tv) = tangents.get_mut(v) {
let l = [
[m[0][0], m[0][1], m[0][2]],
[m[1][0], m[1][1], m[1][2]],
[m[2][0], m[2][1], m[2][2]],
];
if let Some(u) = normalize3(mul3(l, [tv[0], tv[1], tv[2]])) {
tv[0] = u[0];
tv[1] = u[1];
tv[2] = u[2];
}
if linear_det(&m) < 0.0 {
tv[3] = -tv[3];
}
}
}
}
out.joints = None;
out.weights = None;
out.targets = Vec::new();
out
}
pub fn normalize_joint_weights(&self) -> Primitive {
let mut out = self.clone();
if let Some(weights) = out.weights.as_mut() {
for row in weights.iter_mut() {
for w in row.iter_mut() {
if !w.is_finite() || *w < 0.0 {
*w = 0.0;
}
}
let sum: f32 = row.iter().sum();
if sum > 0.0 {
for w in row.iter_mut() {
*w /= sum;
}
}
}
}
out
}
}
impl Mesh {
pub fn skinned(&self, palette: &[[[f32; 4]; 4]]) -> Mesh {
let mut out = self.clone();
for prim in &mut out.primitives {
*prim = prim.skinned(palette);
}
out.weights = Vec::new();
out.target_names = Vec::new();
out
}
pub fn normalize_joint_weights(&self) -> Mesh {
let mut out = self.clone();
for prim in &mut out.primitives {
*prim = prim.normalize_joint_weights();
}
out
}
}