mirror of
https://github.com/scurest/apicula.git
synced 2025-06-18 22:55:33 -04:00
Refactor: pack all weights in a skeleton into one buffer
Instead of every vert having a SmallVec of its influences, have it store an offset into the packed buffer of all the influences instead. This is way more efficient. Like, 10% of the size it was before. Also lets us drop the smallvec dependency.
This commit is contained in:
parent
c81d9d5269
commit
ae33d9a919
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -42,7 +42,6 @@ dependencies = [
|
||||
"json 0.11.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wild 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -13,7 +13,6 @@ glium = "0.23.0"
|
||||
json = "0.11"
|
||||
log = { version = "0.4.6", features = ["std"] }
|
||||
png = "0.11.0"
|
||||
smallvec = "0.6.5"
|
||||
termcolor = "0.3.5"
|
||||
time = "0.1.36"
|
||||
wild = "2.0.2"
|
||||
|
@ -411,10 +411,8 @@ fn library_controllers(xml: &mut Xml, ctx: &Ctx) {
|
||||
let encode = |x: f32| (x * 4096.0) as u32;
|
||||
let decode = |x: u32| x as f64 / 4096.0;
|
||||
weights_lut.clear();
|
||||
for v in &ctx.skel.vertices {
|
||||
for influence in &v.influences {
|
||||
weights_lut.push(encode(influence.weight));
|
||||
}
|
||||
for w in &ctx.skel.weights {
|
||||
weights_lut.push(encode(w.weight));
|
||||
}
|
||||
// Here is the list of all weights.
|
||||
xml!(xml;
|
||||
@ -439,21 +437,21 @@ fn library_controllers(xml: &mut Xml, ctx: &Ctx) {
|
||||
/joints>;
|
||||
);
|
||||
|
||||
let num_verts = ctx.skel.vertices.len();
|
||||
let num_verts = ctx.prims.vertices.len();
|
||||
xml!(xml;
|
||||
<vertex_weights count=[(num_verts)]>;
|
||||
<input semantic=["JOINT"] source=["#controller-joints"] offset=["0"]/>;
|
||||
<input semantic=["WEIGHT"] source=["#controller-weights"] offset=["1"]/>;
|
||||
<vcount>
|
||||
for v in (&ctx.skel.vertices) {
|
||||
(v.influences.len())" "
|
||||
for vi in (0 .. ctx.prims.vertices.len()) {
|
||||
(ctx.skel.vert_weights(vi).len())" "
|
||||
}
|
||||
</vcount>;
|
||||
<v>
|
||||
for v in (&ctx.skel.vertices) {
|
||||
for influence in (&v.influences) {
|
||||
(influence.joint)" "
|
||||
(weights_lut.idx(&encode(influence.weight)))" "
|
||||
for vi in (0 .. ctx.prims.vertices.len()) {
|
||||
for w in (ctx.skel.vert_weights(vi)) {
|
||||
(w.joint)" "
|
||||
(weights_lut.idx(&encode(w.weight)))" "
|
||||
}
|
||||
}
|
||||
</v>;
|
||||
|
@ -210,7 +210,7 @@ fn mesh(ctx: &Ctx, gltf: &mut GlTF) {
|
||||
// glTF gives joint/weight influences in sets of 4 (JOINT_0 is a VEC4
|
||||
// accessor with the first four joints, JOINTS_1 has the next four, etc).
|
||||
// Find out how many sets we need.
|
||||
let num_sets = (ctx.skel.max_num_influences + 3) / 4;
|
||||
let num_sets = ((ctx.skel.max_num_weights + 3) / 4) as usize;
|
||||
|
||||
// Make sure joints fit in a byte
|
||||
assert!(ctx.skel.tree.node_count() <= 255);
|
||||
@ -223,15 +223,14 @@ fn mesh(ctx: &Ctx, gltf: &mut GlTF) {
|
||||
});
|
||||
let dat_len = {
|
||||
let dat = &mut gltf.buffers[buf].bytes;
|
||||
for sv in &ctx.skel.vertices {
|
||||
let mut i = 0;
|
||||
while i != 4 * num_sets {
|
||||
if i < sv.influences.len() {
|
||||
dat.push(sv.influences[i].joint as u8);
|
||||
for vi in 0 .. verts.len() {
|
||||
let ws = ctx.skel.vert_weights(vi);
|
||||
for i in 0 .. 4 * num_sets {
|
||||
if i < ws.len() {
|
||||
dat.push(ws[i].joint as u8);
|
||||
} else {
|
||||
dat.push(0);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
dat.len()
|
||||
@ -260,15 +259,14 @@ fn mesh(ctx: &Ctx, gltf: &mut GlTF) {
|
||||
});
|
||||
let dat_len = {
|
||||
let dat = &mut gltf.buffers[buf].bytes;
|
||||
for sv in &ctx.skel.vertices {
|
||||
let mut i = 0;
|
||||
while i != 4 * num_sets {
|
||||
if i < sv.influences.len() {
|
||||
dat.push_normalized_u8(sv.influences[i].weight);
|
||||
for vi in 0 .. verts.len() {
|
||||
let ws = ctx.skel.vert_weights(vi);
|
||||
for i in 0 .. 4 * num_sets {
|
||||
if i < ws.len() {
|
||||
dat.push_normalized_u8(ws[i].weight);
|
||||
} else {
|
||||
dat.push_normalized_u8(0.0);
|
||||
dat.push(0);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
dat.len()
|
||||
|
@ -13,7 +13,6 @@ extern crate time;
|
||||
extern crate png;
|
||||
extern crate termcolor;
|
||||
extern crate atty;
|
||||
extern crate smallvec;
|
||||
#[macro_use]
|
||||
extern crate json;
|
||||
extern crate wild;
|
||||
|
@ -113,27 +113,38 @@
|
||||
use cgmath::{Matrix4, SquareMatrix, One, ApproxEq};
|
||||
use super::vertex_record::VertexRecord;
|
||||
use super::{SMatrix, AMatrix};
|
||||
use super::{Skeleton, Joint, Transform, SkinVertex, Influence};
|
||||
use super::{Skeleton, Joint, Transform, Weight, WeightsOfs};
|
||||
use nitro::Model;
|
||||
use util::tree::{Tree, NodeIdx};
|
||||
|
||||
pub fn build_skeleton(vr: &VertexRecord, model: &Model, objects: &[Matrix4<f64>]) -> Skeleton {
|
||||
let mut b = Builder::new(model, objects);
|
||||
|
||||
// Caches the right skinvertex for each of the matrices in vr.
|
||||
let mut skin_vert_cache: Vec<Option<SkinVertex>> = vec![None; vr.matrices.len()];
|
||||
let mut max_num_influences = 0;
|
||||
// Caches the WeightOfs for each of the matrices in vr.
|
||||
let mut mat_cache: Vec<Option<WeightsOfs>> = vec![None; vr.matrices.len()];
|
||||
let mut max_num_weights = 0;
|
||||
let mut weights: Vec<Weight> = Vec::with_capacity(vr.matrices.len());
|
||||
|
||||
let vertices = vr.vertices.iter().map(|&mat_idx| {
|
||||
if skin_vert_cache[mat_idx as usize].is_none() {
|
||||
let mut sv = b.amatrix_to_skinvert(&vr.matrices[mat_idx as usize]);
|
||||
simplify_skinvert(&mut sv);
|
||||
max_num_influences = max_num_influences.max(sv.influences.len());
|
||||
skin_vert_cache[mat_idx as usize] = Some(sv);
|
||||
let verts = vr.vertices.iter().map(|&mat_idx| {
|
||||
if mat_cache[mat_idx as usize].is_none() {
|
||||
let mut ws = b.amatrix_to_weights(&vr.matrices[mat_idx as usize]);
|
||||
simplify_weights(&mut ws);
|
||||
assert!(ws.len() <= 255);
|
||||
max_num_weights = max_num_weights.max(ws.len() as u8);
|
||||
|
||||
// Add the weights for this vert to the weights array
|
||||
// and create an offset to them.
|
||||
let start = weights.len();
|
||||
let len = ws.len();
|
||||
assert!(start <= 0xffffff);
|
||||
weights.append(&mut ws);
|
||||
let ofs = WeightsOfs(start as u32 | ((len as u32) << 24));
|
||||
|
||||
mat_cache[mat_idx as usize] = Some(ofs);
|
||||
}
|
||||
|
||||
skin_vert_cache[mat_idx as usize].as_ref().unwrap().clone()
|
||||
}).collect();
|
||||
mat_cache[mat_idx as usize].unwrap()
|
||||
}).collect::<Vec<WeightsOfs>>();
|
||||
|
||||
if b.unusual_matrices {
|
||||
warn!("unusual matrices encountered in model {}; the skin for this \
|
||||
@ -160,8 +171,9 @@ pub fn build_skeleton(vr: &VertexRecord, model: &Model, objects: &[Matrix4<f64>]
|
||||
Skeleton {
|
||||
tree: b.graph,
|
||||
root,
|
||||
vertices,
|
||||
max_num_influences,
|
||||
max_num_weights,
|
||||
weights,
|
||||
verts,
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,15 +228,14 @@ impl<'a, 'b> Builder<'a, 'b> {
|
||||
self.roots.push(root);
|
||||
}
|
||||
|
||||
fn amatrix_to_skinvert(&mut self, amatrix: &AMatrix) -> SkinVertex {
|
||||
fn amatrix_to_weights(&mut self, amatrix: &AMatrix) -> Vec<Weight> {
|
||||
self.detect_unusual_matrices(amatrix);
|
||||
|
||||
let influences = amatrix.terms.iter().map(|term| {
|
||||
amatrix.terms.iter().map(|term| {
|
||||
let weight = term.weight;
|
||||
let joint = self.cmatrix_to_joint(&term.cmat.factors);
|
||||
Influence { weight, joint }
|
||||
}).collect();
|
||||
SkinVertex { influences }
|
||||
Weight { weight, joint }
|
||||
}).collect()
|
||||
}
|
||||
|
||||
fn cmatrix_to_joint(&mut self, mut factors: &[SMatrix]) -> NodeIdx {
|
||||
@ -348,20 +359,20 @@ impl<'a, 'b> Builder<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify_skinvert(sv: &mut SkinVertex) {
|
||||
fn simplify_weights(weights: &mut Vec<Weight>) {
|
||||
// Group like terms.
|
||||
for i in 0..sv.influences.len() {
|
||||
for j in (i+1)..sv.influences.len() {
|
||||
if sv.influences[i].joint == sv.influences[j].joint {
|
||||
sv.influences[i].weight += sv.influences[j].weight;
|
||||
sv.influences[j].weight = 0.0;
|
||||
for i in 0..weights.len() {
|
||||
for j in (i+1)..weights.len() {
|
||||
if weights[i].joint == weights[j].joint {
|
||||
weights[i].weight += weights[j].weight;
|
||||
weights[j].weight = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sv.influences.retain(|influence| influence.weight != 0.0);
|
||||
weights.retain(|influence| influence.weight != 0.0);
|
||||
|
||||
// Sort by weight, highest to lowest
|
||||
sv.influences.sort_by(|in1, in2| in2.weight.partial_cmp(&in1.weight).unwrap());
|
||||
weights.sort_by(|w1, w2| w2.weight.partial_cmp(&w1.weight).unwrap());
|
||||
}
|
||||
|
||||
/// Inverts a matrix, bumping its entries slightly if necessary until it is
|
||||
|
@ -57,16 +57,16 @@ pub use self::symbolic_matrix::{SMatrix, CMatrix, AMatrix, ATerm};
|
||||
|
||||
use cgmath::Matrix4;
|
||||
use nitro::Model;
|
||||
use smallvec::SmallVec;
|
||||
use util::tree::{Tree, NodeIdx};
|
||||
|
||||
/// Skeleton (or skin) for a model.
|
||||
pub struct Skeleton {
|
||||
pub tree: Tree<Joint>,
|
||||
pub root: NodeIdx,
|
||||
pub vertices: Vec<SkinVertex>,
|
||||
/// Largest number of influences on any vertex.
|
||||
pub max_num_influences: usize,
|
||||
pub max_num_weights: u8, // max weights on any vertex
|
||||
|
||||
pub weights: Vec<Weight>, // weights for all verts packed together
|
||||
verts: Vec<WeightsOfs>, // verts[vi] points to the weights for vertex vi in weights
|
||||
}
|
||||
|
||||
pub struct Joint {
|
||||
@ -85,18 +85,26 @@ pub enum Transform {
|
||||
Root,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SkinVertex {
|
||||
pub influences: SmallVec<[Influence; 1]>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Influence {
|
||||
pub struct Weight {
|
||||
pub weight: f32,
|
||||
pub joint: NodeIdx,
|
||||
}
|
||||
|
||||
/// Points to a slice skel.weights[start..start+len].
|
||||
/// start is the low 24 bits, len is the high 8 bits.
|
||||
#[derive(Copy, Clone)]
|
||||
struct WeightsOfs(u32);
|
||||
|
||||
|
||||
impl Skeleton {
|
||||
pub fn vert_weights(&self, vi: usize) -> &[Weight] {
|
||||
let ofs = self.verts[vi];
|
||||
let start = (ofs.0 & 0xffffff) as usize;
|
||||
let len = (ofs.0 >> 24) as usize;
|
||||
&self.weights[start .. start + len]
|
||||
}
|
||||
|
||||
pub fn build(model: &Model, objects: &[Matrix4<f64>]) -> Skeleton {
|
||||
// First play back rendering commands recording the symbolic value of
|
||||
// the matrix applied to every vertex.
|
||||
|
Loading…
Reference in New Issue
Block a user