Layer 2. CPU-side authoritative state for the scene graph: nodes,
meshes, materials, cameras, lights, animations, sprites, emitters, force
fields, rigid-body attachments. render only reads from here
— never the reverse.
Node carries a Transform, a parent +
children, a NodeTagSet, and a NodeAttachment
union (mesh / light / emitter / forcefield / sprite / rigid body).
Hierarchical transforms cascade parent → child. Lookups are via
generational handles (NodeHandle, MeshHandle,
…) backed by containers.Pool.
traverse walks the dirty subtree and recomputes world
matrices. Nothing recomputes unless something upstream moved.
The world module must not touch GPU buffers
directly. Every mutator (spawn, translate,
set_material_handle, …) appends to a private staging queue.
The engine drains the queue once per frame on the render thread inside
sync_staging_to_gpu and forwards the entries to
render.
This is the single-source-of-truth contract. CPU state in
world is authoritative; GPU state is a mirror. Drift is
impossible because drift would require two writers, and
render only ever reads.
See architecture for the staging
pipeline and the FRAMES_IN_FLIGHT age rule that defers GPU
releases.
Each Camera carries a PassTypeSet
describing which passes run for it. The same scene can host a main
camera, shadow casters, minimap cameras, and offscreen render targets —
each with its own pass set and attachments. The composition story
(camera-B’s output as a texture in camera-A) is just “sample a bindless
handle”; the engine side does no special casing.
world is the layer that applies the data
structures from animation/. Per skinned node it runs FK
clip sampling, then any IK or procedural modifier layers, blends them by
weight + bone mask, and writes the resulting bone matrices into the
node’s skinning buffer.
Layer types: FKLayer (sampled clip),
IKLayer (FABRIK target), ProceduralLayer (tail
/ path / spider-leg / single-bone-rotation modifiers). Layers blend in
insertion order using the layer’s BlendMode (REPLACE / ADD
/ MULTIPLY / OVERRIDE).
world.load_gltf and world.load_obj populate
nodes + meshes + materials + skeletons + clips in one pass. The engine
wrapper mjolnir.load_gltf injects the texture allocator so
glTF textures land in the bindless arena automatically.