Skip to content

Latest commit

 

History

1,002 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MeshCraft

A C++23 3D scene editor for the .mc3.xml format — a lightweight XML-based scene description used by the OpenEggbert project.

MeshCraft screenshot

Description

MeshCraft lets you build and edit 3D scenes from primitive shapes, groups, materials, CSG boolean operations, and external OBJ meshes. Scenes are saved as .mc3.xml files and can be exported to glTF/GLB for use in games and real-time applications.

The MC3 Format

MC3 (MeshCraft 3D) is an XML-based source format (.mc3.xml). It describes scenes as editable constructive objects rather than raw triangle meshes.

Key capabilities:

  • primitive shapes: box, sphere, cylinder, cone, plane, torus, capsule, disk, grid, icosphere
  • hierarchical groups and transforms (position, rotation, scale, pivot)
  • materials: PBR (base color, roughness, metallic, emissive, normal/occlusion textures)
  • CSG operations: union, difference, intersection (via Manifold)
  • extrude along path (line, arc, helix, polyline, bezier)
  • animation channels: position, rotation, scale, visibility, material color, deform
  • reusable definitions (prefabs / instances)
  • layers, tags, collision hints
  • fog and environment settings
  • export: glTF/GLB via mc3togltf; binary MCB via mc3tomcb

See MC3_FORMAT.md for the full specification and docs/CAPABILITY_MATRIX.md for the current format/editor/exporter/platform capability boundary.

Architecture

Component Description
mc3/ Core library — Mc3Document data model, XML parser/writer, no graphics dependency
mcb/ MCB binary format read/write (fast runtime loading)
mc3tomcb/ CLI: convert .mc3.xml.mcb
mc3togltf/ CLI: export .mc3.xml.gltf / .glb
MeshCraft Editor application — Dear ImGui UI, orbit camera, gizmos, CSG, timeline
../cna/ XNA-like C++ runtime (SDL3 + OpenGL ES 3, sibling repo)

Building

Prerequisites

  • CMake 3.21 or newer
  • C++23-capable compiler (GCC 13+, Clang 16+, MSVC 2022+)
  • Python 3 + lxml (for XSD validation tests)
  • Sibling repositories checked out next to mesh-craft/:
    • cna/
    • sharp-runtime/

Build (Linux)

cmake -S . -B cmake-build-debug -G Ninja
ninja -C cmake-build-debug

Graphics backend

MESH_CRAFT_GRAPHICS_BACKEND selects the CNA engine backend (EASYGL | SDL_RENDERER | BGFX | VULKAN | WEBGPU, default EASYGL). The editor's ImGui renderer consumes CNA draw primitives rather than native GL calls. EASYGL is the fully supported source-GLSL route; the current runtime gate also permits the separately qualified Vulkan editor path, while other backends remain deliberately rejected until they receive real screenshot qualification. Android and Emscripten force EASYGL: that selects GLES 3.0 / WebGL 2 rather than the non-qualified SDL renderer. The CNA-free CLI tools (mc3togltf, mc3tomcb) do not depend on the backend at all.

Android is a supported target at the source-configuration level, but no APK or on-device run is claimed yet: this workspace has no Android NDK and CNA's current Android cross-build is blocked in the sibling sharp-runtime before graphics code compiles. See plan.md AUD-042 for the exact remaining validation and packaging work.

# Working editor (default):
cmake -S . -B cmake-build-debug -G Ninja -DMESH_CRAFT_GRAPHICS_BACKEND=EASYGL
ninja -C cmake-build-debug

Note: sources are collected with file(GLOB_RECURSE), so CMake does not automatically notice a newly added .cpp file. After adding one (or a new CMakeLists.txt-registered test executable), re-run the cmake -S . -B cmake-build-debug configure step before building — ninja alone will not pick it up.

Install MC3/MCB libraries and create the CLI archive

The release component installs Mc3 and Mcb with MeshCraft::Mc3/MeshCraft::Mcb CMake targets and both conversion tools:

