IndirectBatchedMesh

@three-blocks/coreWebGPU
new IndirectBatchedMesh()
Extends
THREE.Mesh

WebGPU-first batched mesh using indirect instanced rendering with GPU-driven culling.

Architecture Overview

  • Packs multiple geometries into a single BufferGeometry with indirect draw commands
  • Always uses multi-draw indirect with firstInstance offsets (even without GPU culling)
  • Stores per-instance data in GPU StorageBufferAttributes (transforms, colors, geometry IDs)
  • Delegates culling/sorting to compute pipelines (e.g., ComputeBatchCulling)
  • Supports optional internal culling or external compute pipeline attachment

Limitations

  • Transparency is not supported: This mesh class is designed for opaque materials only. Transparent materials will not render correctly due to the lack of depth sorting. Use standard Mesh or InstancedMesh with ComputeInstanceCulling for transparent objects.

Culling Flow

  1. ComputeBatchCulling evaluates visibility/LOD and compacts survivor IDs
  2. Mesh reads from buffers via setIndirect() and setSurvivorIdBuffer()
  3. Renderer draws using buffers while compute updates for next frame

Buffer Roles

  • matricesSB: Authoritative per-instance transforms
  • survivorIdSB: Maps instanceIndex → original instance ID after culling
  • geometryIdSB: Identifies which geometry segment each instance uses
  • indirect: Controls final instanceCount for GPU draw

Example: Basic setup with multiple geometries

See also
Example
import { IndirectBatchedMesh, indirectBatch } from '@three-blocks/core';
import * as THREE from 'three/webgpu';

// Create batched mesh: 10000 instances, 50000 vertices, 100000 indices
const material = new THREE.MeshStandardNodeMaterial({ color: 0x4488ff });
const mesh = new IndirectBatchedMesh(10000, 50000, 100000, material);
// Add multiple geometry types
const boxId = mesh.addGeometry(new THREE.BoxGeometry(1, 1, 1));
const sphereId = mesh.addGeometry(new THREE.SphereGeometry(0.5, 16, 16));
const coneId = mesh.addGeometry(new THREE.ConeGeometry(0.5, 1, 8));

// Scatter instances of different geometries
const matrix = new THREE.Matrix4();
const geometries = [boxId, sphereId, coneId];

for (let i = 0; i < 5000; i++) {
  const geomId = geometries[i % 3];
  const instanceId = mesh.addInstance(geomId);

  matrix.makeTranslation(
    (Math.random() - 0.5) * 200,
    (Math.random() - 0.5) * 50,
    (Math.random() - 0.5) * 200
  );
  mesh.setMatrixAt(instanceId, matrix);
}

scene.add(mesh);

Properties

.frustumCulled : boolean

Disable frustum culling for the indirect batched mesh.
Default is false.

.perObjectFrustumCulled : boolean

Enable per-instance frustum culling when using internal culling.
Default is true.

.maxInstanceCount : number

Maximum instance capacity.

.instanceCount : number

Current number of active instances.

.unusedVertexCount : number

Remaining vertex capacity.

.unusedIndexCount : number

Remaining index capacity.

Methods

beginBulkUpdate#

beginBulkUpdate() : this

Defer CPU indirect rebuilds while performing many mutations (add/remove instances). Call endBulkUpdate() to rebuild once after the batch.

Returns
this

endBulkUpdate#

endBulkUpdate(rebuild : boolean) : this

Resume CPU indirect rebuilds after beginBulkUpdate().

Parameters
rebuildoptionalboolean
Rebuild immediately when pending.
Default is true.
Returns
this

enableWorkgroupIndirect#

enableWorkgroupIndirect(workgroupSize : number) : THREE.IndirectStorageBufferAttribute

Optional helper: allocate and update an indirect workgroup buffer for compute passes. Writes ceil(instanceCount / workgroupSize) into element 0 each frame.

Parameters
workgroupSizeoptionalnumber
Workgroup size used by your compute shader.
Default is 64.
Returns
THREE.IndirectStorageBufferAttribute — Indirect buffer with [x=workgroups, y=1, z=1].

