// usage: NODE_PATH=~/tools-gltf/node_modules node rotate_y180.cjs in.glb out.glb // Rotates the model 180 deg about Y by baking it into POSITION/NORMAL/TANGENT (x,z -> -x,-z) and node translations. const { NodeIO } = require('@gltf-transform/core'); const { ALL_EXTENSIONS } = require('@gltf-transform/extensions'); (async () => { const io = new NodeIO().registerExtensions(ALL_EXTENSIONS); const doc = await io.read(process.argv[2]); const seen = new Set(); for (const mesh of doc.getRoot().listMeshes()) for (const prim of mesh.listPrimitives()) { for (const sem of ['POSITION', 'NORMAL', 'TANGENT']) { const acc = prim.getAttribute(sem); if (!acc || seen.has(acc)) continue; seen.add(acc); const v = [0, 0, 0, 0]; for (let i = 0; i < acc.getCount(); i++) { acc.getElement(i, v); v[0] = -v[0]; v[2] = -v[2]; acc.setElement(i, v); } } for (const t of prim.listTargets()) for (const sem of ['POSITION', 'NORMAL', 'TANGENT']) { const acc = t.getAttribute(sem); if (!acc || seen.has(acc)) continue; seen.add(acc); const v = [0, 0, 0, 0]; for (let i = 0; i < acc.getCount(); i++) { acc.getElement(i, v); v[0] = -v[0]; v[2] = -v[2]; acc.setElement(i, v); } } } let nodeNote = []; for (const n of doc.getRoot().listNodes()) { const t = n.getTranslation(), r = n.getRotation(), s = n.getScale(); if (t.some(x => x) || r[3] !== 1 || s.some(x => x !== 1)) nodeNote.push([n.getName(), t, r, s]); } console.error('non-identity node transforms:', JSON.stringify(nodeNote)); await io.write(process.argv[3], doc); })();