cmake --install cmake-build-debug --prefix "$PWD/meshcraft-install" --component release
cmake --build cmake-build-debug --target meshcraft_cli_release -j2

The latter writes a relocatable MeshCraft-<version>-<system>-cli.tar.gz in the build directory. An external CMake project can use the installed libraries with find_package(Mc3 CONFIG REQUIRED) and find_package(Mcb CONFIG REQUIRED), then link MeshCraft::Mc3 and MeshCraft::Mcb.

Build (Windows, MinGW cross-compile from Linux)

Requires the x86_64-w64-mingw32-gcc/g++ toolchain (Debian/Ubuntu: apt install g++-mingw-w64-x86-64). Write a toolchain file:

# mingw-toolchain.cmake
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
cmake -S . -B b-mingw -G Ninja -DCMAKE_TOOLCHAIN_FILE=mingw-toolchain.cmake -DBUILD_TESTING=OFF
cmake --build b-mingw -j4

Known blocker (as of this writing): configure succeeds and the build progresses through ~73% of the object graph (all of CNA's XNA-compatibility layer, Mc3, Manifold, most of ImGui), then fails on imgui_impl_opengl3.cpp: fatal error: GLES3/gl3.h: No such file or directory — CNA's build configures -DIMGUI_IMPL_OPENGL_ES3 unconditionally for the EASYGL backend regardless of target platform, and no GLES-for-Windows headers are vendored. This is a CNA-side graphics-backend decision, not something this repo can fix on its own — see cna's own issue tracker. SQLite3/OpenSSL/LibXml2 all gracefully disable (stubbed at compile time) rather than blocking configure when absent for the target, and the produced build.ninja correctly copies SDL3/SDL3_image/SDL3_mixer/libwinpthread-1 DLLs next to MeshCraft.exe and statically links libgcc/libstdc++.

Build (Web, Emscripten)

Requires the Emscripten SDK (source emsdk_env.sh puts emcmake/emcc/em++ on PATH):

emcmake cmake -S . -B b-web -G Ninja -DBUILD_TESTING=OFF
cmake --build b-web -j4
cd b-web && python3 -m http.server 8765   # then open http://localhost:8765/MeshCraft.html

Builds and links cleanly, and the page loads and initializes (WebGL 2.0 context, SDL_CreateWindow succeeds, scene creation logs) — but the app then crashes on the first window-resize event before rendering a frame, so the viewport never appears. The crash is a std::runtime_error thrown inside CNA (GameWindow::queryClientBoundsFromSDL()SDL_GetWindowSize() reports the video subsystem uninitialized); it is out of this repo's scope to fix. See NEXT.md §4 for the full traced root cause. CLI tools (mc3togltf, mc3tomcb) build the same way and run correctly under Node.

Offline / vendored build (SYS-W11-07)

Every third-party dependency this repo fetches is pinned to an exact version tag, so a fully offline build is possible without any code change — CMake's built-in FETCHCONTENT_SOURCE_DIR_<NAME> cache variable (uppercase of the FetchContent_Declare name) redirects that dependency to a local directory instead of cloning it, verified working in this repo directly (cmake -DFETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON=/path/to/local/checkout ... logs "Using the multi-header code from /path/to/local/checkout/include/", no network access attempted). Pre-populate each directory (a plain git clone <repo> --branch <tag> works) and pass the matching variable at configure time:

Dependency Repository Tag CMake variable
tinyxml2 leethomason/tinyxml2 10.0.0 FETCHCONTENT_SOURCE_DIR_TINYXML2
nlohmann/json nlohmann/json v3.11.3 FETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON
Dear ImGui ocornut/imgui v1.91.6 FETCHCONTENT_SOURCE_DIR_IMGUI
Manifold elalish/manifold v3.0.0 FETCHCONTENT_SOURCE_DIR_MANIFOLD
tinyobjloader tinyobjloader/tinyobjloader v2.0.0rc13 FETCHCONTENT_SOURCE_DIR_TINYOBJLOADER
tinygltf syoyo/tinygltf v2.9.3 FETCHCONTENT_SOURCE_DIR_TINYGLTF
cpp-httplib (optional, AI feature) yhirose/cpp-httplib v0.18.3 FETCHCONTENT_SOURCE_DIR_HTTPLIB
Lua (scripting, SYS-W14-18) lua/lua v5.4.7 FETCHCONTENT_SOURCE_DIR_LUA
sol2 (scripting, SYS-W14-18) ThePhD/sol2 v3.3.0 FETCHCONTENT_SOURCE_DIR_SOL2
cmake -S . -B b-offline -G Ninja \
  -DFETCHCONTENT_SOURCE_DIR_TINYXML2=/path/to/tinyxml2 \
  -DFETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON=/path/to/json \
  -DFETCHCONTENT_SOURCE_DIR_IMGUI=/path/to/imgui \
  -DFETCHCONTENT_SOURCE_DIR_MANIFOLD=/path/to/manifold \
  -DFETCHCONTENT_SOURCE_DIR_TINYOBJLOADER=/path/to/tinyobjloader \
  -DFETCHCONTENT_SOURCE_DIR_TINYGLTF=/path/to/tinygltf \
  -DFETCHCONTENT_SOURCE_DIR_HTTPLIB=/path/to/cpp-httplib \
  -DFETCHCONTENT_SOURCE_DIR_LUA=/path/to/lua \
  -DFETCHCONTENT_SOURCE_DIR_SOL2=/path/to/sol2

Omit any variable for a dependency you're fine fetching normally — they mix freely. ../cna/../sharp-runtime are never fetched at all (plain sibling-directory add_subdirectory, see Prerequisites above), so they need no override.

Not implemented, and not possible from this repo alone: "package-first discovery" (trying find_package(CNA) before falling back to add_subdirectory(../cna)) needs cna's own CMakeLists.txt to install()/export a package config first — it doesn't today (no install(TARGETS ...), no generated CNAConfig.cmake anywhere in that tree). Adding that is a change to cna itself, out of bounds per this project's own boundary rules (CLAUDE.md: "No CNA changes without owner permission").

Run

./cmake-build-debug/MeshCraft path/to/scene.mc3.xml

Headless one-shot flags

MeshCraft also supports three headless (no window shown, no interactive loop) one-shot modes, each rendering/measuring the scene and exiting:

./cmake-build-debug/MeshCraft scene.mc3.xml --screenshot out.png   # or out.ppm — see note below
./cmake-build-debug/MeshCraft scene.mc3.xml --export out.glb
./cmake-build-debug/MeshCraft scene.mc3.xml --benchmark            # timing breakdown to stdout

--screenshot's output format is chosen by the path's extension: .png writes a real PNG; any other extension (e.g. .ppm) writes raw PPM (P6) bytes regardless of what the extension says.

Export to glTF

./cmake-build-debug/mc3togltf/mc3togltf scene.mc3.xml scene.glb
./cmake-build-debug/mc3togltf/mc3togltf --stats scene.mc3.xml scene.glb  # print export statistics

For safety with untrusted scenes, mc3togltf rejects texture/mesh paths that are absolute or escape the input document's directory by default (so a malicious .mc3 can't read arbitrary local files into the output GLB). Pass --allow-external-resources for trusted scenes that legitimately reference files outside their own directory.