enableInternalCulling#

enableInternalCulling(renderer : THREE.WebGPURenderer) : this

Enable built-in compute-based culling using ComputeBatchCulling. Automatically creates and attaches a culling pipeline.

Parameters
rendererTHREE.WebGPURenderer
WebGPU renderer instance.
Returns
this

setCullingMatrixNode#

setCullingMatrixNode(resolverFn : function|null) : this

Provide a custom mat4 node for culling/sorting (e.g., animation texture).

Parameters
resolverFnfunction | null
Function receiving { i, baseMatrix, gid } and returning a mat4 node.
Returns
this

attachGUI#

attachGUI(folder : Object)

Attach culling controls to a GUI folder. Compatible with lil-gui, dat.gui, and Three.js Inspector.

Parameters
folderObject
GUI folder instance (e.g., from lil-gui or renderer.inspector.createParameters()).

disposeGUI#

disposeGUI()

Detach and destroy the GUI folder.

computeBoundingBox#

computeBoundingBox() : void

Compute the world-space bounding box for all active instances. Updates this.boundingBox.

Returns
void

computeBoundingSphere#

computeBoundingSphere() : void

Compute the world-space bounding sphere for all active instances. Updates this.boundingSphere.

Returns
void

updateInternalCulling#

updateInternalCulling(camera : THREE.Camera) : this

Update the internal culling system for the current frame. Swaps buffers and runs the compute culling pass.

Parameters
cameraoptionalTHREE.Camera
Camera to use for frustum culling.
Returns
this

setSurvivorIdBuffer#

setSurvivorIdBuffer(sbAttr : THREE.StorageBufferAttribute, isInternal : boolean) : this

Attach a survivor ID mapping buffer produced by external compute culling.

