#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BoneWeightExport {
pub bone_names: Vec<String>,
pub weights: Vec<Vec<(usize, f32)>>,
}
#[allow(dead_code)]
pub fn export_bone_weights(
bone_names: &[&str],
weights: &[Vec<(usize, f32)>],
) -> BoneWeightExport {
BoneWeightExport {
bone_names: bone_names.iter().map(|s| s.to_string()).collect(),
weights: weights.to_vec(),
}
}
#[allow(dead_code)]
pub fn weight_vertex_count(exp: &BoneWeightExport) -> usize {
exp.weights.len()
}
#[allow(dead_code)]
pub fn weight_bone_count(exp: &BoneWeightExport) -> usize {
exp.bone_names.len()
}
#[allow(dead_code)]
pub fn weight_to_json(exp: &BoneWeightExport) -> String {
let mut s = String::from("{\"bones\":[");
for (i, name) in exp.bone_names.iter().enumerate() {
if i > 0 {
s.push(',');
}
s.push('"');
s.push_str(name);
s.push('"');
}
s.push_str("],\"vertex_count\":");
s.push_str(&exp.weights.len().to_string());
s.push('}');
s
}
#[allow(dead_code)]
pub fn weight_to_bytes(exp: &BoneWeightExport) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&(exp.weights.len() as u32).to_le_bytes());
for w in &exp.weights {
buf.extend_from_slice(&(w.len() as u32).to_le_bytes());
for &(bi, bw) in w {
buf.extend_from_slice(&(bi as u32).to_le_bytes());
buf.extend_from_slice(&bw.to_le_bytes());
}
}
buf
}
#[allow(dead_code)]
pub fn max_influences_per_vertex(exp: &BoneWeightExport) -> usize {
exp.weights.iter().map(|w| w.len()).max().unwrap_or(0)
}
#[allow(dead_code)]
pub fn normalize_bone_weights_export(exp: &mut BoneWeightExport) {
for w in &mut exp.weights {
let sum: f32 = w.iter().map(|&(_, v)| v).sum();
if sum > 0.0 {
for pair in w.iter_mut() {
pair.1 /= sum;
}
}
}
}
#[allow(dead_code)]
pub fn bone_weight_export_size(exp: &BoneWeightExport) -> usize {
weight_to_bytes(exp).len()
}
#[cfg(test)]
mod tests {
use super::*;
fn sample() -> BoneWeightExport {
export_bone_weights(
&["hip", "spine", "head"],
&[
vec![(0, 0.5), (1, 0.3), (2, 0.2)],
vec![(1, 1.0)],
],
)
}
#[test]
fn test_export_bone_weights() {
let e = sample();
assert_eq!(e.bone_names.len(), 3);
assert_eq!(e.weights.len(), 2);
}
#[test]
fn test_weight_vertex_count() {
assert_eq!(weight_vertex_count(&sample()), 2);
}
#[test]
fn test_weight_bone_count() {
assert_eq!(weight_bone_count(&sample()), 3);
}
#[test]
fn test_weight_to_json() {
let j = weight_to_json(&sample());
assert!(j.contains("\"bones\""));
assert!(j.contains("\"hip\""));
assert!(j.contains("\"vertex_count\":2"));
}
#[test]
fn test_weight_to_bytes() {
let b = weight_to_bytes(&sample());
assert!(!b.is_empty());
let vc = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
assert_eq!(vc, 2);
}
#[test]
fn test_max_influences() {
assert_eq!(max_influences_per_vertex(&sample()), 3);
}
#[test]
fn test_normalize() {
let mut e = export_bone_weights(&["a", "b"], &[vec![(0, 2.0), (1, 2.0)]]);
normalize_bone_weights_export(&mut e);
let sum: f32 = e.weights[0].iter().map(|p| p.1).sum();
assert!((sum - 1.0).abs() < 1e-5);
}
#[test]
fn test_bone_weight_export_size() {
let sz = bone_weight_export_size(&sample());
assert!(sz > 4);
}
#[test]
fn test_empty_export() {
let e = export_bone_weights(&[], &[]);
assert_eq!(weight_vertex_count(&e), 0);
assert_eq!(max_influences_per_vertex(&e), 0);
}
#[test]
fn test_normalize_zero_sum() {
let mut e = export_bone_weights(&["a"], &[vec![(0, 0.0)]]);
normalize_bone_weights_export(&mut e);
assert!((e.weights[0][0].1).abs() < 1e-5);
}
}