import { readFileSync } from 'node:fs' import draco3d from '../../../../../node_modules/draco3d/draco3d.js' const d = await draco3d.createDecoderModule({}) for (const path of process.argv.slice(2)) { const raw=readFileSync(path), size=raw.readUInt32LE(12), j=JSON.parse(raw.toString('utf8',20,20+size)), bin=raw.subarray(size+28) const report={path,triangles:0,bad:[]} for(const p of j.meshes[0].primitives){ const e=p.extensions.KHR_draco_mesh_compression,v=j.bufferViews[e.bufferView],data=bin.subarray(v.byteOffset||0,(v.byteOffset||0)+v.byteLength) const decoder=new d.Decoder(), buffer=new d.DecoderBuffer(), mesh=new d.Mesh();buffer.Init(new Int8Array(data),data.length) const status=decoder.DecodeBufferToMesh(buffer,mesh);if(!status.ok())throw Error(status.error_msg()) function attr(s){const a=decoder.GetAttributeByUniqueId(mesh,e.attributes[s]),ptr=d._malloc(mesh.num_points()*12);decoder.GetAttributeDataArrayForAllPoints(mesh,a,d.DT_FLOAT32,mesh.num_points()*12,ptr);const result=new Float32Array(d.HEAPF32.buffer,ptr,mesh.num_points()*3).slice();d._free(ptr);return result} const positions=attr('POSITION'),normals=attr('NORMAL'),ptr=d._malloc(mesh.num_faces()*12);decoder.GetTrianglesUInt32Array(mesh,mesh.num_faces()*12,ptr);const indices=new Uint32Array(d.HEAPU32.buffer,ptr,mesh.num_faces()*3).slice();d._free(ptr) for(let f=0;fArray.from(positions.subarray(id*3,id*3+3))),u=b.map((x,i)=>x-a[i]),w=c.map((x,i)=>x-a[i]),cross=[u[1]*w[2]-u[2]*w[1],u[2]*w[0]-u[0]*w[2],u[0]*w[1]-u[1]*w[0]],area2=Math.hypot(...cross) report.triangles++;const dot=cross.reduce((sum,x,i)=>sum+x*normals[ids[0]*3+i],0)/area2 if(dot<0.9||area2<1e-10)report.bad.push({material:j.materials[p.material].name,face:f,dot,area:area2/2,positions:[a,b,c]}) } for(const obj of [status,mesh,buffer,decoder])d.destroy(obj) } console.log(JSON.stringify(report)) }