Convert to MCB

./cmake-build-debug/mc3tomcb/mc3tomcb scene.mc3.xml scene.mcb

Tests

ctest -V --test-dir cmake-build-debug

Re-run only the tests that failed last time (useful after fixing a handful of failures without re-running the whole ~40s suite):

ctest --test-dir cmake-build-debug --rerun-failed --output-on-failure

Tests are also grouped by label (format/export/render/registry/ ai/commands) — run just one group with ctest --test-dir cmake-build-debug -L export. See TESTING.md for the full test reference.

Current Features

  • Full .mc3.xml scene load/save with XML roundtrip
  • All primitive types rendered in 3D viewport
  • Transform gizmos (move / rotate / scale), local/world space
  • Object hierarchy panel with selection, multi-select, drag-and-drop
  • Properties panel (geometry, material, transform), multi-edit
  • Material editor (PBR: base color, roughness, metallic, emissive)
  • CSG boolean operations (union, difference, intersection via Manifold)
  • Extrude along path (arc, helix, polyline, bezier cross-sections)
  • Animation timeline: keyframes, channels, cubic/step/linear interpolation
  • Named layers, object tags, collision hints
  • Asset-definition LOD for Instances: authored near/mid/far definitions, hysteresis, maximum-distance culling, and selected-instance debug state; this is separate from primitive tessellation LOD
  • Fog, environment background, emissive bloom post-process
  • First-person walk mode
  • GLB export settings UI; headless screenshot mode
  • Undo/redo (exact 20-step deep-copy stack) plus a separate local, memory-budgeted session-history/checkpoint review dialog
  • Auto-save with rotating backups
  • MCB binary format (mc3tomcb)
  • glTF/GLB export (mc3togltf) with geometry reuse — repeated identical primitives, OBJ meshes, extrude shapes, and <instance> nodes share one glTF mesh buffer per unique geometry+material combination

