Data Flow
How information moves from disk to UI, from UI to scene model, and back to disk.
The Mc3Document as the Central Hub
Every piece of scene data lives in exactly one Mc3Document instance owned by MeshCraftApplication as a std::shared_ptr<Mc3Document> document_.
The renderer, all UI panels, the undo stack, and the serialiser all reference this same object.
MeshCraftApplication
├── std::shared_ptr<Mc3Document> document_ ← single source of truth
├── SceneRenderer renderer_
├── EditorCamera camera_
├── SceneHierarchyPanel hierarchyPanel_
├── PropertiesPanel propertiesPanel_
├── TimelinePanel timelinePanel_
└── std::vector<Mc3Document> undoStack_ ← deep-copy snapshots
Load Path
┌─────────────┐ argv[1] / Ctrl+O ┌─────────────────┐
│ OS / User │ ──────────────────▶ │ MeshCraftApp │
└─────────────┘ │ ::loadFile() │
└────────┬─────────┘
│ calls
▼
┌──────────────────────┐
│ Mc3Document │
│ ::loadFromFile() │
│ (tinyxml2 parse) │
└────────┬─────────────┘
│ populates
▼
┌──────────────────────┐
│ Mc3Document │
│ (lights, materials, │
│ objects, actions…) │
└──────────────────────┘
MeshCraftApplication::LoadContent()checksargv[1]; if present, callsloadFile(path).loadFile()callsMc3Document::loadFromFile(path), which uses tinyxml2 to parse the XML into the in-memory model.- The renderer's GPU caches are empty; meshes are uploaded lazily on the first frame they are needed.
- The undo stack is cleared on load.
Edit Path (UI → Model)
User drags gizmo / edits field in ImGui panel
│
▼
┌────────────────────────────────┐
│ PropertiesPanel / Gizmo code │
│ 1. pushUndo() │ ← deep-copy Mc3Document into undoStack_
│ 2. mutate document_->objects │ ← in-place edit
└────────────┬───────────────────┘
│ document_ is shared_ptr — renderer sees the same data next frame
▼
┌─────────────────────┐
│ SceneRenderer │
│ next frame: │
│ cache miss → re- │
│ upload GPU mesh │
└─────────────────────┘
The mutation protocol for every editor panel is always:
- Call
MeshCraftApplication::pushUndo()— pushes a deep copy of*document_. - Mutate
document_fields directly. - Optionally invalidate renderer caches if the mutation changes geometry.
Save Path
Ctrl+S / Ctrl+Shift+S
│
▼
┌──────────────────────────────┐
│ MeshCraftApplication │
│ ::saveFile(path) │
└──────────┬───────────────────┘
│ calls
▼
┌──────────────────────────────┐
│ Mc3Document::saveToFile() │
│ (tinyxml2 write) │
└──────────────────────────────┘
│ writes
▼
.mc3.xml on disk
Mc3Document::saveToFile() serialises the entire in-memory document back to XML. The file is overwritten atomically (write to temp, then rename — exact behavior depends on the OS and tinyxml2 version).
Animation Playback Data Flow
TimelinePanel
sets: currentAction_, playbackTime_, isPlaying_
│
▼
MeshCraftApplication::Update()
if (isPlaying_) advance playbackTime_
│
▼
SceneRenderer::drawScene(document_, camera_, animOverrides)
│
For each object in scene:
1. look up animOverrides[object.name]
2. if override present → use override transform
3. else → use object.transform
│
▼
render with computed transform
The animOverrides map is rebuilt every frame by evaluating all channels of the active action at playbackTime_ using evaluateChannel(). It is a temporary data structure — it is not stored in Mc3Document and is not saved to disk.
mc3togltf Data Flow
The converter has its own independent data flow with no runtime editor dependency:
.mc3.xml file
│
▼
Mc3XmlParser (local copy in mc3togltf/)
│ builds local Mc3Document model
▼
GltfExporter
│ traverses model, emits glTF JSON / GLB binary
▼
.gltf + .bin OR .glb
The mc3togltf converter has its own copy of the MC3 parser (not the same source files as mc3/). Changes to the format must be applied to both parsers.