import Node from '../core/Node.js';
import { normalLocal } from './Normal.js';
import { positionLocal } from './Position.js';
import { nodeProxy, vec3, mat3, mat4, int, ivec2, float, Fn } from '../tsl/TSLBase.js';
import { textureLoad } from './TextureNode.js';
import { textureSize } from './TextureSizeNode.js';
import { tangentLocal } from './Tangent.js';
import { instanceIndex, drawIndex } from '../core/IndexNode.js';
import { varyingProperty } from '../core/PropertyNode.js';
/** @module BatchNode **/
/**
* This node implements the vertex shader logic which is required
* when rendering 3D objects via batching. `BatchNode` must be used
* with instances of {@link BatchedMesh}.
*
* @augments Node
*/
class BatchNode extends Node {
static get type() {
return 'BatchNode';
}
/**
* Constructs a new batch node.
*
* @param {BatchedMesh} batchMesh - A reference to batched mesh.
*/
constructor( batchMesh ) {
super( 'void' );
/**
* A reference to batched mesh.
*
* @type {BatchedMesh}
*/
this.batchMesh = batchMesh;
/**
* The batching index node.
*
* @type {IndexNode?}
* @default null
*/
this.batchingIdNode = null;
}
/**
* Setups the internal buffers and nodes and assigns the transformed vertex data
* to predefined node variables for accumulation. That follows the same patterns
* like with morph and skinning nodes.
*
* @param {NodeBuilder} builder - The current node builder.
*/
setup( builder ) {
if ( this.batchingIdNode === null ) {
if ( builder.getDrawIndex() === null ) {
this.batchingIdNode = instanceIndex;
} else {
this.batchingIdNode = drawIndex;
}
}
const getIndirectIndex = Fn( ( [ id ] ) => {
const size = int( textureSize( textureLoad( this.batchMesh._indirectTexture ), 0 ) );
const x = int( id ).modInt( size );
const y = int( id ).div( size );
return textureLoad( this.batchMesh._indirectTexture, ivec2( x, y ) ).x;
} ).setLayout( {
name: 'getIndirectIndex',
type: 'uint',
inputs: [
{ name: 'id', type: 'int' }
]
} );
const indirectId = getIndirectIndex( int( this.batchingIdNode ) );
const matricesTexture = this.batchMesh._matricesTexture;
const size = textureSize( textureLoad( matricesTexture ), 0 );
const j = float( indirectId ).mul( 4 ).toInt().toVar();
const x = j.modInt( size );
const y = j.div( int( size ) );
const batchingMatrix = mat4(
textureLoad( matricesTexture, ivec2( x, y ) ),
textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ),
textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ),
textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) )
);
const colorsTexture = this.batchMesh._colorsTexture;
if ( colorsTexture !== null ) {
const getBatchingColor = Fn( ( [ id ] ) => {
const size = textureSize( textureLoad( colorsTexture ), 0 ).x;
const j = id;
const x = j.modInt( size );
const y = j.div( size );
return textureLoad( colorsTexture, ivec2( x, y ) ).rgb;
} ).setLayout( {
name: 'getBatchingColor',
type: 'vec3',
inputs: [
{ name: 'id', type: 'int' }
]
} );
const color = getBatchingColor( indirectId );
varyingProperty( 'vec3', 'vBatchColor' ).assign( color );
}
const bm = mat3( batchingMatrix );
positionLocal.assign( batchingMatrix.mul( positionLocal ) );
const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) );
const batchingNormal = bm.mul( transformedNormal ).xyz;
normalLocal.assign( batchingNormal );
if ( builder.hasGeometryAttribute( 'tangent' ) ) {
tangentLocal.mulAssign( bm );
}
}
}
export default BatchNode;
/**
* TSL function for creating a batch node.
*
* @function
* @param {BatchedMesh} batchMesh - A reference to batched mesh.
* @returns {BatchNode}
*/
export const batch = /*@__PURE__*/ nodeProxy( BatchNode );