Current Limitations

  • Bloom post-process uses CNA RenderTarget2D + ShaderEffect blur/composite passes; visibility still depends on authored emissive brightness. Custom text-shader effects are disabled on Vulkan until CNA exposes a complete cross-backend effect contract.
  • Walk mode always retains the y=0 ground plane and collides with scene primitives explicitly marked collision="box", uniform collision="sphere"/IcoSphere, or compatible upright collision="capsule" (including parent transforms). mesh, convex, incompatible shapes, and over-budget proxies are reported and ignored rather than being approximated silently.
  • Preferences dialog: auto-save interval, snap (translate/rotate/scale), grid spacing, and theme are all persisted (savePrefsAlg/loadPrefsAlg)
  • 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
  • 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
  • CSG export/preview: 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; the editor retains a named child-by-child fallback. Real CSG produces smooth normals with sharp creases, generated box/default or root-selected UV projection, and child-material ranges in both glTF and the live viewport when no CSG-root material overrides them; operand UV unwraps themselves are not preserved through cuts
  • 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: 37 render-labeled tests include real headless --screenshot checks (fog, light/camera gizmos, look-through-camera, embedded GLB, procedural LOD, authored asset LOD/culling, 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
  • 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
  • Model Registry: desktop SQLite entries have deterministic cached 64×64 catalog tiles (visual identities, not GPU-rendered scene previews), metadata/tag filters, license/provenance details, material-health reporting, and an explicit local asset-pack export with resolved library files. Database browsing/saving requires the system SQLite3 library and is stubbed on Emscripten/Android; the pure local pack helper remains testable there. There is no sync between registry database files, no account, no upload, and no automatic backup/share — the database remains local at ~/.meshcraft/modelregistry.sqlite3 (see m1m2m3.md).
  • 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.
  • 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.
  • Editable GLB/glTF import (SYS-W14-36): File → Import GLB / glTF... maps a bounded self-contained triangle GLB into native MC3 hierarchy, material/inline-image, camera, and punctual-light records. Mesh children preserve their source primitive with strict metadata selectors, so later preview/export does not flatten them again. The separate, unchecked Trusted external .gltf import option accepts only source-directory companions under 16 MiB JSON/48 MiB payload limits, converts them immediately to the same portable inline GLB, and never persists external paths. Inline image data has a 16 MiB encoded/16-million-pixel viewport decode cap. Skins/morphs/animations are explicitly reported as lossy, while non-triangle primitives reject before mutation.
  • N3–N7 scene data (scripts, sounds, music, triggers, scene states, meta) and event bindings are fully round-tripped (XML/JSON/MCB/XSD) and editable. Scripts (type="lua") run through the sandboxed LuaScriptRunner; explicit Triggers Fire and States Apply State actions perform their real effects. The Events tab's explicit Preview / Play events mode delivers timers, Walk Mode Area enter/exit, and picked-object clicks while respecting enabled/cooldown/one-shot/dispatch limits. Trigger, state, and script work runs as one isolated, validated document transaction: a failed script rolls back the complete batch, selection/undo history stay unchanged, and pending audio/action effects are suppressed. Normal editing does not dispatch bindings; this is a bounded preview, not a general game runtime. glTF warns once and omits bindings. See MC3_FORMAT.md and plan.md for the exact scope.
  • <library>/<imports> (R101/R110, Mc3ImportResolver): SYS-W14-28 completes the editor workflow around the existing format APIs. File ▸ Open Library and Save as Library use the dedicated .mc3lib.xml/.mc3lib.json loaders and writers; Save refreshes the content hash and requires a valid namespace plus major.minor.patch version. A selected scene object can become a named definition, and any definition can be published with its referenced materials/textures as a self-contained library. Imports resolve automatically after load and via the Imports tab; its health view reports the resolved file, declared namespace/version, effective SHA-256 hash, direct definition count, and missing/hash/cycle/identity/collision errors. Imported definitions remain external when a scene is saved, the picker can place them as editable Instances with text/category/semantic/style filters, and an edit that turns one into a local override is warned before saving rather than silently shadowing its source library.
  • <texture mip_maps="...">: as of SYS-W14-22 (2026-07-20), honored by mc3togltffalse 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.
  • <texture color_space="...">: as of SYS-W14-23 (2026-07-20), mc3togltf still doesn't re-encode pixels at export time (glTF 2.0's per-slot encoding — baseColor/emissive sRGB, normal/metallic-roughness/occlusion linear — is spec-mandated and has no per-texture override), but now warns when a texture's declared color_space conflicts with its slot's mandated encoding (e.g. a normal map declared color_space="srgb"), naming the material, texture, slot, and both the declared and required values, instead of silently doing nothing with the mismatch.
  • <uv_mapping projection="box"/"sphere">: mc3togltf uses exporter-compatible box/triplanar and equirectangular sphere projection. As of SYS-W14-32 (2026-07-26), the live viewport applies the same default/box/sphere projection plus scale, rotation, and offset to ordinary normal/UV meshes; its separately generated CSG mapping cache remains intentionally independent. See MC3_FORMAT.md for the precise mapping rules.
  • <light brightness="...">: as of SYS-W14-26 (2026-07-20), mc3togltf converts per light type instead of writing the same raw number into every type's glTF intensity regardless of its physically different mandated unit (directional = lux, point/spot = candela). Directional passes through unconverted (matches Blender's own glTF exporter's Sun-lamp convention); point/spot convert via brightness / (4π) × 683 (683 lm/W is the CIE luminous-efficacy constant, matching Blender's Point/Spot-lamp formula). A deliberate, documented scale factor per light type, not a claim of full physical calibration — brightness still has no editor-side lux/candela input mode. See MC3_FORMAT.md's Lights section for the full writeup.
  • <ambient> light: glTF 2.0 core + KHR_lights_punctual have no ambient-light concept at all (a real spec gap). As of SYS-W14-27 (2026-07-20), instead of being dropped outright it's approximated — every <ambient> light's color × brightness in the document is summed and baked into every material's own emissiveFactor, tinted by that material's base_color and clamped to [0,1], so a glTF-conformant viewer isn't fully unlit wherever an ambient fill was authored. A lossy approximation, not real global illumination — see MC3_FORMAT.md's Lights section for the full formula.
  • Live-viewport light shading (AUD-077, SYS-W14-33, 2026-07-26): directional (up to three) and first ambient lights use CNA BasicEffect; on source-GLSL-capable CNA backends, the first eight authored point/spot lights also preview through ShaderEffect, with inverse-square attenuation, range cutoff, spotlight cone/falloff, color, and authored brightness. Backends without that capability log a clear BasicEffect-plus-gizmo fallback. This is an unshadowed editor preview; mc3togltf remains the ground truth for exported lighting. See MC3_FORMAT.md's Lights section for details.

