magic-pixels
    Preparing search index...

    Scene graph

    A scene is a tree. Scene is the root, Meshes, cameras, Lights and plain Object3Ds are the nodes, and every node's transform is relative to its parent. The whole point of the tree is that "relative to the parent" composes: move the parent and every descendant moves with it.

    • position, quaternion, rotation, scale: the object's transform relative to its parent. The rotation is stored twice: quaternion is a Quaternion, the representation the local matrix is built from, and rotation is the same rotation as Euler angles in radians, XYZ order (see Matrices). Write to whichever is convenient; the tree walk keeps them in sync (see below).
    • localMatrix: position, quaternion and scale composed into one matrix, T × R × S.
    • worldMatrix: parent.worldMatrix × localMatrix, the transform relative to the scene root. This is what the renderer uploads as modelMatrix.
    • parent and children, maintained by add(), remove() and removeFromParent(). An object has one parent; adding it somewhere else moves it. add() refuses to create a cycle.
    • visible: an invisible object and its whole subtree are skipped by prepareScene().

    updateWorldMatrix() is the one function that turns the tree into matrices. The renderer calls it on the scene at the start of every frame, and it recurses depth-first:

    1. If matrixAutoUpdate is true (the default), bring quaternion and rotation in sync and recompose localMatrix from position, quaternion and scale. The sync compares both rotation fields against their values at the last update: if the quaternion changed, rotation is derived from it; if only the Euler angles changed, quaternion is derived from them; if both changed, the quaternion wins. There is no change detection on position and scale, so the recompose always happens; composing is cheap.
    2. If this object was flagged or an ancestor's world matrix changed, recompute worldMatrix from the parent's, clear the flag, call the onWorldMatrixChanged() hook (cameras use it to update their view matrix), and force all descendants to recompute too.
    3. Recurse into the children with that force flag.

    A camera that is not part of the scene gets its own updateWorldMatrix() call from prepareScene(), so scene.add(camera) is optional. Adding it is useful when the camera should follow an object: parent it to the object.

    Set matrixAutoUpdate = false to stop the recompose in step 1, write to localMatrix directly and set worldMatrixNeedsUpdate = true so step 2 runs. This is for cases where a matrix is the natural input: a transform loaded from a file, a physics engine, an instance matrix. The three vectors then no longer describe the object.

    Because a child inherits its parent's rotation, planet.add(moon) with the moon at (2, 0, 0) makes the moon orbit when planet.rotation.y changes. But that also spins the planet. To rotate the moon around the planet without rotating the planet, insert an empty Object3D as a pivot:

    const pivot = new Object3D();
    planet.add(pivot);
    pivot.add(moon);
    moon.position.set(2, 0, 0);
    pivot.rotation.y += dt;

    The pivot sits at the planet's origin, so rotating it swings everything attached to it around that origin. Empty nodes as pivots, hinges and joints are the standard tool in any scene graph; a glTF file is full of them.

    object.lookAt(target) rotates the object so that its +Z axis points at target, a point in the parent's coordinate system (the same system as position). A camera's viewing direction is -Z, so Camera overrides lookAt to point -Z at the target, and DirectionalLight, which shines along -Z, does the same; all share Mat4.lookAt, which builds a rotation from a forward vector and an up hint by two cross products.

    The resulting matrix goes into setRotationFromMatrix(), which sets quaternion from the rotation part of the matrix and derives rotation from the quaternion, so both fields are current right after the call. The conversions are explained on the Quaternions page.

    object.traverse(callback) visits the object and every descendant, depth-first, in the order the renderer draws them. It is the tool for things like "hide every mesh with this material" or "count the triangles".