Plan agreed 2026-07-14 (Kaetemi). Two deliverables, one architecture:
.max-file conversion to glTF 2.0 with nel_* extras, as a new pipeline_max_export_gltf tool. mesh_export/mesh_utils until, for every asset class the direct route exports, max → glTF → assimp route → artifact matches max → direct route → artifact under the existing corpus comparators..lmscene from assimp, §11-lm) falls out of parity work for free.pipeline_max_export_gltf shares the same T1 parse and pipeline_max_export_common extraction as the direct exporters: glTF geometry buffers are written from the same CMeshBuild-level data export_shape consumes, tracks from the same decoded data export_anim consumes. glTF buffers are raw IEEE float32, so values survive byte-exact. Fidelity testing then measures format loss only, not decode drift. (Same move as the .lmscene intermediate, in an interoperable container.)extras, per the established convention (NeL-Specific glTF Extras): per-key scalars, %.9g forced-decimal floats, readers prefer extras and fall back to standard glTF fields. Extras (not a vendor extension) because Blender maps extras to custom properties and round-trips them — artists see and can edit NeL appdata (LOD settings, collision/SWT flags, lightmap params) as plain custom props. The skeleton pipeline already does exactly this (pipeline_max_export_skel --gltf → mesh_utils exportSkels(), bit-exact TRS via nel_t*/r*/s* keys) — it is the beachhead and the pattern to copy.nel_* keys is more robust. Pin the assimp version; keep aiProcess post-processing at zero (no tangent generation, no node collapsing, no unit scaling — every one is a fidelity leak).| Subsystem | glTF carrier | Fidelity class |
|---|---|---|
| Meshes (pos/normal/UV/vcolor), hierarchy | standard glTF buffers | lossless (float32 verbatim) |
| Materials incl. UV-scroll (waterfalls) | PBR + KHR_texture_transform + nel_* extras for NeL-specific params |
lossless via extras |
| Skinning | glTF skins (NeL's 4-weight limit fits) | lossless |
| Skeletons | done today (extras TRS) | lossless (proven) |
| Animations | sampled curves for interop plus exact NeL track data as an extras blob | dual: sampled view lossy, blob lossless; Blender edits invalidate the blob (documented, detectable) |
| PatchMesh (zones, patch props) | ligo outputs as nel_zones blob + tessellated nel_proxy viewing meshes (decision 2026-07-14; zone BUILD pipeline unchanged) |
blob lossless, proxy baked |
| Appdata (LOD, collision, SWT, lightmap params) | nel_* extras per node/material |
lossless |
Animation is the honest problem case: glTF has no Bezier/TCB keys, and the tangent semantics were hard-won (anim-export corpus). Sampled curves serve Blender authoring; the extras blob serves corpus round-tripping. Zones stay native — with the improvised ligo heightmap the zone sources are a direct-route concern regardless (see §10z-ring).
For every corpus file and every asset class: export via the direct route AND via max → glTF → mesh_export, compare artifacts with the existing field-level comparators (shape floateq tiers, skel, anim, ig). New ctest pipeline_max_gltf_corpus in the *_corpus.py shape, self-skipping without the corpus, budgeted like the other gates. This is the strongest available correctness story and it is cheap — every tier already exists.
mesh_utils today: static meshes (position/normal/UV, materials via assimp_material, scene_meta) and bit-exact skeletons. Declared TODOs in assimp_shape.cpp: multi-LOD/MRM builds, water shapes, particle systems, remanence, flare, pacs prim. No animations, no IG, no .lmscene.
pipeline_max_gltf_corpus. Multilod assembly landed same day (+418 shapes → 2649 shapes + 623 igs byte-identical; direct-only = skinned/water/remanence/flare only). Remaining staged import skip: MRM morph targets (zero corpus hits outside skinned).nel_skin_*, nel_bs_*); 3301 shapes + 623 igs byte-identical; direct-only = water/remanence/flare only. — glTF-skins interop view DONE 2026-07-14: JOINTS_0/WEIGHTS_0 + skin objects + IBMs, with the biped rig's figure-mode rest pose decoded onto the glTF nodes (was identity — biped TM controllers aren't PRS); rest-pose skinning identity verified to ~3e-7 m (ctest pipeline_max_gltf_skins); skinned characters are now poseable in any glTF DCC/viewer.nel_* track blob; anim corpus gate on the blob path, tolerance tier on the sampled path. — BLOB TIER DONE 2026-07-14 (design doc §13-gltf): asset.extras.nel_anim carries the exact CAnimation stream; 5045 anims byte-identical over the unioned 4524-file anim corpus; caught + fixed an NL3D uninitialized-range serialization bug that made even the direct exporter byte-nondeterministic. — SAMPLED CHANNELS DONE 2026-07-14: TRS tracks from the blob LINEAR-sampled at 30 fps onto the rig nodes (rest pose from the skins tier); clips play in any glTF viewer/DCC; blob stays authoritative (ctest pipeline_max_gltf_anim_channels). Non-TRS tracks (materials, morph factors) not sampled yet..lmscene emission from the assimp route — closes lightmapper producer (c) (§11-lm).max2blend)nel_shape_blob; structural extras QUEUED (bar: reader reconstructs from extras, byte-gate against direct — same proof as materials; blob drops once green). — Ligo zones DONE 2026-07-14 as nel_zones blob + tessellated nel_proxy viewing meshes: final sweep 29910 shapes + 625 igs + 5045 anims + 2640 zone files byte-identical, 0 direct-only / 0 via-only — total co-production. — ALL .max processes DONE 2026-07-15 as gated blobs (swt, pacs_prim, veget, clod, cmb, skel — same guarded-main + corpus-gate pattern as igs/anims/zones; structural extras for water/remanence/flare also landed, writer self-checked). — .lmscene from the glTF route DONE 2026-07-15 as asset.extras.nel_lmscene: the shape exporter's whole per-file flow compiled in with PMB_SHAPE_NO_MAIN runs in lightmap-scene-only mode (receiver nodes take the full build path, everything else skips), bytes byte-gated against the direct --lm-scene write on the singles tier. Every artifact class the .max pipeline produces now rides the container.nel_* customnel_* material extras
assimp_material conversion — never positional binding). Same--materials flag.mesh_utils/material_sidecar.cpp, mesh_export --materials,pipeline_max_materials_sidecar — bind-all/fallback/partial/tamper-fails/stacking/nodes array, matched to the scene's nodes bynel_* extras vocabulary the max2gltf writer emitsasset.extras). Authored by a future dedicated.nelmeta/CSceneMeta sidecar. Design details in.lmscene producer (item 4) needs designed: geometry from theKHR_lights_punctual + nel_* light extras, per-node lightmapSee also: Pipeline Max — Design and Coherency Contract (§11-lm lightmapper architecture, §9 shape corpus tiers), NeL-Specific glTF Extras, Tutorial Tech Tree.