@pixi/3d
    Preparing search index...

    Class BatchedMesh3D

    A scene node that draws many meshes as slots of one batch, with no scene node per mesh

    Reach for it for crowds, foliage, debris and other large counts. Each slot is a geometry, a material and a matrix, and updating a slot costs a few array writes. Moving the BatchedMesh3D moves every slot at no extra cost.

    The slot count is fixed at construction. Fill slots with setGeometryAt, setMaterialAt and setMatrixAt, then put each one in its passes with setPassesAt. A slot needs a geometry and a material only while it's in a pass, so spare slots can stay empty. To spawn and despawn, show and hide slots with setVisibleAt.

    Unlike a Mesh3D, a slot doesn't notice when its material's shader changes; see invalidate. For per-slot values a shader reads, such as a tint, see instanceData.

    import { BatchedMesh3D, RenderPassBit } from '@pixi/3d';

    const batched = new BatchedMesh3D({ size: 1000 });

    for (let i = 0; i < 1000; i++) {
    batched.setGeometryAt(i, cubeGeometry);
    batched.setMaterialAt(i, material);
    batched.setMatrixAt(i, matrix);
    batched.setPassesAt(i, RenderPassBit.OPAQUE | RenderPassBit.SHADOW);
    }

    view.root.addChild(batched);

    // Moving one slot is a matrix write, cheap enough for every frame.
    batched.setMatrixAt(0, nextMatrix);

    // Hide a slot to despawn it; show it again to respawn.
    batched.setVisibleAt(1, false);

    BatchedMesh3DOptions For the constructor options.

    Hierarchy (View Summary)

    Index
    boundsVolume: Box | null = null

    A fixed local-space box measured in place of this container and its subtree, or null to measure the geometry

    Set one on a large subtree whose extent you already know, such as a level chunk, to skip walking it. getLocalBounds and the size members read it. It's pixi's boundsArea; it doesn't change culling.

    swarm.boundsVolume = new Box(new Vector3(-10, 0, -10), new Vector3(10, 5, 10));
    
    children: Container3D[] = []

    Child nodes

    Their order doesn't decide what draws first. Read the array, but change it through addChild and removeChild. To visit the whole subtree, use traverse.

    for (const child of group.children) child.visible = false;
    
    cursor: string & {} | Cursor | null

    CSS cursor shown while this node is the pointer's target

    Only the target's own cursor counts, as in 2D: a hit on a 'passive' child shows its interactive ancestor's cursor, so set it on the node with the listeners. null shows the View3D's own cursor. Any CSS cursor value works, url(...) included.

    null
    
    button.cursor = 'pointer';
    handle.cursor = 'grab';
    blade.cursor = 'url("crosshair.png") 8 8, crosshair'; // custom, with a hotspot
    destroyed: boolean = false

    Whether destroy has run, as in pixi's Container

    eventMode: EventMode

    How this node takes part in pointer events, as on a pixi.js Container

    • 'none': never picked, and neither is anything below it. The subtree costs nothing per pointer event.
    • 'passive' (default): fires no listeners and is never the target. Under an interactive ancestor it is still picked, and the hit goes to that ancestor with this node's surface data. So one 'static' group makes its whole subtree clickable, with the group as target.
    • 'auto': as 'passive', but ray-tested even with no interactive ancestor, which costs time and finds no target. Prefer 'passive'.
    • 'static': picked against its hitVolume, and fires listeners.
    • 'dynamic': as 'static', and the View3D also picks again while a mouse or pen is still, so a node that moves under it gets its enter and leave events.

    The renderer's eventMode option only sets the default for 2D containers, and the View warns once when it isn't 'passive'. Picking costs time for each node that takes part, so set the mode on the nodes and groups that need it.

    'passive'
    
    button.eventMode = 'static';   // a clickable mesh
    spinner.eventMode = 'dynamic'; // it moves under a still pointer
    panel.eventMode = 'none'; // decoration; skip it and its children

    // A whole model that answers as one target; its meshes need no setup
    robot.eventMode = 'static';
    hitVolume: HitVolume3DInput

    Shape the pointer ray is tested against when picking this node, like hitArea in 2D

    Setting it doesn't make the node interactive; eventMode does that. Left null, a Mesh3D uses its geometry's bounding box and a plain Container3D has no shape, so it only hears events from its children.

    A shape you set also clips the node's descendants, as hitArea does in 2D: a descendant only hits where the hit point is inside every such shape above it, whatever those nodes' eventMode. A clipped hit doesn't block what is behind it. A Geometry3D clips by its bounding box, an IHitVolume3D only through its optional containsPoint, and the default bounding box never clips. A ray that misses a Box or Sphere shape, or a Geometry3D's bounding box, skips the exact test of every descendant under it.

    null
    
    mesh.interactive = true;                  // picked by its bounding box
    mesh.hitVolume = 'geometry'; // picked per triangle
    mesh.hitVolume = new Sphere(center, 0.4); // a cheaper or tighter shape
    mesh.hitVolume = null; // back to the bounding box

    // Children of the porthole only pick inside its sphere
    porthole.hitVolume = new Sphere(new Vector3(), 1);

    HitVolume3DInput For what each accepted value resolves to.

    interactive: boolean

    Shorthand for eventMode, as on a pixi.js Container

    true sets 'static' and false sets 'passive'. It reads true for 'static' and 'dynamic'. false doesn't stop picking: under an interactive ancestor, a 'passive' node is still picked on that ancestor's behalf. Set eventMode = 'none' to take a node and its subtree out of picking.

    mesh.interactive = true;  // eventMode = 'static'
    mesh.interactive = false; // eventMode = 'passive'
    interactiveChildren: boolean

    Whether this node's descendants can be picked, as on a pixi.js Container

    false takes the whole subtree out of picking: descendants neither hit nor block what is behind them, and cost nothing per pointer event. The node itself still picks by its own eventMode and volume, so it can answer for the subtree.

    true
    
    menu.interactiveChildren = false; // nothing in the menu can be picked
    menu.interactiveChildren = true; // every button picks again, eventMode intact

    // One sphere answers for the whole robot: one ray test instead of one per mesh
    robot.eventMode = 'static';
    robot.hitVolume = new Sphere(new Vector3(), 2);
    robot.interactiveChildren = false;
    label: string | null = null

    Name to find the node by, with getChildByLabel

    Animation tracks find their target by it too. A plain Container3D starts with null; built-in subclasses such as Mesh3D start with their class name.

    turret.label = 'turret';
    tank.getChildByLabel('turret', true)?.lookAt(target);
    localTransform: Matrix4 = ...

    Local transform matrix, built from the position, rotation, scale and origin

    It catches up with changes in the view's transform pass, or when you call updateLocalTransform. Read it, but change the components instead: the next rebuild overwrites it.

    onclick: Pointer3DEventHandler | null

    Property-based handler for the click event

    Fired when a mouse click on this node completes, with any button but the right one.

    null
    
    mesh.eventMode = 'static';
    mesh.onclick = (e) => {
    if (e.hit3D) marker.position.copyFrom(e.hit3D.point);
    };

    IFederatedContainer3D#onpointertap For the equivalent for every pointer type.

    oncontextmenu: Pointer3DEventHandler | null

    Property-based handler for the contextmenu event

    Fired when the browser is about to open its context menu over this node; call preventDefault() to keep it closed.

    null
    
    mesh.oncontextmenu = (e) => {
    e.preventDefault();
    openMenuFor(mesh, e.global);
    };
    onglobalmousemove: Pointer3DEventHandler | null

    Property-based handler for the globalmousemove event

    Fired on every mouse move, wherever the pointer is on the page.

    null
    
    mesh.onglobalmousemove = (e) => {
    if (dragging) dragTo(e.global);
    };

    IFederatedContainer3D#onglobalpointermove For the equivalent for every pointer type.

    onglobalpointermove: Pointer3DEventHandler | null

    Property-based handler for the globalpointermove event

    Fired on every pointer move on the page, whether or not the pointer is over this node. The node still needs eventMode 'static' or 'dynamic' to hear it.

    null
    
    // Keep a drag going once the pointer runs off the dragged mesh
    mesh.onglobalpointermove = (e) => {
    const hit = dragging && view3d.screenToWorld(e.global, ground);
    if (hit) mesh.position.copyFrom(hit);
    };
    onglobaltouchmove: Pointer3DEventHandler | null

    Property-based handler for the globaltouchmove event

    Fired on every touch move, wherever the touch is on the page.

    null
    
    mesh.onglobaltouchmove = (e) => {
    if (dragging) dragTo(e.global);
    };

    IFederatedContainer3D#onglobalpointermove For the equivalent for every pointer type.

    onmousedown: Pointer3DEventHandler | null

    Property-based handler for the mousedown event

    Fired when a mouse button other than the right one goes down over this node.

    null
    
    mesh.onmousedown = (e) => {
    if (e.button === 1) startPan(e.global); // middle button
    };

    IFederatedContainer3D#onpointerdown For the equivalent for every pointer type.

    onmouseenter: Pointer3DEventHandler | null

    Property-based handler for the mouseenter event

    Fired when the mouse enters this node. Doesn't bubble.

    null
    
    mesh.onmouseenter = () => (tooltip.visible = true);
    

    IFederatedContainer3D#onpointerenter For the equivalent for every pointer type.

    onmouseleave: Pointer3DEventHandler | null

    Property-based handler for the mouseleave event

    Fired when the mouse leaves this node. Doesn't bubble.

    null
    
    mesh.onmouseleave = () => (tooltip.visible = false);
    

    IFederatedContainer3D#onpointerleave For the equivalent for every pointer type.

    onmousemove: Pointer3DEventHandler | null

    Property-based handler for the mousemove event

    Fired while the mouse moves across this node.

    null
    
    mesh.onmousemove = (e) => tooltip.position.copyFrom(e.global);
    

    IFederatedContainer3D#onpointermove For the equivalent for every pointer type.

    onmouseout: Pointer3DEventHandler | null

    Property-based handler for the mouseout event

    Fired when the mouse moves off this node. Bubbles, unlike mouseleave.

    null
    
    shelf.onmouseout = (e) => unhighlight(e.target3D);
    

    IFederatedContainer3D#onpointerout For the equivalent for every pointer type.

    onmouseover: Pointer3DEventHandler | null

    Property-based handler for the mouseover event

    Fired when the mouse moves onto this node. Bubbles, unlike mouseenter.

    null
    
    // One handler on the shelf hears every item on it
    shelf.onmouseover = (e) => highlight(e.target3D);

    IFederatedContainer3D#onpointerover For the equivalent for every pointer type.

    onmouseup: Pointer3DEventHandler | null

    Property-based handler for the mouseup event

    Fired when a mouse button other than the right one is released over this node.

    null
    
    mesh.onmouseup = (e) => {
    if (e.button === 1) endPan();
    };

    IFederatedContainer3D#onpointerup For the equivalent for every pointer type.

    onmouseupoutside: Pointer3DEventHandler | null

    Property-based handler for the mouseupoutside event

    Fired when a mouse button that went down on this node is released anywhere else.

    null
    
    mesh.onmouseupoutside = () => endPan();
    

    IFederatedContainer3D#onpointerupoutside For the equivalent for every pointer type.

    onpointercancel: Pointer3DEventHandler | null

    Property-based handler for the pointercancel event

    Never called: pixi delivers no pointer cancellation, in 2D either.

    null
    
    // End a drag on these two instead
    handle.onpointerup = endDrag;
    handle.onpointerupoutside = endDrag;
    onpointerdown: Pointer3DEventHandler | null

    Property-based handler for the pointerdown event

    Fired when a pointer goes down on this node: mouse, touch or pen alike.

    null
    
    handle.eventMode = 'static';
    handle.onpointerdown = (e) => {
    if (!e.hit3D) return;
    dragOffset.copyFrom(e.hit3D.localPoint); // where on the handle it was grabbed
    dragging = true;
    };
    onpointerenter: Pointer3DEventHandler | null

    Property-based handler for the pointerenter event

    Fired when a pointer enters this node. Doesn't bubble, so an ancestor hears it only when the pointer enters the ancestor itself.

    null
    
    button.eventMode = 'static';
    button.onpointerenter = () => button.scale.set(1.1);
    onpointerleave: Pointer3DEventHandler | null

    Property-based handler for the pointerleave event

    Fired when a pointer leaves this node. Doesn't bubble.

    null
    
    button.onpointerleave = () => button.scale.set(1);
    
    onpointermove: Pointer3DEventHandler | null

    Property-based handler for the pointermove event

    Fired while a pointer moves across this node.

    null
    
    // Trail the cursor across the surface it is over
    table.eventMode = 'static';
    table.onpointermove = (e) => {
    if (e.hit3D) cursorMarker.position.copyFrom(e.hit3D.point);
    };
    onpointerout: Pointer3DEventHandler | null

    Property-based handler for the pointerout event

    Fired when a pointer moves off this node. Bubbles, unlike pointerleave.

    null
    
    shelf.onpointerout = (e) => unhighlight(e.target3D);
    
    onpointerover: Pointer3DEventHandler | null

    Property-based handler for the pointerover event

    Fired when a pointer moves onto this node. Bubbles, unlike pointerenter.

    null
    
    // One handler on the shelf hears every item on it
    shelf.onpointerover = (e) => highlight(e.target3D);
    onpointertap: Pointer3DEventHandler | null

    Property-based handler for the pointertap event

    Fired when a pointer completes a tap on this node, for any pointer type and any button, right clicks included.

    null
    
    // Works for mouse, touch and pen alike
    mesh.eventMode = 'static';
    mesh.onpointertap = (e) => select(e.target3D);
    onpointerup: Pointer3DEventHandler | null

    Property-based handler for the pointerup event

    Fired when a pointer is released over this node.

    null
    
    handle.onpointerup = () => {
    dragging = false;
    };
    onpointerupoutside: Pointer3DEventHandler | null

    Property-based handler for the pointerupoutside event

    Fired when a pointer that went down on this node is released anywhere else. The event's target3D is the node that was pressed, and hit3D is what the pointer was released over.

    null
    
    // The release that ends a drag usually happens off the mesh
    handle.onpointerupoutside = () => {
    dragging = false;
    };
    onrightclick: Pointer3DEventHandler | null

    Property-based handler for the rightclick event

    Fired when a right click completes on this node.

    null
    
    mesh.onrightclick = () => openMenuFor(mesh);
    
    onrightdown: Pointer3DEventHandler | null

    Property-based handler for the rightdown event

    Fired when the right mouse button goes down over this node.

    null
    
    mesh.onrightdown = (e) => startRotate(e.global);
    
    onrightup: Pointer3DEventHandler | null

    Property-based handler for the rightup event

    Fired when the right mouse button is released over this node.

    null
    
    mesh.onrightup = endRotate;
    
    onrightupoutside: Pointer3DEventHandler | null

    Property-based handler for the rightupoutside event

    Fired when the right mouse button that went down on this node is released anywhere else.

    null
    
    mesh.onrightupoutside = endRotate;
    
    ontap: Pointer3DEventHandler | null

    Property-based handler for the tap event

    Fired when a touch completes a tap on this node. Chrome gives each touch a new pointerId, and detail only counts repeat taps by the same one, so it stays 1 there; time a double tap yourself.

    null
    
    let lastTap = 0;
    mesh.ontap = (e) => {
    if (e.timeStamp - lastTap < 300) focusOn(mesh); // double tap
    lastTap = e.timeStamp;
    };

    IFederatedContainer3D#onpointertap For the equivalent for every pointer type.

    ontouchcancel: Pointer3DEventHandler | null

    Property-based handler for the touchcancel event

    Never called: pixi delivers no touch cancellation, in 2D either.

    null
    
    // End a drag on these two instead
    handle.ontouchend = endDrag;
    handle.ontouchendoutside = endDrag;
    ontouchend: Pointer3DEventHandler | null

    Property-based handler for the touchend event

    Fired when a touch lifts off this node.

    null
    
    handle.ontouchend = endDrag;
    

    IFederatedContainer3D#onpointerup For the equivalent for every pointer type.

    ontouchendoutside: Pointer3DEventHandler | null

    Property-based handler for the touchendoutside event

    Fired when a touch that started on this node lifts off anywhere else.

    null
    
    handle.ontouchendoutside = endDrag;
    

    IFederatedContainer3D#onpointerupoutside For the equivalent for every pointer type.

    ontouchmove: Pointer3DEventHandler | null

    Property-based handler for the touchmove event

    Fired while a touch moves across this node.

    null
    
    canvasMesh.hitVolume = 'geometry'; // per triangle, so hits carry a uv
    canvasMesh.ontouchmove = (e) => {
    if (e.hit3D) paintAt(e.hit3D.uv);
    };

    IFederatedContainer3D#onpointermove For the equivalent for every pointer type.

    ontouchstart: Pointer3DEventHandler | null

    Property-based handler for the touchstart event

    Fired when a touch lands on this node.

    null
    
    handle.ontouchstart = (e) => startDrag(e.pointerId);
    

    IFederatedContainer3D#onpointerdown For the equivalent for every pointer type.

    onwheel: Wheel3DEventHandler | null

    Property-based handler for the wheel event

    Fired when the wheel turns while the pointer is over this node.

    null
    
    // Scroll to push the panel away from the camera
    panel.eventMode = 'static';
    panel.onwheel = (e) => {
    panel.position.z += e.deltaY * 0.01;
    };
    parent: Container3D | null = null

    Parent node, or null for a node with none, such as a view's root

    uid: number = ...

    Unique id, assigned at construction

    worldTransform: Matrix4 = ...

    World transform: the parent's world transform times this node's localTransform

    The view updates it once per frame, before drawing, for visible nodes only, and not at all while the view holds no mesh. So it can be out of date after a change this frame or while the node or an ancestor is hidden, and a node outside any view keeps the identity matrix. getGlobalTransform always works out a current one.

    • get angle(): PointLike3D

      rotation in degrees, as in pixi.js

      It reads and writes rotation, so the two always agree. Reading converts back from radians, so a value can come back a hair off, such as 30 as 29.999999999999996; compare with a tolerance.

      Returns PointLike3D

      node.angle.y = 90; // same as node.rotation.y = Math.PI / 2
      node.angle = { x: 0, y: 45, z: 0 };
    • set angle(value: PointData3D): void

      Sets the Euler rotation and makes it the rotation in use, even when the values are unchanged

      Parameters

      Returns void

    • get cull(): CullMode

      Frustum-culling mode for this batch, which ignores the view's own cull

      Off by default, since a batch usually sits mostly in view. Turn it on for a large batch spread well beyond the view, such as scattered scenery. Once on, it tests every slot each frame, and again for each shadow map that redraws, even with the whole batch in view; where the test runs on the CPU, as under WebGL2, that cost grows with the slot count. A change takes effect on the next render.

      Returns CullMode

      // ten thousand trees across the map, most of them off screen: test each one
      forest.cull = 'gpu';

      CullMode For the modes.

    • set cull(value: CullMode): void

      Sets this batch's frustum-culling mode

      Parameters

      Returns void

    • get depth(): number

      Depth along local z: |scale.z| × local bounds depth

      Assigning writes scale.z only; see width.

      Returns number

      bench.depth = 0.5;
      
    • set depth(value: number): void

      Scales along z so the depth becomes value

      Parameters

      • value: number

        Target depth along z

      Returns void

    • get height(): number

      Height along local y: |scale.y| × local bounds height

      Assigning writes scale.y only; see width.

      Returns number

      tree.height = 6;
      
    • set height(value: number): void

      Scales along y so the height becomes value

      Parameters

      • value: number

        Target height along y

      Returns void

    • get instanceData(): BatchInstanceData
      Advanced

      The slots' instance data as interleaved data, for rewriting many slots a frame

      BatchInstanceData has the layout and the rules:

      const { data, stride, offset, fields } = boxes.instanceData;

      for (let i = 0, o = offset + fields.tint; i < boxes.size; i++, o += stride) {
      data[o + 3] = fade[i];
      }

      boxes.instanceData.update();

      Throws when the batch was built without the instanceData option.

      Returns BatchInstanceData

    • get items(): readonly BatchedMesh3DSlot[]

      The slots, one per index

      Set a slot's geometry, material and matrix through the set*At methods. A slot's billboardMode and receiveShadow are set here, followed by invalidate.

      Returns readonly BatchedMesh3DSlot[]

      batched.items[0].billboardMode = 'spherical';
      batched.invalidate(0);
    • get matrices(): BatchMatrices
      Advanced

      The slot matrices as interleaved data, for rewriting many slots a frame without a copy each

      BatchMatrices has the layout and the rules:

      const { data, stride, offset } = batched.matrices;

      for (let i = 0, o = offset; i < batched.size; i++, o += stride) {
      data[o + 13] = heights[i]; // translation Y
      }

      batched.matrices.update();

      Returns BatchMatrices

    • get origin(): ObservableVector3

      Point in local space that rotation and scale happen around, as in pixi.js

      Unlike a pivot, it leaves an unrotated, unscaled node where it is: the origin sits at position + origin in the parent. Assigning copies the values in, and a single number sets all three axes.

      Returns ObservableVector3

      // Tip a crate over its bottom-right edge without moving it first.
      crate.origin = { x: 0.5, y: -0.5, z: 0 };
      crate.angle.z = -90;
    • set origin(value: number | PointData3D): void

      Sets the origin; a number sets all three axes

      Parameters

      • value: number | PointData3D

        The new origin, in local space

      Returns void

    • get quaternion(): ObservableQuaternion

      Local rotation as a unit quaternion; see rotation for which of the two is in use

      Assigning copies the values in, so node.quaternion = other.quaternion doesn't link the two.

      Returns ObservableQuaternion

      node.quaternion = other.quaternion;
      node.quaternion = { x: 0, y: Math.SQRT1_2, z: 0, w: Math.SQRT1_2 };
    • set quaternion(value: QuaternionData): void

      Sets the quaternion and makes it the rotation in use, even when the values are unchanged

      Parameters

      Returns void

    • get rotation(): ObservableVector3

      Local rotation as Euler angles in radians: turns around Z, then Y, then X, each about the parent's axes

      The node keeps both this and quaternion, and whichever you set last is the rotation in use. Assigning copies the values in. Writing unchanged values to either through a component, set or copyFrom doesn't switch which one is in use; assign it whole.

      Returns ObservableVector3

      node.rotation.y = Math.PI / 2;
      node.rotation = { x: 0, y: Math.PI / 2, z: 0 };
    • set rotation(value: PointData3D): void

      Sets the Euler rotation and makes it the rotation in use, even when the values are unchanged

      Parameters

      Returns void

    • get scale(): ObservableVector3

      Local scale

      Assigning copies the values in, and a single number scales all three axes; the object itself never changes.

      Returns ObservableVector3

      node.scale.set(2, 1, 1);
      node.scale = 2; // uniform
    • set scale(value: number | PointData3D): void

      Sets the local scale; a number sets all three axes

      Parameters

      Returns void

    • get size(): number

      Number of slots, fixed at construction

      Returns number

      for (let i = 0; i < batched.size; i++) batched.setMatrixAt(i, matrices[i]);
      
    • get visible(): boolean

      Whether this container and its descendants draw

      Hiding is cheap, and a hidden node stays in the scene; to take it out, remove it with removeChild. Emits visibleChanged when the value changes.

      Returns boolean

      door.visible = false;
      
    • set visible(value: boolean): void

      Shows or hides this container and its descendants

      Parameters

      • value: boolean

        Whether they draw

      Returns void

    • get width(): number

      Width along local x: |scale.x| × local bounds width

      Assigning writes scale.x only. To set several axes, setSize measures the bounds once instead of once per axis.

      Returns number

      crate.width = 2;
      
    • set width(value: number): void

      Scales along x so the width becomes value

      Parameters

      • value: number

        Target width along x

      Returns void

    • get worldVisible(): number

      1 when this node and every ancestor are visible, otherwise 0

      The view works it out once per frame, so it lags a visible change until the next render.

      Returns number

    • get x(): number

      Local position on the x axis; the same as position.x

      Returns number

      player.x += speed * dt;
      
    • set x(value: number): void

      Sets position.x

      Parameters

      • value: number

        The new x position

      Returns void

    • get y(): number

      Local position on the y axis; the same as position.y

      Returns number

      ball.y = Math.abs(Math.sin(time)) * 2;
      
    • set y(value: number): void

      Sets position.y

      Parameters

      • value: number

        The new y position

      Returns void

    • get z(): number

      Local position on the z axis; the same as position.z

      Returns number

      if (car.z > finishLine) finish();
      
    • set z(value: number): void

      Sets position.z

      Parameters

      • value: number

        The new z position

      Returns void

    • Adds one or more children to the end of this container's child list

      A child with another parent leaves it first. A child that is already here moves to the end, without events.

      Type Parameters

      Parameters

      • ...children: U

        The children to add

      Returns U[0]

      The first added child

      view.root.addChild(crate, lamp);
      
    • Adds child at index, or moves it there when it is already a child of this container

      A move within this container emits no events.

      Type Parameters

      Parameters

      • child: U

        The child to add or move

      • index: number

        The destination index

      Returns U

      The added child

      rack.addChildAt(sword, 0);
      

      When index is out of bounds.

    • Destroys this node and its batch

      The batch is always destroyed. Slot geometries and materials are often shared, so they stay unless you pass geometry or material; each is destroyed once, however many slots use it. texture then frees the destroyed materials' textures, and textureSource their sources.

      Parameters

      Returns void

      crowd.destroy({ geometry: true, material: true });
      
    • Gets the child at index

      Type Parameters

      Parameters

      • index: number

        Index of the child to fetch

      Returns U

      The child at index

      const first = group.getChildAt(0);
      

      When index is out of bounds.

    • Returns the first child whose label matches, or null

      Each container checks its direct children before searching inside them. An unlabelled node never matches, even a pattern.

      Parameters

      • label: string | RegExp

        Exact label, or a pattern to test labels against

      • deep: boolean = false

        Whether to search the whole subtree, not only direct children

      Returns Container3D | null

      The first match, or null when nothing matches

      const wheel = car.getChildByLabel('wheel_front_left', true);
      const anyWheel = car.getChildByLabel(/^wheel_/, true);
    • Gets the index of child within this container

      Parameters

      Returns number

      The child's index

      const slot = rack.getChildIndex(sword);
      

      When child is not a child of this container.

    • Collects every child whose label matches, in the order Container3D.getChildByLabel visits them

      Parameters

      • label: string | RegExp

        Exact label, or a pattern to test labels against

      • deep: boolean = false

        Whether to search the whole subtree, not only direct children

      • out: Container3D[] = []

        Array the matches are appended to; a new one when omitted

      Returns Container3D[]

      out

      const wheels = car.getChildrenByLabel(/^wheel_/, true);
      
    • Returns this container's position in world space: parent.toGlobal(this.position), or a copy of position with no parent

      Parameters

      • out: Vector3 = ...

        Point the result is written into. Defaults to a new Vector3.

      • skipUpdate: boolean = false

        true to use the parent's cached world transform (see Container3D.getGlobalTransform); this node's own position is always read as it stands.

      Returns Vector3

      const screenPos = view3d.worldToScreen(mesh.getGlobalPosition());
      
    • Works out this container's current world transform into out

      It walks up the parent chain, so the result is right even before the view's transform pass has run this frame. With skipUpdate it copies the cached worldTransform instead, which is cheaper but can be stale.

      Parameters

      • out: Matrix4 = ...

        Matrix the result is written into. Defaults to a new Matrix4.

      • skipUpdate: boolean = false

        true to copy the cached world transform instead of recomputing.

      Returns Matrix4

      const world = mesh.getGlobalTransform();
      const localRay = ray.applyMatrix4(world.getInverse(world), new Ray());
    • Measures the bounding box of this container and its descendants, in this container's local space

      A subtree with a boundsVolume counts as that box. Hidden descendants are left out; this container counts whether or not it is visible. An empty subtree measures as a zero box at the origin. Each call walks the subtree, so cache the result if you need it every frame.

      Parameters

      • out: Box = ...

        Box to write into; a new one when omitted

      Returns Box

      out

      const bounds = model.getLocalBounds();
      const center = bounds.getCenter();
    • Returns the scaled size of this container, measuring the local bounds once

      Parameters

      • out: Size3D = ...

        Object to write into; a new one when omitted

      Returns Size3D

      The size, written into out

      const { width, height, depth } = model.getSize();
      
    • Whether a slot is shown, as setVisibleAt left it

      A slot in no pass can still read true.

      Parameters

      • index: number

        The slot index within this batch

      Returns boolean

      false while the slot is hidden

      for (let i = 0; i < bullets.size; i++) {
      if (bullets.getVisibleAt(i)) continue;
      bullets.setMatrixAt(i, muzzleMatrix);
      bullets.setVisibleAt(i, true);
      break;
      }
    • Makes a slot rebuild its shader and draw state on the next frame

      Swapping a slot's geometry or material does this for you. A change to the shader or render state of a material slots already use doesn't, such as a texture swap, a new alphaMode or a state field (BaseMaterial3D lists them), so call this for each of those slots afterwards, after changing a slot's billboardMode or receiveShadow, and after unloading a slot's geometry to apply an edit. A slot's passes don't follow its material, so when alphaMode switches to or from 'blend', also move the slot with setPassesAt: RenderPassBit.TRANSPARENT to blend, RenderPassBit.OPAQUE to stop. Values such as baseColor, and the view-wide toneMapping, fog, environment and shadowFilter, reach every slot on their own.

      Parameters

      • index: number

        The slot index within this batch

      Returns void

      leafMaterial.textures.baseColor = autumnLeaves;
      for (const i of leafSlots) trees.invalidate(i);
    • Whether this node fires listeners and can be an event target

      True for eventMode 'static' and 'dynamic', as in 2D.

      Returns boolean

      mesh.eventMode = 'dynamic';
      mesh.isInteractive(); // true

      mesh.eventMode = 'auto';
      mesh.isInteractive(); // false: hit-tested, but never the target
    • Advanced

      Prints this container's subtree to the console as an indented tree of ConstructorName "label" lines, for debugging

      Parameters

      • depth: number = 0

        Indentation level to start at; used by the recursion

      Returns void

      model.logHierarchy();
      
    • Turns this node so its forward axis, local +Z, points at a world-space target

      Cameras and lights aim local -Z instead. The node aims from position + origin, and uses the parent's current world transform, so a move earlier in the frame counts.

      Parameters

      • target: PointData3D

        World-space point to face

      • up: PointData3D = _lookAtDefaultUp

        World-space up reference; defaults to (0, 1, 0)

      Returns void

      turret.lookAt(enemy.getGlobalPosition());
      
    • Removes one or more children from this container; a node that isn't a child is ignored

      Type Parameters

      Parameters

      • ...children: U

        The children to remove

      Returns U[0]

      The first child passed in

      view.root.removeChild(crate);
      
    • Removes the child at index

      Type Parameters

      Parameters

      • index: number

        Index of the child to remove

      Returns U

      The removed child

      const newest = queue.removeChildAt(queue.children.length - 1);
      

      When index is out of bounds.

    • Removes the children from beginIndex up to, but not including, endIndex

      Parameters

      • beginIndex: number = 0

        First index to remove

      • OptionalendIndex: number

        End of the range, exclusive; defaults to the child count

      Returns Container3D[]

      The removed children, last child first

      const debris = level.removeChildren();
      

      When beginIndex is negative or past endIndex, or equal to it while this container has children.

    • Removes this container from its parent, if it has one

      Returns void

      pickup.removeFromParent();
      
    • Moves one or more nodes under this container without moving them in the world; see reparentChildAt

      Type Parameters

      Parameters

      • ...child: U

        The children to reparent

      Returns U[0]

      The first reparented child

      hand.reparentChild(sword);
      
    • Moves child under this container at index without moving it in the world

      Its local position, rotation and scale change so that it renders where it did, and it keeps its origin. Unlike pixi, it uses current world transforms, so a move earlier in the frame counts. A child that is already here only changes index.

      Type Parameters

      Parameters

      • child: U

        The child to reparent

      • index: number

        The destination index

      Returns U

      The reparented child

      rack.reparentChildAt(sword, 0);
      
    • Replaces oldChild with newChild at the same index, copying oldChild's local position, rotation and scale onto newChild

      newChild keeps its own origin, and its position is solved so it sits exactly where oldChild did.

      Type Parameters

      Parameters

      • oldChild: U

        The child to replace

      • newChild: T

        The child that takes its place

      Returns void

      car.replaceChild(flatTyre, spareWheel);
      

      When oldChild is not a child of this container.

    • Moves an existing child to index within this container

      Parameters

      • child: Container3D

        The child to move

      • index: number

        The destination index

      Returns void

      rack.setChildIndex(sword, 0);
      

      When index is out of bounds or child is not a child of this container.

    • Sets a slot's geometry; the slot rebuilds its shader on the next frame

      Parameters

      • index: number

        The slot index within this batch

      • geometry: Geometry3D

        The slot's geometry

      Returns void

      forest.setGeometryAt(i, i % 3 === 0 ? pineGeometry : oakGeometry);
      
    • Advanced

      Writes one instance-data field of the item at index

      Allocates nothing, and the next render uploads it. Throws when the batch declares no field of that name.

      Parameters

      • index: number

        The slot index within this batch

      • name: string

        The field, as declared in the instanceData option

      • x: number

        The field's first number

      • y: number

        The field's second number

      • z: number

        The field's third number

      • w: number

        The field's fourth number

      Returns void

      boxes.setInstanceDataAt(i, 'tint', 1, 0.2, 0.2, 1);
      

      BatchedMesh3D.instanceData For rewriting many slots a frame.

    • Sets a slot's material; the slot rebuilds its shader on the next frame

      The slot stays in the passes setPassesAt gave it, so move it there when the new material blends and the old one didn't, or the other way round.

      Parameters

      • index: number

        The slot index within this batch

      • material: BaseMaterial3D

        The slot's material

      Returns void

      crowd.setMaterialAt(i, team === 'red' ? redShirt : blueShirt);
      
    • Sets a slot's matrix, relative to this node

      It copies the matrix and allocates nothing, so it's fine for every slot every frame. The matrix must be affine, with a bottom row of 0, 0, 0, 1. To skip the copy when rewriting many slots a frame, write into matrices.

      Parameters

      • index: number

        The slot index within this batch

      • matrix: Matrix4

        The slot's matrix

      Returns void

      batched.setMatrixAt(i, matrix.makeTranslation(x, 0, z));
      
    • Sets which passes draw a slot; 0 takes it out of every pass

      Pass a bitwise OR of RenderPassBit flags. A slot doesn't pick its passes from its material the way a Mesh3D does, so match them yourself: OPAQUE for an opaque or 'mask' material, TRANSPARENT for a 'blend' one, TRANSMISSION in place of OPAQUE for a Material3D with transmission, plus SHADOW to cast shadows. No flag joins a custom MeshPass, such as an outline.

      A change costs some work on the next frame in each pass it touches, and a slot that joins a pass rebuilds its shader. To hide and show slots, use setVisibleAt, which rebuilds nothing.

      Parameters

      • index: number

        The slot index within this batch

      • passMask: number

        Bitmask of RenderPassBit flags for this slot

      Returns void

      batched.setPassesAt(i, RenderPassBit.OPAQUE | RenderPassBit.SHADOW);
      batched.setPassesAt(j, 0);
    • Rescales this container so its size matches the given width, height and depth

      A missing height or depth takes the width. An axis whose local extent is zero, such as a flat plane's depth, keeps a unit scale. The sign of each scale axis is kept, so a mirrored node stays mirrored.

      Parameters

      • value: number | Pick<Size3D, "width"> & Partial<Size3D>

        Target width, or an object with width and optional height / depth

      • Optionalheight: number

        Target height; defaults to the width

      • Optionaldepth: number

        Target depth; defaults to the width

      Returns void

      model.setSize(2); // stretch each axis to 2
      model.setSize(4, 2, 1);
      model.setSize({ width: 4, height: 2 }); // depth follows width: 4
    • Shows or hides a slot, the way Container3D#visible does for a mesh

      A hidden slot keeps its geometry, material, matrix and passes, and draws nothing, shadows included. Every slot starts visible. A flip rebuilds nothing, which makes this the way to spawn and despawn: size the batch for the most slots you'll use and hide the spare ones. With cull off, the next frame after a flip filters the whole batch, at a cost set by the batch's size, not the number of flips.

      Blended slots are the exception: the TRANSPARENT pass sorts hidden slots too, every frame. Take a blended slot that stays spare for a while out of the pass with setPassesAt, which costs one rebuild, and keep hiding one that flips often.

      Parameters

      • index: number

        The slot index within this batch

      • visible: boolean

        Whether the slot draws

      Returns void

      bullets.setVisibleAt(i, false); // despawn
      bullets.setVisibleAt(j, true); // spawn
    • Swaps the places of two children in this container's list

      Type Parameters

      Parameters

      • child: U

        The first child

      • child2: U

        The second child

      Returns void

      rack.swapChildren(sword, shield);
      

      When either argument is not a child of this container.

    • Converts a point into this container's local space

      Type Parameters

      Parameters

      • position: PointData3D

        The point to convert.

      • Optionalfrom: Container3D

        Container whose local space position is in. Defaults to world space.

      • Optionalout: T

        Point the result is written into. Defaults to a new Vector3.

      • skipUpdate: boolean = false

        true to use cached world transforms (see Container3D.getGlobalTransform).

      Returns T

      const targetFromTurret = turret.toLocal({ x: 0, y: 0, z: 0 }, target);
      
    • Calls callback with this container, then with every container under it, depth first

      Each container comes before its children, and hidden ones are visited too. A Skeleton's bones and a BatchedMesh3D's items aren't containers, so the walk doesn't reach them. It can't stop early or skip a subtree.

      The callback may remove, destroy or replace the container it's given. A container it adds with addChild is visited only if its new parent hasn't been visited yet. Other changes to the tree during the walk can skip a container or visit one twice.

      Parameters

      • callback: (container: Container3D) => void

        Called once with each container, this one first

      Returns void

      const robot = new Model3D(source);

      robot.traverse((container) => {
      if (container instanceof Mesh3D) container.enablePass('outline');
      });
    • Frees this batch's GPU memory and unloads its slots' geometries with Geometry3D.unload; the next render rebuilds them and draws the same image

      Slot materials are left alone, as Mesh3D.unload leaves them; call BaseMaterial3D.unload to free their textures. A batch taken out of the scene keeps its GPU memory until this is called or it's destroyed, so adding it back before then is cheap.

      Returns void

      view.root.removeChild(crowd);
      crowd.unload(); // hand back the crowd's GPU memory until it is shown again

      BatchedMesh3D.destroy For permanent teardown.

    • Advanced

      Rebuilds localTransform from the position, rotation, scale and origin

      The view calls it for each node that changed, so you only need it to read a current localTransform before the next frame. When rotation is the rotation in use, it updates the quaternion to match.

      Returns void

      crate.rotation.y = Math.PI / 4;
      crate.updateLocalTransform();
      const local = crate.localTransform.clone();