Expand description
Parenting: local placements composed into world placements.
A scene node is an entity carrying a Local — where it sits relative to
whatever it is attached to. Attachment is a second component, Parent,
holding a whole Entity handle. propagate reads those two stores and
fills a third with Global, the world placement, resolving every parent
before its children.
§Contract
-
Deterministic. For a hierarchy without loops, the output depends on the shape of that hierarchy and on nothing else — not on slot numbers, not on insertion history, not on how many times
propagatehas run. Two worlds built with different slot assignments but the same shape produce the same placements, bit for bit; the relabelling property test holds this.A loop is the one exception, and it is exact rather than hedged. Every node still gets a placement and the count still reports the loop. The cut falls on the loop member the climb reaches last — the one whose own parent is the member the climb entered the loop by — and which member that is follows from which node the pass seeded from, which is entity order. Relabel the world and a different member may be cut, so the placements inside a loop can differ between two worlds of the same shape. No ordering fixes this — a loop has no member a hierarchy can call first — so a caller who needs reproducible placements must not build one, and
Propagated::cyclicis how they find out they did. -
Total. Every live entity with a
Localgets aGlobal, including nodes whose parent died, nodes whose parent is not a scene node, and nodes inside a cycle. There is no live input for which a node is silently skipped;Propagatedcounts each category so a caller can notice. A despawned entity is not walked at all — see Stale globals. -
Exact rotation. Angles compose by wrapping addition on a binary-angle integer, so a chain of rotations neither drifts nor accumulates error, at any depth. Translation composes in fixed point and saturates rather than wrapping;
renew_fixed::saturations()counts it when it happens. -
Globals are derived, never authored.
Globalhas no public fields, and no public way to build one from a placement — no constructor taking a translation and a rotation, and noDefault. The only values that exist are the onespropagatederived andGlobal::IDENTITY, the world origin. That is what makes “a world placement is a function of the hierarchy” a property of the type rather than a habit a caller can forget.
§Two mechanics that look like details and are not
A parent is a whole handle, not a slot. Parent stores Entity,
generation included, and propagate checks it against Entities. Slots
are recycled; a bare index would silently re-attach an orphan to whatever
moved in next, which is a bug that reproduces perfectly and looks like a
physics glitch.
The check is against the Entities handed to propagate, and generations
are only unique within one allocator. A handle minted by a different
Entities whose slot and generation both happen to match will be obeyed.
Nothing in the engine hands out entities from two allocators into one world,
and this crate does not defend against it.
Resolution order is not slot order. Entities::spawn pops its free list
newest-first, so a child can hold a lower slot than its parent. A single
ascending pass would then compose that child against its parent’s
previous-tick placement — one frame of lag, on some entities, depending on
spawn history. It is invisible to a determinism test, because it is
perfectly reproducible. propagate therefore walks each node’s ancestry
upward first and composes on the way back down. slot_order_does_not_decide
is the regression test.
§Stale globals
Despawning an entity does not remove its components — that is true of every
store in this engine and scene is not special. A Global left behind by a
despawned node is never read as anybody’s parent, because the handle check
rejects it first; it is only occupying a slot until the caller removes it.
Structs§
- Global
- Where a node sits in the world.
- Local
- Where a node sits relative to its parent, or relative to the world when it has none.
- Parent
- What a node is attached to.
- Propagated
- What a single
propagatecall did. - Scratch
- Reusable buffers for
propagate.