synthda-demo / scripts /soma.test.mjs
evelynsaltt's picture
Add fitted native SOMA bodies to the motion playground
c3ad958 verified
Raw History Blame Contribute Delete
3.09 kB
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {Matrix4,Vector3,Quaternion} from '../vendor/three.module.js';
const root=new URL('../assets/soma/',import.meta.url),read=name=>JSON.parse(fs.readFileSync(new URL(name,root),'utf8'));
const rig=read('rig.json'),reports=read('fit-report.json');
test('SOMA export has valid native mesh, skin weights, topology and bind transforms',()=>{
assert.equal(rig.model,'SOMA-X');assert.equal(rig.vertices.length,4505);assert.equal(rig.faces.length,9006);assert.equal(rig.jointNames.length,78);
for(const f of rig.faces)for(const j of f)assert.ok(Number.isInteger(j)&&j>=0&&j<rig.vertices.length);
rig.vertices.forEach((v,i)=>{assert.ok(v.every(Number.isFinite));assert.ok(Math.abs(rig.skinWeights[i].reduce((a,b)=>a+b,0)-1)<1e-5);rig.skinIndices[i].forEach(j=>assert.ok(j>=0&&j<78));});
for(const b of rig.bindMatrices)assert.ok(Math.abs(new Matrix4().fromArray(b.flat()).determinant())>0.9);
});
test('All 33 SOMA animations match saved frame counts and have usable normalized transforms',()=>{
assert.equal(Object.keys(reports).length,33);
const inv=rig.bindMatrices.map(m=>new Matrix4().fromArray(m.flat()).transpose().invert());
for(const [name,r] of Object.entries(reports)){
const bytes=fs.readFileSync(new URL(name+'.bin',root)),a=new Float32Array(bytes.buffer,bytes.byteOffset,bytes.length/4);
assert.equal(a.length,r.frames*2*78*7,name);assert.ok(r.scale>0&&Number.isFinite(r.meanJointError));
for(let i=0;i<a.length;i+=7){assert.ok(Math.abs(Math.hypot(...a.subarray(i,i+4))-1)<1e-5,name);assert.ok(a.subarray(i,i+7).every(Number.isFinite));}
const seq=JSON.parse(fs.readFileSync(new URL(`assets/data/${name.replace(/-(sourceA|sourceB|[1-9])$/,'')}.json`,new URL('../',import.meta.url))));assert.equal(r.frames,seq.sourceA.length);
// Evaluate browser skinning at key late frames to catch invalid transforms/scaling.
for(const frame of [0,75,r.frames-1])for(const v of [0,100,1000,3000,4504]){
const output=new Vector3();for(let k=0;k<4;k++){const j=rig.skinIndices[v][k],i=(frame*78+j)*7,m=new Matrix4().compose(new Vector3().fromArray(a,i+4),new Quaternion().fromArray(a,i),new Vector3(r.scale,r.scale,r.scale)).multiply(inv[j]);output.addScaledVector(new Vector3(...rig.vertices[v]).applyMatrix4(m),rig.skinWeights[v][k]);}
assert.ok(output.toArray().every(Number.isFinite));assert.ok(output.length()<10,name);
}
}
});
test('SOMA display filter reduces Example 2 late-frame joint acceleration',()=>{
const r=reports['fall-real-synthetic-2-5'],b=fs.readFileSync(new URL('fall-real-synthetic-2-5.bin',root)),a=new Float32Array(b.buffer,b.byteOffset,b.length/4),stride=78*7;
const energy=offset=>{let sum=0,n=0;for(let f=76;f<r.frames-1;f++)for(let j=0;j<78;j++)for(let c=4;c<7;c++){const i=offset+f*stride+j*7+c,d=a[i+stride]-2*a[i]+a[i-stride];sum+=d*d;n++;}return Math.sqrt(sum/n);};
const original=energy(0),smooth=energy(r.frames*stride);assert.ok(smooth<original*.4);console.log('SOMA late-frame position second-difference RMS:',{original,smooth});
});