code wiki / _hdl_build / nx_gltf_export_gate.nx
nx_gltf_export_gate.nx
buildroot/runtime/_hdl_build/nx_gltf_export_gate.nx
about
nx_gltf_export_gate.nx -- prove the glTF/.glb export (roadmap R7, VRM foundation): mesh the bestiary,
write a binary glTF per variant, and validate the container: magic "glTF" + version 2 + total-length ==
filesize + a JSON chunk (POSITION/NORMAL accessors) + a BIN chunk big enough for the geometry the JSON
declares. This is a REAL interchange format (three.js/Unity/Godot/Blender import it), the step up from
STL/OBJ toward riggable VRM avatars.
MIGRATED OFF D001 2026-08-26. This gate used to hand-roll a `fails` tally and print its own
"GLTF-EXPORT-GATE 4/4 verdict=GREEN" line, so /api/promote refused it: nothing outside could read its
outcome, nx_gate_green could not judge it, and it recorded no harness.jrnl frame, leaving flake and
erosion invisible for it. It now inherits nx_gate_verdict -- per-tooth gv_check, so declared == executed
by construction, and the verdict is carried in the EXIT CODE where /api/gate_run reads it.
The migration surfaced two vacuity defects, fixed here rather than carried across:
- the BIN-size check compared got_bin >= nv*24 + nf*12, which PASSES when the mesher returns nv=0 and
nf=0. A tooth that passes on the empty set is not a tooth. The vertex and triangle counts are now
declared SUBJECTS, so an empty mesh ends SKIP (no evidence) instead of scoring a silent GREEN.
- the six-variant export loop printed a line per file and asserted NOTHING about any of them, so a
write_glb returning 0 for every variant was invisible. It is a tooth now.
Two neg-controls drive the header detector and the JSON scanner with crafted inputs, because a checker
that answers the same way for everything scores 100% and proves nothing.
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_sdfrender.nxnx_meshgen.nxnx_assetvariant.nxnx_gltf_export.nxnx_gate_verdict.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 30 | const GX_MAGIC_GLTF: i64 = 0x46546C67 // "glTF", little-endian |
| 31 | const GX_CHUNK_JSON: i64 = 0x4E4F534A // "JSON" |
| 32 | const GX_CHUNK_BIN: i64 = 0x004E4942 // "BIN\0" |
| 33 | const GX_VERSION: i64 = 2 // glTF 2.0 |
| 34 | const GX_HDR_BYTES: i64 = 12 // magic + version + total length |
| 35 | const GX_CHUNKHDR: i64 = 8 // chunk length + chunk type |
| 36 | const GX_VEC3_F32: i64 = 12 // three float32 per vertex attribute |
| 37 | const GX_ATTRS: i64 = 2 // POSITION and NORMAL, both vec3 |
| 38 | const GX_IDX_BYTES: i64 = 4 // uint32 index |
| 39 | const GX_TRI_IDX: i64 = 3 // indices per triangle |
| 43 | const GX_NVARIANT: i64 = 6 |
| 44 | const GX_VT_FIELDS: i64 = 5 |
| 45 | const GX_SCRATCH: i64 = 16 |
functions
| 47 | func rd32(b: *u8, o: i64) -> i64 { return (b[o]&0xff) | ((b[o+1]&0xff)<<8) | ((b[o+2]&0xff)<<16) | ((b[o+3]&0xff)<<24) } |
| 49 | func has_str(b: *u8, n: i64, s: *u8) -> i64 called by 1: main |
| 67 | func glb_magic_rejects(b: *u8) -> i64 |
| 72 | func main() -> i64 |