meshcheck vs. gltf-validator

The Khronos glTF validator answers "is this a valid glTF 2.0 file?" meshcheck answers "is this asset usable in a real pipeline?" Two different questions.

The Khronos glTF Validator is the reference tool for one precise question: is this a spec-conformant glTF 2.0 file? It checks the JSON against the schema, verifies accessor bounds and buffer views, flags undefined references and disallowed values. If it passes, your file is legal glTF that any conformant loader will parse.

meshcheck answers a different question: is this asset usable in the pipeline you’re about to drop it into? Triangle budgets, texture memory, UV overlap, transform scale, watertight geometry, whether it renders to visible pixels at all. A file can be flawless glTF and still be unshippable — the two properties are unrelated.

We run the validator; we don’t replace it

meshcheck does not reimplement spec conformance. Our SPEC-001 check shells out to the Khronos gltf-validator binary and maps its errors and warnings into the report verbatim. If the validator says your file is malformed, SPEC-001 fails and we surface exactly what it found. We build on Khronos, not against it — the comparison here is about coverage, not correctness of the spec check itself.

Where the tools diverge is everything past conformance. The validator is done once the file is legal glTF. meshcheck’s other eight check families start there.

A file that passes spec and fails the pipeline

Consider Duck__flip_faces.glb — the Khronos sample Duck with its triangle winding order reversed on a portion of the mesh. Flipping winding is a modeling defect, not a spec violation: the file is still structurally valid glTF 2.0. Here is the Khronos validator’s own verdict on it:

{
  "issues": {
    "numErrors": 0,
    "numWarnings": 0,
    "numInfos": 0,
    "numHints": 3,
    "messages": [
      {
        "code": "BUFFER_VIEW_TARGET_MISSING",
        "message": "bufferView.target should be set for vertex or index data.",
        "severity": 3,
        "pointer": "/meshes/0/primitives/0/attributes/POSITION"
      }
    ]
  }
}

Zero errors, zero warnings. Three severity-3 hints about an unset bufferView.target — advisory, not a failure. By the validator’s measure this file is fine, and meshcheck agrees: SPEC-001 passes.

Now the same file through meshcheck on the pc profile:

{
  "verdict": "fail",
  "summary": { "errors": 1, "warnings": 3, "info": 13, "skipped": 4 },
  "checks": [
    {
      "id": "GEO-003",
      "name": "Inconsistent winding / flipped normals",
      "category": "geometry",
      "severity": "error",
      "status": "fail",
      "measured": { "face_count": 4212, "flipped_face_count": 422, "flipped_pct": 10.018993 },
      "threshold": { "flipped_pct": 5.0 },
      "message": "10.018993% of faces disagree with their shell's winding (> 5%)"
    }
  ]
}

GEO-003 measures winding agreement between adjacent faces per shell. 422 of 4,212 faces (10.018993%) disagree with their neighbours — past the 5% default threshold, so the check fails and the report rolls up to fail. In an engine that culls back-faces those triangles render inside-out or vanish. The validator has no opinion on this because winding order is not a spec concern. meshcheck does, because a real renderer does.

Reproduce it yourself against the corpus fixture:

# meshcheck's own CLI (crates/meshcheck-corpus)
meshcheck-corpus check corpus/broken/Duck__flip_faces.glb --profile pc

# or the hosted API — same report, SPEC-001 runs the bundled validator
curl -X POST https://api.meshcheck.dev/v1/validate \
  -H "X-Api-Key: mc_live_..." \
  -F file=@Duck__flip_faces.glb \
  -F profile=pc

Coverage, side by side

The validator’s domain is the SPEC column. meshcheck runs that same check and adds eight more families.

Family meshcheck checks Khronos gltf-validator
SPEC — spec conformance SPEC-001 spec conformance (runs the validator), SPEC-003 references resolvable, SPEC-004 file size vs budget This is the validator’s domain — we wrap it
GEO — geometry integrity GEO-002 open boundaries (holes), GEO-003 flipped winding, GEO-007 floating debris Not applicable
XFM — transforms & scale XFM-001 real-world scale sanity, XFM-002 pivot placement Not applicable
UV — texture coordinates UV-002 UV overlap, UV-004 texel stretch, UV-005 island padding Not applicable
MAT — materials & textures MAT-001 texture resolution vs budget, MAT-002 estimated GPU texture memory Not applicable
PERF — performance budget PERF-001 triangle count, PERF-003 draw-call estimate Not applicable
RND — render checks RND-001 renders without error, RND-002 visible pixels Not applicable
TOP — topology & surface structure TOP-001 quad reconstructibility, TOP-004 hard-edge share Not applicable
VIS — vision (/inspect) VIS-001 prompt match, VIS-002 texture-artifact scan Not applicable

“Not applicable” is not a knock on the validator — it is a tool for spec conformance and it does that job well. It just does not measure triangle budgets, UV layout, transform scale, topology quality, or whether the thing renders, because those are out of scope for a conformance checker. That gap is the reason meshcheck exists.

The one direct peer that reaches past conformance into surface structure is the Khronos glTF Asset Auditor, a commerce-profile library (not a hosted API) that ships a beveled-edge — hard-edge — check the base validator lacks. meshcheck’s TOP-004 uses the same 90° threshold and edge convention as the Khronos glTF Asset Auditor’s hard-edge check; meshcheck additionally reports a length-weighted share so a million tiny edges don’t drown one long hard seam. It also welds coincident positions by a configurable tolerance (weld_tolerance) so seams a viewer reads as continuous surface aren’t miscounted as hard edges.

When to reach for which

  • Use gltf-validator when you need to know a file is legal glTF 2.0 — writing an exporter, debugging a loader, or gating on spec conformance alone. It is the authority, and it is free and offline.
  • Use meshcheck when a spec-valid file still has to survive a real pipeline: an AI generator’s output, a marketplace ingest gate, an agent deciding whether to accept or regenerate an asset. You get the spec check and the budget, geometry, UV, transform, topology, and render measurements, as one machine-readable report.

Every meshcheck check is a deterministic measurement — same file, same profile, same report, byte for byte. The one exception is /inspect (the VIS family), which asks a vision model a semantic question and is labelled deterministic: false. See the Checks reference for the full catalog.