donmccurdy / glTF-Transform

glTF 2.0 SDK for JavaScript and TypeScript, on Web and Node.js.
https://gltf-transform.dev
MIT License
1.37k stars 148 forks source link

Out of bounds index accessor after `sparse` and `draco` compression #1496

Open marwie opened 1 week ago

marwie commented 1 week ago

Describe the bug Processing the attached GLB first with sparse and then with draco produces a mesh that references a joint (accessor) that doesnt exist

To Reproduce Steps to reproduce the behavior:

  1. Download attached files. soc.orig.glb is the original file
  2. Run gltf-transform sparse soc.orig.glb soc.orig.sparse.glb anf then gltf-transform draco soc.orig.sparse.glb soc.orig.sparse.draco.glb
  3. To make the error visible in the validator run gltf-transform copy soc.orig.sparse.draco.glb soc.orig.sparse.draco.copy.glb
  4. See error Joints accessor element at index 4 (component index 0) has value 12560 that is greater than the maximum joint index (1) set by skin 2

Step 3 and 4 can be omitted and soc.orig.sparse.draco.glb can directly opened, see mesh at index 4 has a JOINT_0 : 111 accessor (while draco does not.

Expected behavior No error / out of bounds index

Versions:

Additional context

soc.zip

image

donmccurdy commented 1 week ago

Seeing other suspicious outputs, not present in the original:

            {
                "code": "TYPE_MISMATCH",
                "message": "Type mismatch. Property value 46.5 is not a 'integer'.",
                "severity": 0,
                "pointer": "/accessors/51/count"
            },
            {
                "code": "MESH_PRIMITIVE_UNEQUAL_ACCESSOR_COUNT",
                "message": "All accessors of the same primitive must have the same count.",
                "severity": 0,
                "pointer": "/meshes/5/primitives/0/attributes/JOINTS_0"
            },
donmccurdy commented 1 week ago

Reproduction:

import { NodeIO } from '@gltf-transform/core';
import { KHRDracoMeshCompression, KHRONOS_EXTENSIONS } from '@gltf-transform/extensions';
import { sparse, draco } from '@gltf-transform/functions';
import draco3d from 'draco3dgltf';
import validator from 'gltf-validator';

const io = new NodeIO()
    .registerExtensions(KHRONOS_EXTENSIONS)
    .registerDependencies({
        'draco3d.decoder': await draco3d.createDecoderModule(),
        'draco3d.encoder': await draco3d.createEncoderModule(),
    });

const document = await io.read('./soc.orig.glb');
await document.transform(sparse(), draco());
const document2 = await io.readBinary(await io.writeBinary(document));
document2.createExtension(KHRDracoMeshCompression).dispose();
const glb = await io.writeBinary(document2);

const report = await validator.validateBytes(glb, {
    maxIssues: 1000,
    ignoredIssues: [
        'ACCESSOR_WEIGHTS_NON_NORMALIZED',
        'IMAGE_MIME_TYPE_INVALID',
        'IMAGE_UNRECOGNIZED_FORMAT',
        'NODE_SKINNED_MESH_LOCAL_TRANSFORMS',
        'NODE_SKINNED_MESH_NON_ROOT',
        'UNUSED_MESH_TANGENT',
        'UNUSED_OBJECT',
        'VALUE_NOT_IN_LIST',
    ]
});

console.log(JSON.stringify(report.issues, null, 2));
{
  "numErrors": 2,
  "numWarnings": 0,
  "numInfos": 1,
  "numHints": 0,
  "messages": [
    {
      "code": "UNSUPPORTED_EXTENSION",
      "message": "Cannot validate an extension as it is not supported by the validator: 'KHR_texture_basisu'.",
      "severity": 2,
      "pointer": "/extensionsUsed/2"
    },
    {
      "code": "TYPE_MISMATCH",
      "message": "Type mismatch. Property value 46.5 is not a 'integer'.",
      "severity": 0,
      "pointer": "/accessors/51/count"
    },
    {
      "code": "MESH_PRIMITIVE_UNEQUAL_ACCESSOR_COUNT",
      "message": "All accessors of the same primitive must have the same count.",
      "severity": 0,
      "pointer": "/meshes/5/primitives/0/attributes/JOINTS_0"
    }
  ],
  "truncated": false
}

Slightly different result, but I suspect the cause is the same. Still investigating.