BabylonJS / BabylonJS/Babylon-Lite
glTF Exporter (v1) — export an AssetContainer to .glb
- Dominant language
- TypeScript
- Stars
- 149
- Forks
- 29
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 106
Description
## Parent
#11 — the project-level glTF Exporters tracker. This epic covers **v1 only**; materials, lights and cameras, node animations, skeletons, and extensions remain open there.
Tracking issue for the glTF exporter feature. Babylon Lite can read glTF but cannot write it, so a developer who loads a `.glb` or builds geometry procedurally has no supported way to get that content back out of the engine.
## What to build
One experimental root export, `exportGltf(container, options?)`, taking an `AssetContainer` and asynchronously returning a self-contained `.glb` binary plus a list of structured lossiness warnings.
This is **export-for-interchange, not round trip**. Lite retains no source JSON, buffer views, accessor layouts, image bytes, sampler indices, or extension objects — everything written out is re-derived. The promise is narrow and honest: a conformant third-party viewer renders the output with the same geometry, hierarchy, orientation, chirality, and face visibility that Lite renders. Anything Lite can observe but v1 cannot represent is reported as a coded warning, never silently dropped.
V1 covers `POSITION`, `NORMAL`, `TEXCOORD_0` and indices for triangle-list meshes, the full node hierarchy including mesh-less pivots, and correct handedness for **both** mesh populations in one file — glTF-loaded subtrees (which export by deleting Lite's synthetic flip root, with zero per-vertex work) and procedural meshes (which need a genuine left-to-right handed conversion with positions, normals, and winding flipped in lockstep).
**Materials are out of v1.** glTF's `materials` array is optional and a primitive with no `material` is fully conformant, so viewers fall back to their own default. Cutting materials removes all material type checking, so mixed Standard/PBR scenes need no special handling and no error path.
The feature must cost **zero bytes** for every scene that does not call it.
## Source material
All of it is already in the repo and is normative. Read it before picking up any slice.
- `features/gltf-exporter/prd.md` — user-facing framing, implementation decisions, testing decisions, out of scope
- `features/gltf-exporter/requirements.md` — 104 numbered requirements, full acceptance matrix, 13 resolved decisions
- `features/gltf-exporter/goals.md` and `goals-review.md` — origin and review
- `GUIDANCE.md` — immutable governing constraints, single source of truth
Every open question is decided. Reopening one is a scope change, not a clarification.
## Slices
Each sub-issue below is a thin vertical slice that is verifiable on its own. They stack because they share a serializer core: nodes land before geometry because a mesh needs a node to hang from, and the glTF-loaded population lands after procedural because it must coexist with it in one file.
| Slice | Blocked by | Covers |
| --- | --- | --- |
| #564 — architecture contract | none | REQ-DELIV-1, API-7/15, ERR-3/7, GEOM-8 carrier decoding |
| #565 — public seam, GLB writer, empty-container export | #564 | REQ-API-1..4/9..12/15, GLB-1..9/11, SIZE-1/3..7/9, TEST-2/10, DELIV-4 |
| #566 — node hierarchy, transforms, traversal warnings | #565 | REQ-NODE-1..4/6..10, API-5/14, HAND-1/5, GLB-9, TEST-6 |
| #567 — mesh geometry, accessors, procedural handedness | #566 | REQ-GEOM-1..16, HAND-3/7, ERR-6, TEST-14/15 |
| #568 — glTF-loaded population, flip-root removal, mixed export | #567 | REQ-HAND-1/2/4/6, NODE-5, GEOM-8/14, TEST-3/5/7 |
| #569 — lifetime preflight and error/warning contract | #568 | REQ-API-13/16/17, ERR-1..10, TEST-4/8/9 |
| #570 — focused BJS-vs-Lite visual parity fixtures | #568 | REQ-TEST-16/17/18, SIZE-2/10 |
| #571 — lab demo, catalog entry, bundle measurement | #568 | REQ-TEST-11/13, GLB-10, DELIV-2/3/6 |
Architecture (#564) blocks everything because GUIDANCE §4 (documentation-driven architecture, the One-Shot Rule) makes the doc a prerequisite for code rather than a parallel deliverable. It is also what makes the later slices independently grabbable: an agent picking one up implements against the contract instead of re-deriving it.
Once #568 lands, the final three run in parallel.
## Ground rules for every slice
These apply to all sub-issues and are not repeated in each one.
- Read `GUIDANCE.md` first. It is the single source of truth and is immutable.
- Zero module-level side effects in every new module. No `register*()` calls, no `globalThis` mutation, no module-level `new Map()`/`new Set()`/`new WeakMap()`.
- No new runtime, dev, or peer dependency — not to the package, not to the workspace root. A glTF validator package is explicitly rejected; structural conformance is proven by assertions this repository owns.
- Never change a `maxRawKB` ceiling or a `maxMad` threshold, and never regenerate an existing golden reference. A bundle-size regression in a scene that does not export is a design defect to fix, not a new baseline.
- Core modules gain no exporter-specific semantics. If a core module must change on the exporter's behalf, the change is confined to a single optional-chained engine seam whose meaning lives entirely in exporter-owned code.
- Scoped validation only: focused unit and plumbing tests, the focused exporter parity specs, filtered bundle builds, demo smoke checks, lint and typecheck. Never the all-scene suite, never a full or unfiltered parity or bundle run, never `pnpm test:perf`.
- No Babylon.js source or test code is copied. Upstream is a behavioural checklist only.
- Conventional Commit messages. A breaking change needs an explicit marker in the PR title or body.
## Known risks
- **Bundle size is the most likely way this fails, not correctness.** Ceilings span roughly 14.8 KB to 161.3 KB across 234 scenes. Verify against the smallest ceilings early rather than at the end.
- **The procedural handedness conversion is the largest single piece of v1 work**, not the GLB writing. Winding, normals, and positions all have to move in lockstep, and one export must handle two populations simultaneously.
- **Depth doubling is easy to miss and trivial to catch.** The loader attaches each mesh as a child of its source node, so without the collapse every load→export cycle adds a level. A two-cycle test catches it immediately.
- **The round-trip test is weaker than it looks.** Export → re-import passes trivially for any self-consistent error. It proves symmetry, not conformance. The structural checks and the manual external-viewer gate are what actually constrain correctness.
Contributor guide
Research direction
Read GUIDANCE.md first, then the glTF exporter PRD and requirements in features/gltf-exporter/. This is a parent issue rather than a self-contained slice, so start with the unblocked architecture contract in #564 and follow its dependencies. The completed feature must export an AssetContainer to a conformant .glb with coded lossiness warnings, while satisfying the listed requirements and focused validation checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- computer-graphics, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100