indirectBatch

@three-blocks/coreWebGPU
indirectBatch(batchMesh : IndirectBatchedMesh) : IndirectBatchNode

TSL helper for applying indirect batched transforms to vertices. Automatically resolves instance IDs, applies transforms, and handles geometry masking.

Example: Basic setup

Parameters
The batched mesh instance to read data from.
Returns
IndirectBatchNode — Node that transforms vertices using batched instance data.
Example
import * as THREE from 'three/webgpu';
import { IndirectBatchedMesh, indirectBatch } from '@three-blocks/core';
import { positionLocal, morphReference, skinning, uniform, subBuild, normalLocal, Fn } from 'three/tsl';

const geometry = new THREE.BoxGeometry(1, 1, 1);
const vertexCount = geometry.getAttribute('position').count;
const indexCount = geometry.getIndex()?.count || 0;
const instanceCount = 512;

const material = new THREE.MeshBasicNodeMaterial();
const mesh = new IndirectBatchedMesh(instanceCount, vertexCount, indexCount, material);

// Disable internal culling for this simple demo
mesh.perObjectFrustumCulled = false;

const geoId = mesh.addGeometry(geometry);

// Setup the indirect batch transform
material.setupPosition = function(builder) {
  const { object, geometry } = builder;

  if (geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color) {
    morphReference(object).toStack();
  }

  if (object.isSkinnedMesh === true) {
    skinning(object).toStack();
  }

  if (object.isIndirectBatchedMesh) {
    // inject here
    indirectBatch(object).toStack();

    const worldMatrix = uniform(new THREE.Matrix4()).onObjectUpdate((_, self) => self.value.copy(object.matrixWorld)).setName('batchWorldMatrix');
    object.setCullingMatrixNode(({ baseMatrix }) => worldMatrix.mul(baseMatrix));
  }

  if (this.positionNode !== null) {
    positionLocal.assign(subBuild(this.positionNode, 'POSITION', 'vec3'));
  }

  return positionLocal;
};

// Use normal-based coloring to verify transforms work correctly
material.colorNode = Fn(() => {
  return normalLocal.mul(0.5).add(0.5);
})();

// Add instances in a grid pattern
const tmpMatrix = new THREE.Matrix4();
const gridSize = Math.ceil(Math.cbrt(instanceCount));
const spacing = 2.5;

let idx = 0;
for (let x = 0; x < gridSize && idx < instanceCount; x++) {
  for (let y = 0; y < gridSize && idx < instanceCount; y++) {
    for (let z = 0; z < gridSize && idx < instanceCount; z++) {
      const id = mesh.addInstance(geoId);
      tmpMatrix.makeTranslation(
        (x - gridSize / 2) * spacing,
        (y - gridSize / 2) * spacing,
        (z - gridSize / 2) * spacing
      );
      mesh.setMatrixAt(id, tmpMatrix);
      idx++;
    }
  }
}

mesh.needsUpdate = true;