Historical platform verification record

docs/CAPABILITY_MATRIX.md is the authoritative current capability matrix. The table below is retained as dated build/runtime evidence and must not be read as a competing live status source.

Status as of the S16 (Cross-Platform Stability) stabilization pass, re-verified 2026-07-07 against a fresh MinGW cross-compile attempt (with the CNA_ENABLE_NET=OFF fix — see plan.md's "Post-650 Follow-Up Findings" — applied). Web-column rows re-diagnosed 2026-07-11: the blank canvas is a downstream symptom of a CNA-side crash on the first resize event, not a canvas-sizing config bug — full trace in NEXT.md §4. The earlier sizing theory is archived in docs/history/web_issues.md. Legend: ✅ verified working   🟡 partially verified / known gap   ❌ not available on this platform   ❓ not yet attempted (no toolchain available to test with).

Feature Linux Windows (MinGW) Web (Emscripten) Android
Full desktop/native build 🟡 the full GUI editor (MeshCraft.exe) still fails, but on the same pre-existing CNA-side GLES3/gl3.h header gap as before (imgui_impl_opengl3.cpp — CNA configures -DIMGUI_IMPL_OPENGL_ES3 unconditionally for the EASYGL backend regardless of target platform), plus 3 separate ../sharp-runtime-side -Werror build failures in its System.Net.Sockets/System.Xml namespaces (afunix.h's ADDRESS_FAMILY on this MinGW version, an unused-function warning, a sign-compare warning) — none of these are in this project's own source, all out of scope to fix without CNA/sharp-runtime maintainer involvement. New finding: the two CNA-free CLI tools (mc3togltf.exe, mc3tomcb.exe) build and link successfully as real Windows PE32+ executables, since neither links SHARP_RUNTIME or CNA at all — confirmed by running file on the actual output binaries, not just a partial object count 🟡 update 2026-07-09: the last-known-good build (2026-07-06 artifacts) still builds/runs fine, but a fresh rebuild now fails — ../sharp-runtime gained a new Emscripten-only regression since then (16 -Werror failures + 1 hard std::chrono::clock_cast compile error). See docs/history/web_issues.md (archived). 🟡 source selects GLES/EASYGL; no NDK configure, APK package, or on-device test yet. CNA's current NDK cross-build is blocked in sibling sharp-runtime before graphics compile
App launches / runs ❌ (build doesn't complete) ❌ loads and initializes (SDL_CreateWindow, WebGL2 context, scene creation) but then crashes on the first resize event, before any frame renders — see below
3D viewport rendering ❌ (build doesn't complete) ❌ never reached. Root cause (re-diagnosed 2026-07-11, supersedes the earlier "canvas 0×0" theory): an uncaught std::runtime_error from CNA's GameWindow::queryClientBoundsFromSDL()SDL_GetWindowSize() reports "Video subsystem has not been initialized" on the first SDL_EVENT_WINDOW_RESIZED, killing the wasm module. 100% inside CNA; not fixable from this repo. Full trace in NEXT.md §4.
Shaders (GLSL ES 3.00 / WebGL2) ✅ (desktop GL) ❌ (build doesn't complete) ✅ all 7 CNA EasyGL 3D shader programs are #version 300 es and compile/link cleanly
Config/prefs/recent-files/keybindings persistence ~/.config/meshcraft %APPDATA%\meshcraft (code-verified; no working build to run it against yet) 🟡 the web pre-JS mounts IDBFS at Emscripten's $HOME and syncs it on startup/unload, so the intended persistent filesystem route is implemented. End-to-end persistence remains unverified because the CNA resize crash prevents a usable editor session. ❓ (falls through to the same non-Windows $HOME-based logic as Linux; not verified on-device)
SQLite Model Registry ✅ (or gracefully stubbed if SQLite3 dev package absent) 🟡 gracefully stubbed when SQLite3 absent (code-verified; no working build to run it against yet) ❌ always stubbed (MESHCRAFT_HAS_SQLITE3 never defined) ❌ always stubbed (same guard as Web)
AI Assistant (Claude API) ✅ (or gracefully stubbed if OpenSSL absent) 🟡 same as SQLite3 above ❌ always stubbed (MESHCRAFT_HAS_AI never defined) ❌ always stubbed (same guard as Web)
File dialogs (text-path-field fallback) ✅ (no native OS dialog anywhere — plain ImGui::InputText, platform-agnostic by construction)
Path handling (UTF-8, spaces, separators) ✅ (round-trip tested) ✅ (std::filesystem::path used consistently; not executable-tested on real Windows) ✅ (std::filesystem::path; not executable-tested) ✅ (same code path)
Static runtime linking / DLL bundling N/A -static-libgcc -static-libstdc++, plus SDL3/SDL3_image/SDL3_mixer/libwinpthread-1 DLLs copied next to the .exe (build-graph verified) N/A (single .wasm, no separate runtime DLLs)
wasm exceptions / preloaded test assets N/A N/A -fwasm-exceptions on compile+link; --preload-file test@/test packages all test/ assets into MeshCraft.data N/A
CI ✅ active GitHub Actions: standalone component matrix plus full EASYGL editor build/tests and Vulkan editor configure/build ❌ no Windows runner ❌ no Web runner ❌ no Android runner

Reporting a Crash

MeshCraft has no built-in crash reporter — if it crashes, capture a backtrace with the OS-standard tools below and attach it to a GitHub Issue along with the scene file (if any) and the exact command line that triggered it.

Linux:

# Debug build already has debug info (see "Build (Linux)" above)
ulimit -c unlimited                       # enable core dumps for this shell
./cmake-build-debug/MeshCraft scene.mc3.xml
# after it crashes, find the core file (verified: named core.<pid> by
# default on this system — check `cat /proc/sys/kernel/core_pattern`
# if yours differs) and load it:
gdb ./cmake-build-debug/MeshCraft core.<pid>
(gdb) bt full                              # full backtrace — this is what to attach

If it hangs instead of crashing, attach a backtrace from a running process instead: gdb -p $(pgrep MeshCraft) -batch -ex "bt full".

Windows: no build/test verification of this exists in this environment (Linux-only dev setup) — the standard approach is to let Windows Error Reporting generate a .dmp file (Control Panel → System → Advanced → check "Windows Error Reporting" settings, or trigger one directly via Task Manager → right-click the hung/crashed process → "Create dump file") and open it in WinDbg or Visual Studio for a backtrace. Treat this as unverified guidance, not a tested procedure.

Backup and Recovery

Auto-save and backups live next to the scene file itself — there is no separate cache/config directory for them.

  • Auto-save: while a file scene.mc3.xml is open, the editor periodically writes the current in-memory state to scene.mc3.xml.autosave (interval configurable in Preferences, default 60s). This file is deleted automatically on the next explicit Save — it's a crash-recovery net, not a permanent artifact.
  • Recovering after a crash: reopen scene.mc3.xml normally. If its .autosave sibling is newer, the editor opens Recover Unsaved Changes. Choose Recover to load the autosave while retaining the original file as the save target, or Discard Autosave to remove it. Recovered content is marked modified and the autosave remains as a safety net until an explicit successful Save.
  • Backup rotation on every explicit Save: before writing, the previous on-disk content is preserved as scene.mc3.xml.backup.1 (most recent prior version); if a .backup.1 already existed, it's first renamed to scene.mc3.xml.backup.2 (previous-to-that version) — a fixed 2-slot ring buffer, nothing older than 2 saves back is kept. To recover from a bad save (e.g. accidentally saved over good work with something wrong), copy .backup.1 (or .backup.2 for one save further back) over scene.mc3.xml.

License

MIT — see LICENSE.

About

C++23 3D scene editor for the XML-based MC3 format — primitives, CSG booleans, PBR materials, keyframe animation, glTF/GLB and MCB export.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages