How to Validate a Reconstructed 3D Mesh Before It Enters Your Build Pipeline
A generated GLB is not game-ready just because it previews cleanly. Your engine's glTF loader is written for correctly-formed assets; it will silently misbehave or crash on malformed data. The cheapest place to catch that is CI, before the asset is ever committed. This is the contract I run on every reconstructed mesh that comes out of the 2D to 3D workspace.
The asset contract
Treat a reconstructed mesh as untrusted input and pin the properties your runtime actually depends on:
-
File format: valid glTF 2.0 binary (
.glb), with textures embedded. - Structure: a known node/mesh/primitive count range, not an unbounded blob.
- Geometry: no non-manifold edges where the target pipeline needs watertight volumes; no loose or duplicate vertices per primitive.
- UVs: inside the 0–1 range, no inverted or overlapping islands for baked materials.
- Scale: a declared real-world size, so the asset is not 1000× too large.
- Materials: PBR maps present and correctly referenced.
A runnable validation gate
The Khronos gltf_validator exits non-zero on any spec error, which is exactly what a CI gate needs:
#!/usr/bin/env bash
set -euo pipefail
ASSET="${1:?usage: validate-glb <file.glb>}"
# 1. Spec validation: fails the build on any Error.
gltf_validator --format json "$ASSET" > "$ASSET.report.json"
# 2. Fail on errors, surface warnings without failing.
python3 - "$ASSET.report.json" <<'PY'
import json, sys
report = json.load(open(sys.argv[1]))
issues = report.get("issues", {})
errors = issues.get("numErrors", 0)
warnings = issues.get("numWarnings", 0)
print(f"errors={errors} warnings={warnings}")
if errors:
sys.exit(f"glTF spec errors: {errors}")
PY
Wire that into the asset build so a bad export fails where the source is obvious:
assets/%.glb: blender/%.blend
blender -b $< --python scripts/export_gltf.py -- $@
gltf_validator $@ || (echo "Validation failed for $@"; exit 1)
Assert the properties the validator does not cover
Spec validation does not know your project's budget. Add a second assertion pass over the report's asset stats:
import json
def assert_mesh_contract(report_path, *, max_triangles=200_000, max_materials=12):
report = json.load(open(report_path))
stats = report["info"]["totalTriangleCount"]
materials = report["info"].get("materialCount", 0)
assert stats <= max_triangles, f"triangle budget exceeded: {stats}"
assert materials <= max_materials, f"material slots exceeded: {materials}"
return {"triangles": stats, "materials": materials}
If your pipeline needs watertight geometry, the Khronos @khronosgroup/gltf-asset-auditor adds the slow but decisive requireManifoldEdges check, plus UV gutter width and UV overlap tests — the same checks the 3D Commerce guidelines define for publishing.
Review the reconstruction itself
Spec-valid is not the same as correct. After the automated gate passes, inspect two things by eye:
- Front vs. reference. Confirm the visible surfaces match the input artwork or photo.
- Hidden sides. Orbit the back and underside. Reconstruction infers these; confirm they are coherent rather than hollow.
Determinism and CI hygiene
- Pin the validator version so a rule change does not silently break every asset.
- Emit
--format jsonand archive the report as a build artifact; the diff between two reports localises a regression fast. - Validate on export, not on import. By import time the source is gone.
- Keep large binaries out of the repo; validate in the build step that produces them.
Limitations
Reconstruction is visual, not metrological. The validator tells you the file is well-formed and within budget; it cannot tell you the mesh is dimensionally accurate. Triangle count says nothing about silhouette quality, and a valid GLB can still have a wrong back. For tolerances, keep a CAD or scan workflow, and treat the reconstructed mesh as a validated block-out.
The generation step
On the 2D to 3D workspace the current route runs Pixal3D at 105 credits per generation with a selectable 1024/1536 resolution, a public watermarked default, and GLB output with textures baked in. Reconstruct, run the gate above, and only then merge into your build.


Top comments (0)