Validate and auto-heal 3D assets (.glb/.gltf/.obj/.ply/.stl) — the stateless QA & repair layer for pipeline and AI-generated content.
Project description
3dqa — free local CLI
Validate and auto-heal 3D assets from the command line. Same deterministic, stateless core as the dashboard and API — but it runs entirely on your machine: $0, offline, no account, no upload, no API key.
Supports .glb .gltf .obj .ply .stl.
Install
pip install 3dqa # core (linter + repair, numpy + trimesh)
pip install "3dqa[all]" # + scipy fast path, decimation, UV unwrap/bake
Prefer a single file? The 3dqa.pyz bundle packs the whole engine into one
executable zipapp — python 3dqa.pyz lint model.glb (needs numpy + trimesh
in the environment; add scipy/xatlas/fast-simplification for the fast path
and full heal).
Use
# Audit one asset. Exit code is a CI gate: 0 = PASS, 1 = FAIL, 2 = error.
3dqa lint model.glb --pretty
# Audit a whole export folder (recursed), one line per asset.
3dqa lint ./exports --quiet
# Gate against target engines from a single scan.
3dqa lint ./exports --targets unreal_5,unity_mobile,three_js_web
3dqa lint ./exports --config 3dqa.config.json
# Non-destructively auto-heal — writes <name>_repaired.glb, never touches input.
3dqa heal model.glb
3dqa heal ./exports --out-dir ./healed --targets unity_mobile
# See exactly what WOULD be repaired, writing nothing.
3dqa heal ./exports --dry-run --pretty
# The closed loop: heal, RE-RENDER the healed artifact, RE-RUN the vision pass
# on it, and emit ONE certificate proving it is geometrically AND visually clean.
3dqa verify model.glb --pretty
3dqa verify ./exports --targets unity_mobile --out-dir ./healed --quiet
# Same loop, keyless and free — the mock visual backend proves the wiring at $0.
3dqa verify model.glb --vision mock --dry-run --pretty
# Compact integration schema for a pipeline hook to branch on.
3dqa verify model.glb --compact
# Half the model spend: skip the pre-heal pass when you only need the
# after-verdict and not the before/after attribution.
3dqa verify ./exports --no-baseline
What you get
lint emits a JSON Quality Certificate — a machine-readable pass/fail verdict
with a finding per check (poly budget, non-manifold edges, watertightness,
normals, UVs, floating debris, appearance) plus optional per-target compatibility.
heal runs the repair loop lint → fix → re-lint and emits a before/after report
with fixed / unfixable / introduced lists and a healing efficacy. A fix is
only reported as fixed after an independent re-lint of the exported .glb
confirms it. Nothing that can’t be safely repaired is hidden.
verify closes the loop. It heals, re-renders the healed artifact headless,
re-runs the vision pass on that render, and returns ONE certificate:
verdict PASS | FAIL | UNVERIFIED
claims geometrically_clean · visually_clean · target_compatible · closed_loop
attestation one sentence stating exactly what was proven
visual.delta resolved / persisted / introduced defect classes, before vs after
It is honest about the case that matters: if healing fixes the math but
introduces a visual defect — decimation smearing a texture, an unwrap
opening a seam — visual.delta.introduced names it and the verdict is FAIL even
though the geometry certificate passes. And if the visual pass could not
actually run (no headless GL, --vision none), the verdict is UNVERIFIED, never
a silent PASS: a certificate only claims "visually clean" when a render was
really audited.
Rendering needs nothing installed
The visual pass renders through three tiers and stops at the first that produces
frames: pyrender (offscreen GL), trimesh (pyglet), then a pure-numpy
software rasterizer. The last tier needs no GL, no display and no driver, so
verify works on a stock laptop or CI runner. The tier that ran is recorded in
visual.render.backend — a software render is a real render, but you always know
which one you got. It is roughly an order of magnitude slower than GL (measured:
7 views at 1024² in 0.76 s for 1.3k faces, 4.8 s for 82k); lower --resolution
if that matters.
--compact emits the integration schema instead of the full certificate:
{"asset_id": "...", "status": "PASSED",
"repartition_delta": {"initial_defects": ["NORMALS_INVERTED"],
"repaired_defects": ["NORMALS_INVERTED"],
"residual_visual_delta": 0.0},
"execution_time_ms": 91.4}
residual_visual_delta is the fraction of visual defect classes still present
after healing (0.0 = visually clean). It is null — never 0.00 — when the
visual pass could not run, because 0.00 there would assert a cleanliness nobody
measured.
Cost
verify runs the vision backend twice — once on the arriving asset, once on
the healed one — because the before pass is what lets the certificate say a
defect was introduced by healing rather than merely present. Two levers:
| Setting | Model calls | Pixels sent |
|---|---|---|
--resolution 1024 + baseline |
2 | 14.7 MPx |
| default (512, baseline) | 2 | 3.7 MPx |
--no-baseline at 512 |
1 | 1.8 MPx |
--no-baseline buys that saving with attribution, and says so: delta.baseline
becomes "skipped" and every comparative field (resolved, persisted,
introduced, regression) is null, not false. The healed asset is
still fully audited — you just can no longer tell whether a defect it shows was
caused by the heal.
Guarantees
- Non-destructive: your input file is never modified.
- Deterministic: same input → same certificate and same repaired bytes.
- Permissive-licensed: MIT/BSD/Apache-2.0 dependencies only (CI-gated).
- Stateless & offline: no network, no telemetry, nothing leaves your box.
Exit codes
| Code | Meaning |
|---|---|
| 0 | every asset PASSes (and passes all targets) |
| 1 | at least one asset FAILs |
| 2 | a load/engine error |
For verify, an UNVERIFIED certificate gates like a FAIL (exit 1). Pass
--allow-unverified to accept a geometry-only pass on a box with no headless GL.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file 3dqa-1.0.0.tar.gz.
File metadata
- Download URL: 3dqa-1.0.0.tar.gz
- Upload date:
- Size: 50.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7905ac7512f86896b3e8a31a6be9c2514e9d3c2698df505e711ddbdd2de3a6d
|
|
| MD5 |
e8c7e52c03e9be94eceec612402cc71c
|
|
| BLAKE2b-256 |
a78a59b1414b8d145d1a16f3cc4114d47f1251c2419ba3092da6e5e9804c5d5c
|
File details
Details for the file 3dqa-1.0.0-py3-none-any.whl.
File metadata
- Download URL: 3dqa-1.0.0-py3-none-any.whl
- Upload date:
- Size: 54.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6db905e9a7b9dfb8dcaabeb2001467f09201e76c21a41ad23ea3511f86df40c
|
|
| MD5 |
48b4b191069bd3cbe6bd332b740a9a37
|
|
| BLAKE2b-256 |
d3398cd20bd227f1af1cbc0e63bc909fe9207d04d9f6bb580882068abc0894d2
|