// Independent little-endian B3D reader + forward kinematics. No encoder helpers, // engine mocks or assumptions that a magic header implies a working animation. import test from 'node:test'; import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { inflateSync } from 'node:zlib'; import { FAUNA_NAMES, FAUNA_ANIMATIONS, FAUNA_ART_MANIFEST, FAUNA_MATERIALS, faunaB3D, faunaTexture, faunaBounds, faunaPortraitSheet } from '../scripts/fauna-art.mjs'; import { png } from '../scripts/assets.mjs'; import { worldArt, animalOBJ } from '../scripts/world-art.mjs'; const names='hare deer fox rabbit red_squirrel bank_vole wood_mouse hedgehog badger roe_deer stoat weasel otter robin blackbird blue_tit barn_owl mallard common_frog smooth_newt brown_trout bumblebee earthworm'.split(' '); const hash=b=>createHash('sha256').update(b).digest('hex'); const sub=(a,b)=>a.map((v,i)=>v-b[i]); const dot=(a,b)=>a.reduce((s,n,i)=>s+n*b[i],0); const cross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]; const near=(a,b,epsilon=1e-5)=>assert.ok(Math.abs(a-b)bytes.readInt32LE(p); const floats=(p,n)=>Array.from({length:n},(_,i)=>bytes.readFloatLE(p+i*4)); function string(p,end) { const stop=bytes.indexOf(0,p); assert.ok(stop>=p&&stop=0&&b<=end); p=b; if(tag==='BB3D') { assert.equal(integer(a),1); chunks(a+4,b); } else if(tag==='TEXS') { const [name,next]=string(a,b); texture=name; assert.deepEqual([integer(next),integer(next+4)],[1,2]); assert.deepEqual(floats(next+8,5),[0,0,1,1,0]); assert.equal(next+28,b); } else if(tag==='BRUS') { brushCount++; assert.equal(integer(a),1); const [,next]=string(a+4,b); assert.deepEqual(floats(next,5),[1,1,1,1,0]); assert.deepEqual([integer(next+20),integer(next+24),integer(next+28)],[1,0,0]); assert.equal(next+32,b); } else if(tag==='NODE') { const [name,next]=string(a,b), transform=floats(next,10); const node={name,parent,pos:transform.slice(0,3),weights:[],keys:[]}; nodes.push(node); assert.deepEqual(transform.slice(3),[1,1,1,1,0,0,0]); chunks(next+40,b,node); } else if(tag==='MESH') { meshCount++; assert.equal(integer(a),0); chunks(a+4,b,parent); } else if(tag==='VRTS') { assert.deepEqual([integer(a),integer(a+4),integer(a+8)],[1,1,2]); assert.equal((size-12)%32,0); for(let q=a+12;qv+(p?.bind[i]||0)); const q=p?multiply(p.q,key.rotation):key.rotation; const pos=p?rotate(p.q,key.pos).map((v,i)=>v+p.pos[i]):key.pos; states.set(node,{bind,q,pos}); for(const [id,weight] of node.weights) { const point=rotate(q,sub(mesh.vertices[id].slice(0,3),bind)).map((v,i)=>v+pos[i]); output[id]=point.map(v=>v*weight); } } return output; } test('exact immutable 23-species/5-clip handoff, provenance, fresh deterministic outputs and fail-closed inputs',()=> { assert.deepEqual(FAUNA_NAMES,names); assert.deepEqual(FAUNA_ANIMATIONS,{idle:[0,19,24],locomotion:[20,39,24],feed:[40,59,24],flight_swim:[60,79,24],rest:[80,99,24]}); assert.equal(FAUNA_ART_MANIFEST.length,23); assert.equal(FAUNA_MATERIALS.length,16); for(const entry of FAUNA_ART_MANIFEST) { assert.ok(Object.isFrozen(entry)&&Object.isFrozen(entry.bounds.min)); assert.equal(entry.mesh,`hearth_fauna_${entry.id}.b3d`); assert.equal(entry.texture,`hearth_fauna_${entry.id}.png`); assert.deepEqual(entry.bounds,faunaBounds(entry.id)); assert.equal(entry.unitsPerNode,10); assert.equal(entry.visualSize,1); assert.equal(entry.license,'MIT'); assert.ok(entry.posture.length>15); assert.deepEqual(entry.clips,FAUNA_ANIMATIONS); assert.deepEqual(faunaB3D(entry.id),faunaB3D(entry.id)); assert.deepEqual(faunaTexture(entry.id),faunaTexture(entry.id)); const altered=faunaTexture(entry.id); altered.data.fill(0); assert.ok(faunaTexture(entry.id).data.some(v=>v)); } for(const bad of ['../hare','constructor','toString','Hare',null,{},'wolf']) { for(const fn of [faunaB3D,faunaTexture,faunaBounds]) assert.throws(()=>fn(bad),/Unknown fauna species/); } for(const bad of [-1,100,.5,NaN,null,'0']) assert.throws(()=>faunaPortraitSheet(bad),/Unknown fauna frame/); const art=worldArt(); assert.equal(art.size,421); for(const id of names) assert.ok(!art.has(`hearth_fauna_${id}.png`),'64px fauna atlas must not enter the 512-tile world sheet'); for(const id of ['hare','fox','deer']) assert.match(animalOBJ(id),/^# Original/,'legacy OBJ API remains available'); }); test('independent B3D parse validates all vertices, outward winding, normals, inset semantic UVs, normalized weights and bind bounds',()=> { const unique=new Set(); for(const id of names) { const bytes=faunaB3D(id),m=parse(bytes); unique.add(hash(bytes)); assert.ok(bytes.length<180000&&bytes.length>20000); assert.deepEqual(m.anim,[0,100,24]); assert.equal(m.texture,`hearth_fauna_${id}.png`); assert.equal(m.nodes.filter(n=>!n.parent).length,1); assert.equal(m.nodes[0].name,'Root'); assert.equal(new Set(m.nodes.map(n=>n.name)).size,m.nodes.length); assert.ok(m.nodes.length>=6&&m.nodes.length<=30); assert.ok(m.vertices.length>=144&&m.vertices.length<=1600); assert.equal(m.vertices.length%24,0); assert.equal(m.triangles.length,m.vertices.length/2); const weights=new Float64Array(m.vertices.length),references=new Set(); for(const n of m.nodes) for(const [v,w] of n.weights) { assert.ok(v>=0&&v0&&w<=1); weights[v]+=w; } for(const w of weights) near(w,1); assert.ok(m.vertices.flat().every(Number.isFinite)); for(const [a,b,c] of m.triangles) { assert.equal(new Set([a,b,c]).size,3); for(const v of [a,b,c]) { assert.ok(v>=0&&vm.vertices[i]),normal=cross(sub(vs[1].slice(0,3),vs[0].slice(0,3)),sub(vs[2].slice(0,3),vs[0].slice(0,3))); assert.ok(Math.hypot(...normal)>1e-6); const first=Math.floor(a/24)*24,box=m.vertices.slice(first,first+24),center=[0,1,2].map(i=>box.reduce((sum,v)=>sum+v[i],0)/24); assert.ok(dot(normal,sub(vs[0].slice(0,3),center))>0,`${id}: outward solid winding`); const tiles=vs.map(v=>v.slice(6).map(n=>Math.floor(n*4)).join()); assert.equal(new Set(tiles).size,1); for(const v of vs) { near(Math.hypot(...v.slice(3,6)),1); near(dot(normal,v.slice(3,6))/Math.hypot(...normal),1); for(const u of v.slice(6)) { assert.ok(u>0&&u<1); const px=u*64%16; assert.ok(px>=.5&&px<=15.5); } } } assert.equal(references.size,m.vertices.length); const bound=faunaBounds(id); near(bound.min[1],0); for(let i=0;i<3;i++) { near(Math.min(...m.vertices.map(v=>v[i]))/10,bound.min[i]); near(Math.max(...m.vertices.map(v=>v[i]))/10,bound.max[i]); assert.ok(bound.max[i]>bound.min[i]&&bound.max[i]-bound.min[i]<2); } } assert.equal(unique.size,23); }); test('every bone contains all 100 real poses; independent skinning proves each clip moves weighted geometry with no root motion or scale keys',()=> { for(const id of names) { const m=parse(faunaB3D(id)); for(const n of m.nodes) { assert.equal(n.keys.length,100); n.keys.forEach((key,i)=> { assert.equal(key.frame,i+1); assert.ok([...key.pos,...key.rotation].every(Number.isFinite)); near(Math.hypot(...key.rotation),1); if(n.name==='Root') { assert.deepEqual(key.pos,[0,0,0]); assert.deepEqual(key.rotation,[1,0,0,0]); } }); } for(const [clip,[a,b]] of Object.entries(FAUNA_ANIMATIONS)) { const poses=Array.from({length:20},(_,i)=>skin(m,a+i)); assert.ok(poses.flat(2).every(Number.isFinite)); let displacement=0; for(let f=1;f<20;f++) for(let v=0;v.001,`${id} ${clip}: genuine skeletal displacement`); for(let v=0;vn.name==='Body'); for(const key of body.keys) near(key.pos[0],body.pos[0]); } }); test('authored anatomy and chained joint motion distinguish ears, antlers, wings, fins, spines, mustelids and a twelve-segment earthworm',()=> { const models=Object.fromEntries(names.map(id=>[id,parse(faunaB3D(id))])); const node=(id,name)=>models[id].nodes.find(n=>n.name===name); const extent=(id,name,axis)=> { const values=node(id,name).weights.map(([v])=>models[id].vertices[v][axis]); return Math.max(...values)-Math.min(...values); }; assert.ok(extent('hare','Ear_-1',1)>extent('rabbit','Ear_-1',1)*1.5); assert.ok(faunaBounds('deer').max[1]>faunaBounds('roe_deer').max[1]); assert.ok(node('deer','Antler_-1').parent===node('deer','Head')); assert.ok(extent('red_squirrel','Tail',1)>extent('red_squirrel','Tail',0)*2,'raised slender squirrel brush'); near(extent('fox','Tail',1),extent('fox','Tail',0)); assert.ok(extent('wood_mouse','Tail_Tip',2)>extent('bank_vole','Tail_Tip',2)*2); assert.ok(extent('stoat','Body',2)>extent('weasel','Body',2)); for(const id of ['hare','deer','fox','badger','otter','common_frog','smooth_newt']) { assert.equal(node(id,'Shin_-1_-1').parent,node(id,'Leg_-1_-1')); assert.notDeepEqual(node(id,'Leg_-1_-1').keys[24].rotation,node(id,'Leg_1_-1').keys[24].rotation); } for(const id of ['robin','blackbird','blue_tit','barn_owl','mallard','bumblebee']) { const wing=node(id,'Wing_-1'); assert.ok(wing.weights.length); assert.notDeepEqual(wing.keys[60].rotation,wing.keys[65].rotation); assert.notDeepEqual(wing.keys[65].rotation,node(id,'Wing_1').keys[65].rotation); if(id!=='bumblebee') assert.equal(node(id,'Wing_Tip_-1').parent,wing); } for(const id of ['otter','smooth_newt','brown_trout']) { const tail=node(id,'Tail'),tip=node(id,'Tail_Tip'); assert.equal(tip.parent,tail); assert.notDeepEqual(tail.keys[60].rotation,tail.keys[65].rotation); } const segments=models.earthworm.nodes.filter(n=>n.name.startsWith('Segment_')); assert.equal(segments.length,12); segments.slice(1).forEach((n,i)=>assert.equal(n.parent,segments[i])); assert.notDeepEqual(segments[0].keys[24].rotation,segments[5].keys[24].rotation); assert.ok(models.hedgehog.vertices.length>models.bank_vole.vertices.length,'spine geometry, not only a colour change'); }); test('64px semantic RGBA atlases encode exact opaque pixels, distinct species, badger stripes, otter cream and bee bands',()=> { const distinct=new Set(),pixel=(image,x,y)=>[...image.data.slice((y*64+x)*4,(y*64+x+1)*4)]; const material=(id,name,x,y)=>pixel(faunaTexture(id),FAUNA_MATERIALS.indexOf(name)%4*16+x,Math.floor(FAUNA_MATERIALS.indexOf(name)/4)*16+y); for(const id of names) { const image=faunaTexture(id); assert.deepEqual([image.width,image.height,image.data.length],[64,64,16384]); distinct.add(hash(image.data)); assert.ok(image.data.filter((_,i)=>i%4===3).every(v=>v===255)); const bytes=png(64,(x,y)=>pixel(image,x,y)),parts=[]; assert.deepEqual([...bytes.slice(16,24)],[0,0,0,64,0,0,0,64]); for(let i=8;i { const image=faunaPortraitSheet(); assert.deepEqual([image.width,image.height,image.data.length],[512,768,1572864]); assert.deepEqual(image,faunaPortraitSheet()); assert.notEqual(hash(image.data),hash(faunaPortraitSheet(65).data)); const cells=new Set(); for(let i=0;i<23;i++) { const cell=Buffer.alloc(128*128*4); for(let y=0;y<128;y++) cell.set(image.data.slice(((Math.floor(i/4)*128+y)*512+i%4*128)*4,((Math.floor(i/4)*128+y)*512+i%4*128+128)*4),y*128*4); cells.add(hash(cell)); const colors=new Set(); for(let j=0;j4,`${names[i]}: actual shaded mesh, not an empty paper tile`); } assert.equal(cells.size,23); });