Architecture
How MeshCraft's major subsystems fit together and communicate.
Subsystem Overview
┌─────────────────────────────────────────────────────────────────┐
│ MeshCraftApplication │
│ (CNA Game subclass — owns all editor state and orchestrates) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ EditorCamera │ │ SelectionMgr │ │ TransformGizmo │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌─────────────────────────────────┐ │
│ │ GridRenderer │ │ SceneRenderer │ │
│ └──────────────────┘ │ (unit meshes, CSG, textures, │ │
│ │ gizmos, edge overlay, anim) │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌───────────────────────────────┐ │
│ │ SceneHierarchyPanel │ │ PropertiesPanel │ │
│ │ (ImGui left panel) │ │ (ImGui right panel) │ │
│ └──────────────────────┘ └───────────────────────────────┘ │
│ │
│ Timeline Panel (Ctrl+T) Menu Bar + Toolbar Status Bar │
└──────────────────────────┬──────────────────────────────────────┘
│ owns
▼
┌──────────────────┐
│ Mc3Document │ ← pure C++, no graphics
│ (mc3 library) │
│ objects, mats, │
│ lights, cameras,│
│ actions, defs │
└──────────────────┘
│ serialized by
▼
┌────────────────────┐
│ Mc3XmlParser / │
│ Mc3XmlWriter │
└────────────────────┘
│ exported by (separate binary)
▼
┌────────────────────┐
│ mc3togltf │
│ (CLI converter) │
│ → glTF 2.0 / GLB │
└────────────────────┘
═══════════════ External Frameworks ═══════════════════════════════
CNA: SDL3 window, GL context, BasicEffect, SpriteBatch, Input
Dear ImGui v1.91.6: all UI panels (SDL3 + OpenGL ES 3 backends)
Manifold v3: CSG boolean mesh evaluation
tinyobjloader: OBJ mesh loading for viewport
tinyxml2: XML parsing (via sharp-runtime)
Application Layer
MeshCraftApplication (src/MeshCraft/MeshCraftApplication.cpp) is the central class. It subclasses CNA's Microsoft::Xna::Framework::Game and owns:
- The complete scene data (
Mc3Document document_) - All editor state: camera, selection, active tool, gizmo, animation playback
- All renderers (GridRenderer, SceneRenderer)
- All UI panels (SceneHierarchyPanel, PropertiesPanel)
- Undo/redo stacks
- Clipboard, recent files, file dialog state
The Game Loop
MeshCraft uses the CNA game loop model — a fixed-step update/draw cycle:
| Override | What happens |
|---|---|
LoadContent() |
Creates renderers, loads Mc3Document from the path given on the command line (or creates an empty scene), initialises ImGui (SDL3 + OpenGL ES 3 backends). |
BeginDraw() |
Calls ImGui::NewFrame() to start an ImGui frame before the 3D scene is rendered. |
Update() |
Polls Keyboard and Mouse; processes shortcuts; handles viewport mouse (orbit, pan, zoom, picking, gizmo drag); advances animation playback clock. |
Draw() |
Clears the frame; evaluates animation overrides; renders the 3D scene via SceneRenderer and grid via GridRenderer; draws the ImGui UI (drawImGuiUi() + drawTimelinePanel()). |
EndDraw() |
Calls ImGui::Render() + ImGui_ImplOpenGL3_RenderDrawData(), optionally writes auto-screenshot, then calls Game::EndDraw(). |
Scene Data Layer (mc3 Library)
The mc3 sublibrary is entirely pure C++ — no graphics, no windowing, no ImGui. It defines the in-memory representation of a scene and handles XML serialization.
Mc3Document ├── objects: vector<shared_ptr<Mc3Object>> ← scene graph roots ├── definitions: map<string, shared_ptr<Mc3Object>> ├── materials: map<string, Mc3Material> ├── textures: map<string, Mc3Texture> ├── lights: vector<Mc3Light> ├── cameras: vector<Mc3Camera> ├── actions: map<string, Mc3Action> ← animation clips └── environment: optional<Mc3Environment> Mc3Object (scene graph node) ├── type: ObjectType (Box, Sphere, Cylinder, Cone, Plane, │ Mesh, Extrude, Group, Instance, │ Union, Difference, Intersection, Area) ├── transform: Mc3Transform (position, rotation, scale, pivot) ├── material: string (IDREF into Mc3Document.materials) ├── primitive: optional<Mc3Primitive> ├── extrude: optional<Mc3Extrude> ├── csgOperation: optional<Mc3CsgOperation> └── children: vector<shared_ptr<Mc3Object>>
Rendering Layer
SceneRenderer traverses the Mc3Document.objects tree recursively. For each node it:
- Computes a world matrix from the object's transform (with pivot compensation)
- Applies animation overrides if a clip is playing
- Selects the appropriate pre-built unit mesh (box, sphere, cylinder, cone, plane)
- For CSG nodes, evaluates real boolean geometry via Manifold and caches the result
- For
<mesh>nodes, loads the OBJ via tinyobjloader and caches it - Applies the material color or texture if available
- Calls
BasicEffectto render with lighting
The EditorCamera provides view and projection matrices; these are passed into every render call.
Editor Interaction Layer
| Component | Role |
|---|---|
EditorCamera | Orbit/pan/zoom model. Computes view + projection matrices and screen-ray directions for picking. |
SelectionManager | Holds a set of shared_ptr<Mc3Object>. Supports single and multi-select (Ctrl+click, box-drag, Ctrl+A). |
TransformGizmo | Tracks which gizmo mode (Move/Scale/Rotate) and which axis is active. Drag state is resolved in MeshCraftApplication::handleMouseInput(). |
UI Layer (Dear ImGui)
All UI is drawn with Dear ImGui (v1.91.6) using the SDL3 + OpenGL ES 3 backends. SDL events are forwarded to ImGui via SDL_AddEventWatch.
| Panel | Location | Notes |
|---|---|---|
| Menu Bar | Top | File, Edit, Add, View menus |
| Toolbar | Below menu | Tool switching + add-primitive buttons + edge overlay toggle |
| Hierarchy Panel | Left (220 px) | Recursive object tree, visibility toggle, drag-and-drop reparenting, inline rename |
| Properties Panel | Right (220 px) | Name, position/rotation/scale, material, collision, tags; tabs for Lights / Cameras / Textures / Materials / Defs |
| Viewport | Center | 3D scene + gizmos; uses raw GL viewport/scissor for proper clipping |
| Timeline Panel | Bottom (190 px, toggle) | Action dropdown, play/pause/stop, time scrubber, per-channel keyframe rows |
| Status Bar | Bottom (22 px) | Object count, selection count, timed status messages |
Important Invariants
Mc3Document.materialsisstd::map<string, Mc3Material>— iterate with structured bindings.Mc3MaterialusesbaseColor(float[4]), notdiffuse.- ImGui panels use
NoMove | NoResize | NoBringToFrontOnFocusflags — do not make them floating. - Viewport rect is
[kLeftPanelW, imguiTopH_]to[W - kRightPanelW, H - kStatusH]. pendingScreenshot_flag is set inDraw()and consumed inEndDraw()after ImGui renders.Color(CNA type) has no default constructor — always init with 4 args:Color(r, g, b, a).- Call
pushUndo()before any mutation ofdocument_. - Do not add
${meta-gl_SOURCE_DIR}/includeto CMakeLists.txt — triggers a full CNA recompile. - Do not change
Mc3Documentpublic API without checkingmc3togltfand all test XMLs.
External Dependency Graph
MeshCraft (executable)
├── CNA ← sibling repo ../cna
│ └── SHARP_RUNTIME ← sibling repo ../sharp-runtime
│ └── tinyxml2 (bundled)
├── Mc3 ← mc3/ sublibrary
│ └── tinyxml2 (same instance, guarded by CMake alias)
├── ImGui v1.91.6 (FetchContent)
├── Manifold v3 (FetchContent)
└── tinyobjloader v2 (FetchContent)
mc3togltf (separate executable)
├── Mc3XmlParser (local copy of mc3 parser)
├── GltfExporter
└── MeshBuilder
└── (no CNA, no ImGui, no Manifold)