Scene graph · class
SceneNode
Explained in Hello world, The scene graph.
class SceneNodeimport { SceneNode } from '@driftengine/core';Constructor
new
constructor()Properties
| Name | Type | Description |
|---|---|---|
positionreadonly | Float32Array<ArrayBuffer> | Local translation. Written in place; call markMoved if you write it directly. |
rotationreadonly | Float32Array<ArrayBuffer> | Local rotation, as a quaternion.MoreA quaternion and not the yaw this engine uses everywhere else. Every rotation in the tree today is a yaw or a matrix, because nothing has needed to interpolate one — and a node is what animation will hold, where "rigid TRS, then skinning" is the first line of that track. Interpolating Euler angles gives gimbal artefacts and a path through orientations nobody authored, and carrying the right type from the start is cheaper than migrating every node a consumer has built. |
scalereadonly | Float32Array<ArrayBuffer> | |
localMatrixreadonly | Float32Array<ArrayBuffer> | |
worldMatrixreadonly | Float32Array<ArrayBuffer> | |
parent | SceneNode | null | |
childrenreadonly | SceneNode[] | |
worldRevision | number | What worldMatrix was last recomputed at. See revisions. |
worldBoundsreadonly | Bounds | A sphere enclosing this node's geometry and every descendant's.MoreThe union is what makes a traversal a partition rather than a loop. A parent that can be tested once and answer for everything beneath it lets a frustum discard a subtree; a parent covering only its own geometry answers for nothing, and the walk has to visit every node to find out — which is the linear scan a hierarchy exists to replace. A node with no geometry and no children is a point at its own origin, which is the honest answer and is never inside a frustum by accident: a zero radius passes only where the point itself does. |
Accessors
| Name | Type | Description |
|---|---|---|
hasGeometryget | boolean | Whether this node has geometry of its own, as opposed to only grouping others. |
Methods
setPosition
setPosition(x: number, y: number, z: number): void| Parameter | Type | Description |
|---|---|---|
x | number | |
y | number | |
z | number |
setScale
setScale(x: number, y: number, z: number): void| Parameter | Type | Description |
|---|---|---|
x | number | |
y | number | |
z | number |
setBounds
setBounds(bounds: Bounds | null): voidSay what geometry this node holds, in its own space.
| Parameter | Type | Description |
|---|---|---|
bounds | Bounds | null |
More
Held by reference rather than copied: a mesh's bounds do not change after upload, and a caller passing the same mesh to a thousand nodes should not pay for a thousand copies.
setRotationAxisAngle
setRotationAxisAngle(x: number, y: number, z: number, radians: number): voidSet the rotation from an axis and an angle in radians. The axis need not be normalised.
| Parameter | Type | Description |
|---|---|---|
x | number | |
y | number | |
z | number | |
radians | number |
markMoved
markMoved(): voidSay that this node's transform was written directly.
More
position, rotation and scale are handed out as arrays a caller can write in place,
because copying three vectors per node per frame is the allocation-free path an animation
system wants. The cost is that this class cannot see the write, so a caller doing it says so.
attachChild
attachChild(child: SceneNode): voidPut child under this node, taking it from wherever it was.
| Parameter | Type | Description |
|---|---|---|
child | SceneNode |
More
A cycle is refused here rather than discovered while walking, because a walk that meets one does not throw: it recurses until the stack is gone, inside a frame loop, with the tab stopped and no useful trace. The check walks up from this node, which is the depth of the tree and not its size.
detachChild
detachChild(child: SceneNode): void| Parameter | Type | Description |
|---|---|---|
child | SceneNode |
updateWorld
updateWorld(): voidBring this node and everything under it up to date.
More
Only what moved. A hierarchy that recomputes every world matrix every frame is slower than the callers it replaces, which already keep matrices they touch only when something moves. A clean subtree is skipped entirely, which is what makes the walk proportional to what changed rather than to the size of the world.
Its ancestors are brought up to date first, and that is a correctness fix rather than a courtesy. Without it, updating a branch after moving something above it composes against a stale parent and puts the whole branch in the wrong place — silently, with no error and a plausible-looking matrix. Walking up costs the depth of the tree, not its size, and touches only this node's own ancestors: a sibling subtree is not recomputed for it.