You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: MC3_FORMAT.md
+10-13Lines changed: 10 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -217,17 +217,14 @@ are covered by this same skip-set (fixed in STAB-0091 — the writer's
217
217
SVG-texture loop was previously missing this check and silently
218
218
re-inlined included SVG textures on every save).
219
219
220
-
**Known limitation — `<embeds>` is not include-aware (STAB-0092):**
221
-
unlike definitions/materials/textures, an included file's own `<embeds>`
222
-
section is **never merged** — only the main document's own top-level
223
-
`<embeds>` is parsed. If a `<definition>` merged from an included file
224
-
references `<mesh src="embed:xyz"/>` where `xyz` is declared in *that
225
-
same included file's*`<embeds>` section (rather than the main
226
-
document's), the reference silently fails to resolve. This is a narrow,
227
-
accepted limitation (embedding glTF data specifically inside a
228
-
shared/included asset library), not implemented — would need an
229
-
`includedEmbeds` tracking set plus an `<embeds>` merge block in
230
-
`mergeInclude()`, mirroring the existing definitions-merge pattern.
220
+
**`<embeds>` are include-aware (STAB-0092):** embeds declared by an included
221
+
file are merged alongside its definitions. Their external `src` paths are
222
+
rebased from the included file's directory to the main document's directory,
223
+
and `includedEmbeds` ensures an unchanged included embed is not inlined when
224
+
the main document is saved. A local edit makes it local content, matching the
225
+
existing definitions/materials/textures ownership rule. Duplicate ids use the
226
+
same documented last-write-wins rule as other included resources and emit a
227
+
named warning.
231
228
232
229
---
233
230
@@ -974,8 +971,8 @@ curve in a short time window could in principle be under-sampled.
974
971
| Per-object `metadata` (`<metadata><property name="..." value="..."/></metadata>`) | ✅ (`AUD-029`) — serialized into `node.extras.metadata`, alongside the pre-existing `tags`/`collision` extras |
975
972
|`--stats` "Warnings" count | ✅ truthful (`AUD-026`) — every `"Warning:"` print site in `GltfExporter.cpp` increments the shared counter (verified by grep, not spot-checked); previously several paths (unknown material, SVG-slot warnings, ambient-light drop, duplicate node name, image-format detection, missing embed texture, action warnings) printed a warning without counting it |
976
973
|`TANGENT` accessor (for `normal_texture`-mapped meshes) | ✅ (STAB-0664) — computed per-vertex (standard per-triangle-then-averaged-then-Gram-Schmidt-orthogonalized algorithm, not a full MikkTSpace port), only when a mesh has both `NORMAL`/`TEXCOORD_0` and its material sets `normal_texture`; meshes without a normal map get no `TANGENT` (not needed) |
977
-
| SVG textures (N1, `<textures><texture>` with an SVG source) |❌ — `svgTextures` is a separate map in `Mc3Document` that `GltfExporter` never rasterizes (rasterization isn't implemented anywhere yet, editor viewport included); since STAB-0440, a material referencing an SVG texture prints a warning naming the material/slot/texture id instead of dropping it silently, but the texture itself is still omitted from the export|
978
-
| Embedded glTF (N2, `<mesh src="embed:id"/>`) |❌ — treated as a literal OBJ file path, which fails to parse; the export doesn't crash but continues with **no mesh on that node** (`Warning: OBJ load failed (...)` on stderr, `stats.warnings` incremented). See STAB-0194 for the tracked automated test of this exact behavior|
974
+
| SVG textures (N1, `<textures><texture>` with an SVG source) |✅ — external and inline SVG are rasterized into bounded PNG pixels for both glTF export and the live viewport. See the Textures section for the 2048px safety cap and cache/sampler details.|
975
+
| Embedded GLB (N2, `<mesh src="embed:id"/>`) |✅ (`SYS-W14-05`) — external self-contained `.glb` files and inline base64 GLB are decoded, their default-scene node transforms are flattened, and triangle geometry reaches both `mc3togltf` and the live viewport. The MC3 object's material remains authoritative: source GLB materials/textures, skins, morph targets, animations, non-triangle primitives, loose `.gltf` companion-file assets, singular transforms, and geometry beyond 64 MiB/300,000 triangles are deliberately rejected with a named warning rather than partially or unsafely imported.|
979
976
| Scripts, Sounds, Music, Triggers, Scene States, Meta (N3-N7) | ❌ (no glTF equivalent — these are MCB/XML-only data, round-tripped but not translated to any glTF concept; see [Scripts (N3)](#scripts-n3) etc. above) |
No actionable follow-up audit task remains: `AUD-089` through `AUD-092` are
859
-
complete, while Android (`AUD-042`) is environment/owner deferred.
860
+
complete. Android (`AUD-042`) remains environment-blocked, but is now an
861
+
explicit supported-platform goal rather than a possible rejection path.
860
862
`SYS-W3-01` has 12 completed subsystem phases and an active Phase 13 for
861
863
application/UI ownership. The authorized Camera Bookmarks, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Status Bar, Walk Mode, View
862
864
Bloom/SSAO, Help, Add/CSG, Edit-history, Edit-clipboard, and Edit-object-actions
@@ -1135,15 +1137,23 @@ light lookup. Splitting any one without a new, broader ownership design would
1135
1137
create a god-context or relocate application state into UI. The next work should
1136
1138
be a separately scoped subsystem, not another mechanical `Overlays.cpp` slice.
1137
1139
1140
+
### Current authorized work
1141
+
1142
+
-**SYS-W14-05:** embedded external/inline GLB support is implemented and
1143
+
under final validation. It safely accepts self-contained triangle GLBs only,
1144
+
with a 64 MiB / 300,000-triangle ceiling; MC3 materials remain authoritative.
1145
+
-**SYS-W14-06:** improved CSG normals/UVs/materials is the next authorized
1146
+
task once the `SYS-W14-05` validation/commit is complete.
1147
+
1138
1148
### Tracked work that is not implementation-ready
1139
1149
1140
-
-**SYS-W8-05:** alternate-backend runtime qualification needs the appropriate
1141
-
backend environment and owner coordination.
1142
-
-**AUD-042:** Android remains deferred until an Android-capable toolchain and
Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -279,11 +279,11 @@ reference.
279
279
- Headless screenshot: a `.png` path writes a real PNG (`stbi_write_png`); every other extension (e.g. `.ppm`) writes raw PPM (P6) bytes regardless of what the extension actually says
280
280
- MCB: as of `SYS-W14-25` (2026-07-20), compression is implemented — `MeshCraft::Mcb::saveToBinary`/`saveToFile` take an opt-in `compress` parameter (default `false`, unchanged output) that zlib-deflates the document payload; `loadFromBinary`/`loadFromFile` transparently detect and decompress it. Requires this build to have been compiled with zlib available (system package, optional — see `THIRD_PARTY.md`); degrades to a clear "requires zlib"/"compiled without zlib support" error rather than misparsing a compressed file or silently ignoring the `compress` request. No editor UI toggle — see `MC3_FORMAT.md`'s MCB section for the full header layout
281
281
- CSG export to glTF: evaluated by Manifold (union/difference/intersection). Unsupported child types inside a CSG node (Plane, Disk, Grid, Mesh, Extrude) cause the export to fail with a clear error in default mode. Pass `--allow-approximate-csg` (CLI) or enable "Allow approximate CSG export" (editor) to bypass Manifold and export children separately (debug fallback, geometrically incorrect). CSG result mesh has flat normals; child materials are not preserved (CSG root material is used)
282
-
- No full end-to-end UI-interaction-simulation harness (nothing scripts a sequence of real user clicks/drags through the live application window) — but this understates real coverage: 34`render`-labeled tests include real pixel-sampling checks against headless `--screenshot` output (fog, light/camera gizmos, look-through-camera, background/skybox textures, LOD, CSG preview cache, etc. — see `TESTING.md`), and a handful of `unit`-labeled tests drive real ImGui widget frames (drag/click gestures, undo-snapshot timing, scene-hierarchy drag-drop) directly against production widget code with no GL context needed
282
+
- No full end-to-end UI-interaction-simulation harness (nothing scripts a sequence of real user clicks/drags through the live application window) — but this understates real coverage: 36`render`-labeled tests include real pixel-sampling checks against headless `--screenshot` output (fog, light/camera gizmos, look-through-camera, embedded GLB, background/skybox textures, LOD, CSG preview cache, etc. — see `TESTING.md`), and a handful of `unit`-labeled tests drive real ImGui widget frames (drag/click gestures, undo-snapshot timing, scene-hierarchy drag-drop) directly against production widget code with no GL context needed
283
283
- AI Assistant: not available on Emscripten or Android builds (`cpp-httplib`/OpenSSL are only fetched/linked when `NOT EMSCRIPTEN AND NOT ANDROID`). The API key is **never persisted to disk** — it lives only in the in-memory UI text buffer for the session, pre-filled from the `ANTHROPIC_API_KEY` environment variable if set, and is not part of the saved preferences file. The **Model** field (default `claude-sonnet-5`) and **Max tokens** (4096–64000, default 32000) are both user-editable in the AI panel and control the outgoing API request directly — there is no fixed/hardcoded model. **Scope** selects what's sent as context: "Full scene" serializes the entire document; "Selection only" serializes just the currently-selected objects (plus all materials/definitions, so references still resolve) and is disabled when nothing is selected. Applying a response that would drastically shrink the scene's object count (more than half, on a scene of 10+ objects) requires an explicit second "Confirm Replace" click rather than applying immediately. Response validation is structural, not semantic: it checks the XML is well-formed, has a `<mc3>` root, isn't empty, and conforms to `mc3.xsd` (element order, attribute types/patterns, ID/IDREF cross-references) — but `mc3.xsd` has no numeric range constraints (no `minInclusive`/`minExclusive` anywhere), so a geometrically nonsensical response (e.g. negative `size`/`radius`) passes validation and applies to the scene as-is
284
284
- Model Registry: no thumbnail column (design placeholder only, never implemented — see `m1m2m3.md`); requires the system SQLite3 library on desktop builds (stubbed out, feature disabled, on Emscripten/Android); no sync between multiple registry database files — it's a single local SQLite file at `~/.meshcraft/modelregistry.sqlite3`, not backed up or shared automatically
285
285
- SVG textures (`<texture type="svg">`): external `.svg` files and inline CDATA markup are rasterized by NanoSVG (maximum output dimension 2048px) for both the live editor viewport and glTF export. The live cache has compact content-hash keys and automatically re-rasterizes an external SVG when its file changes; malformed input is warned once per unchanged source. `wrap_u`, `wrap_v`, and `filter` round-trip and affect both the viewport sampler and glTF sampler; `mip_maps` affects glTF, while the live CNA texture remains level-zero because its available API cannot generate a mip chain. `.gltf` exports write a generated PNG beside the document; `.glb` embeds it. Unsupported or malformed SVG is skipped with a named warning rather than dropping the material silently.
286
-
- Embedded glTF (`<mesh src="embed:id"/>`): parsed and serialized, but `GltfExporter` treats `embed:id` as a literal OBJ file path, which fails to parse — the export doesn't crash, but the node exports with no mesh (see `MC3_FORMAT.md`'s export support matrix)
286
+
- Embedded GLB (`<mesh src="embed:id"/>`): external self-contained `.glb` files and inline base64 GLB both resolve in `mc3togltf` and in the live viewport. The asset's default-scene transforms are flattened into the Mesh object's local geometry; MC3 retains authority over the material. The loader rejects loose companion-file `.gltf`, non-triangle primitives, animation/skin/morph data, malformed paths/data, and assets over the documented 64 MiB/300,000-triangle limits with a named warning instead of importing an unsafe partial asset.
287
287
- N3–N7 scene data (scripts, sounds, music, triggers, scene states, meta): fully round-tripped (XML/MCB/XSD) and editable. As of `SYS-W14-18`/`SYS-W14-19`/`SYS-W14-20` (2026-07-20), scripts (`type="lua"`) actually run — a real sandboxed Lua interpreter (`LuaScriptRunner`) with `def:place()`/`place_at()`/`has_socket()` (compose-time socket placement) and `scene:find()` (read/write any object's position/rotation/scale/visible/material) — triggers actually fire (an explicit "Fire" action executes `play-action`/`play-sound`/`play-music`/`run-script` steps for real), and scene states actually apply (an explicit "Apply State" action writes a state's overrides onto the matching live objects). What's still data-model-only: there is no in-scene EVENT system (collision/click/timer) that fires a trigger or switches a scene state automatically — only the explicit manual actions above do. See `MC3_FORMAT.md`'s per-section status notes and `plan.md`'s `SYS-W14-##` rows.
288
288
-`<library>`/`<imports>` (R101/R110, `Mc3ImportResolver`): as of `SYS-W14-21` (2026-07-20), the editor actually resolves `<imports>` against the loaded document's own directory and merges the imported libraries' definitions into `document_.definitions`, so `<instance definition="namespace:id">` referencing an imported library's definition renders instead of silently resolving to nothing — this now runs automatically right after every load (initial launch, Open dialog, Open Recent, autosave recovery) plus an explicit "Resolve Imports" button in the Imports tab for re-resolving after editing the rows without a full reload. A resolution failure (missing library file, content-hash mismatch, an import cycle/depth-limit) doesn't fail the whole document load — it's reported via the status bar and the affected imports simply stay unresolved.
289
289
-`<texture mip_maps="...">`: as of `SYS-W14-22` (2026-07-20), honored by `mc3togltf` — `false` makes the exporter emit a plain (non-mipmap) glTF sampler `minFilter` instead of unconditionally requesting a mipmapped one. **Not honored by the live editor viewport** — CNA's `Texture2D` asset-loading path has no mipmap-generation option (confirmed in CNA's own OpenGL backend: it explicitly does not generate mipmaps by default for the filter that path uses), and closing that would need a CNA-side API change, out of scope per this repo's CNA boundary. See `MC3_FORMAT.md`'s Textures section for the full writeup.
0 commit comments