Docs/Internals/Data Flow

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…) │
                                    └──────────────────────┘
    
  1. MeshCraftApplication::LoadContent() checks argv[1]; if present, calls loadFile(path).
  2. loadFile() calls Mc3Document::loadFromFile(path), which uses tinyxml2 to parse the XML into the in-memory model.
  3. The renderer's GPU caches are empty; meshes are uploaded lazily on the first frame they are needed.
  4. 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:

  1. Call MeshCraftApplication::pushUndo() — pushes a deep copy of *document_.
  2. Mutate document_ fields directly.
  3. 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.