Parameters
sbAttrTHREE.StorageBufferAttribute
StorageBufferAttribute (Uint32, itemSize=1).
isInternaloptionalboolean
If true, marks this as an internal buffer (won't disable internal culling).
Default is false.
Returns
this

setDrawFirstInstanceBuffer#

setDrawFirstInstanceBuffer(sbAttr : THREE.StorageBufferAttribute|null) : this

Attach a per-draw firstInstance buffer (geometry offsets) produced by compute culling.

Parameters
sbAttrTHREE.StorageBufferAttribute | null
StorageBufferAttribute (Uint32, itemSize=1).
Returns
this

setIndirect#

setIndirect(indirectAttribute : THREE.IndirectStorageBufferAttribute) : this

Set the indirect draw command buffer for this mesh.

Parameters
indirectAttributeTHREE.IndirectStorageBufferAttribute
Indirect draw args.
Returns
this

setInstanceCount#

setInstanceCount(maxInstanceCount : number)

Resize the instance pool capacity. Preserves existing data and initializes new slots to identity transforms.

Parameters
maxInstanceCountnumber
New maximum instance count.

addInstance#

addInstance(geometryId : number) : number

Add a new instance referencing a previously added geometry.

Parameters
geometryIdnumber
Geometry ID returned by addGeometry().
Returns
number — The new instance ID.

addGeometry#

addGeometry(geometry : THREE.BufferGeometry, reservedVertexCount : number, reservedIndexCount : number) : number

Add a geometry to the batch, allocating space in the merged buffers.

Parameters
geometryTHREE.BufferGeometry
Source geometry to add.
reservedVertexCountoptionalnumber
Vertex capacity to reserve (-1 = exact fit).
Default is -1.
reservedIndexCountoptionalnumber
Index capacity to reserve (-1 = exact fit).
Default is -1.
Returns
number — Geometry ID for creating instances.

setGeometryAt#

setGeometryAt(geometryId : number, geometry : THREE.BufferGeometry) : number

Replace or update geometry data at a specific geometry slot.

Parameters
geometryIdnumber
Target geometry ID.
geometryTHREE.BufferGeometry
New geometry data.
Returns
number — The geometry ID.

deleteGeometry#

deleteGeometry(geometryId : number) : this

Mark a geometry as inactive and delete all associated instances.

Parameters
geometryIdnumber
Geometry ID to delete.
Returns
this

deleteInstance#

deleteInstance(instanceId : number) : this

Mark an instance as inactive and return its slot to the pool.

Parameters
instanceIdnumber
Instance ID to delete.
Returns
this

optimize#

optimize() : this

Defragment geometry data by compacting active geometry segments. Reduces gaps and improves memory layout.

Returns
this

getBoundingBoxAt#

getBoundingBoxAt(geometryId : number, target : THREE.Box3) : THREE.Box3|null

Get the bounding box for a specific geometry.

Parameters
geometryIdnumber
Target geometry ID.
targetTHREE.Box3
Box3 to store the result.
Returns
THREE.Box3 | null — The bounding box or null if invalid.

getBoundingSphereAt#

getBoundingSphereAt(geometryId : number, target : THREE.Sphere) : THREE.Sphere|null

Get the bounding sphere for a specific geometry.

Parameters
geometryIdnumber
Target geometry ID.
targetTHREE.Sphere
Sphere to store the result.
Returns
THREE.Sphere | null — The bounding sphere or null if invalid.

setMatrixAt#

setMatrixAt(instanceId : number, matrix : THREE.Matrix4) : this

Set the transform matrix for an instance.

Parameters
instanceIdnumber
Target instance ID.
matrixTHREE.Matrix4
Transform matrix to apply.
Returns
this

getMatrixAt#

getMatrixAt(instanceId : number, matrix : THREE.Matrix4) : THREE.Matrix4

Get the transform matrix for an instance.

Parameters
instanceIdnumber
Target instance ID.
matrixTHREE.Matrix4
Matrix to store the result.
Returns
THREE.Matrix4

setColorAt#

setColorAt(instanceId : number, color : THREE.Color) : this

Set the color for an instance. Automatically enables instance colors if not already enabled.

Parameters
instanceIdnumber
Target instance ID.
colorTHREE.Color
Color to apply.
Returns
this

getColorAt#

getColorAt(instanceId : number, color : THREE.Color) : THREE.Color

Get the color for an instance.

Parameters
instanceIdnumber
Target instance ID.
colorTHREE.Color
Color to store the result.
Returns
THREE.Color

setGeometryIdAt#

setGeometryIdAt(instanceId : number, geometryId : number) : this

Change which geometry an instance references.

Parameters
instanceIdnumber
Target instance ID.
geometryIdnumber
New geometry ID.
Returns
this

getGeometryIdAt#

getGeometryIdAt(instanceId : number) : number

Get the geometry ID for an instance.

Parameters
instanceIdnumber
Target instance ID.
Returns
number — Geometry ID.

copy#

copy(source : IndirectBatchedMesh) : this

Create a deep copy of this IndirectBatchedMesh.

Parameters
Source mesh to copy from.
Returns
this

dispose#

dispose() : void

Dispose of GPU resources. Call this when the mesh is no longer needed.

Returns
void

Type Definitions

IndirectBatchedMeshBuffers#

matricesSB:THREE.StorageBufferAttribute, colorsSB:THREE.StorageBufferAttribute|null, geometryIdSB:THREE.StorageBufferAttribute, survivorIdSB:THREE.StorageBufferAttribute|null, +1 more
Properties
matricesSBTHREE.StorageBufferAttribute
Per-instance 4×4 transform matrices (mat4, 16 floats each).
colorsSBTHREE.StorageBufferAttribute | null
Optional per-instance colors (vec4, 4 floats each).
geometryIdSBTHREE.StorageBufferAttribute
Per-instance geometry identifier for shader-side masking (uint).
survivorIdSBTHREE.StorageBufferAttribute | null
Optional survivor-to-instance mapping (uint) written by compute culling.
indirectoptionalTHREE.IndirectStorageBufferAttribute
Indirect draw command buffer controlling instance count.