Skip to content

⬅️ Back to Table of Contents

📄 InstanceNode.js

📊 Analysis Summary

Metric Count
🔧 Functions 2
🧱 Classes 1
📦 Imports 16
📊 Variables & Constants 6

📚 Table of Contents

🛠️ File Location:

📂 src/nodes/accessors/InstanceNode.js

📦 Imports

Name Source
Node ../core/Node.js
varyingProperty ../core/PropertyNode.js
instancedBufferAttribute ./BufferAttributeNode.js
instancedDynamicBufferAttribute ./BufferAttributeNode.js
normalLocal ./Normal.js
transformNormal ./Normal.js
positionLocal ./Position.js
nodeProxy ../tsl/TSLBase.js
vec3 ../tsl/TSLBase.js
mat4 ../tsl/TSLBase.js
NodeUpdateType ../core/constants.js
buffer ../accessors/BufferNode.js
instanceIndex ../core/IndexNode.js
InstancedInterleavedBuffer ../../core/InstancedInterleavedBuffer.js
InstancedBufferAttribute ../../core/InstancedBufferAttribute.js
DynamicDrawUsage ../../constants.js

Variables & Constants

Name Type Kind Value Exported
buffer InstancedInterleavedBuffer let/var new InstancedInterleavedBuffer( instanceMatrix.array, 16, 1 )
bufferFn (array: any, type?: string, stride?: ... let/var instanceMatrix.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute :...
instanceBuffers BufferAttributeNode[] let/var [ // F.Signature -> bufferAttribute( array, type, stride, offset ) bufferFn( ...
buffer InstancedBufferAttribute let/var new InstancedBufferAttribute( instanceColor.array, 3 )
bufferFn (array: any, type?: string, stride?: ... let/var instanceColor.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : ...
instancePosition any let/var instanceMatrixNode.mul( positionLocal ).xyz

Functions

InstanceNode.setup(builder: NodeBuilder): void

JSDoc:

/**
     * 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.
     */

Parameters:

  • builder NodeBuilder

Returns: void

Calls:

  • buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element
  • bufferFn
  • mat4 (from ../tsl/TSLBase.js)
  • vec3 (from ../tsl/TSLBase.js)
  • instanceMatrixNode.mul
  • positionLocal.assign
  • builder.hasGeometryAttribute
  • transformNormal (from ./Normal.js)
  • normalLocal.assign
  • varyingProperty( 'vec3', 'vInstanceColor' ).assign

Internal Comments:

// Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.
// F.Signature -> bufferAttribute( array, type, stride, offset ) (x2)
// POSITION (x2)
// NORMAL
// ASSIGNS (x4)
// COLOR

Code
setup( builder ) {

        const { count, instanceMatrix, instanceColor } = this;

        let { instanceMatrixNode, instanceColorNode } = this;

        if ( instanceMatrixNode === null ) {

            // Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.

            if ( count <= 1000 ) {

                instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex );

            } else {

                const buffer = new InstancedInterleavedBuffer( instanceMatrix.array, 16, 1 );

                this.buffer = buffer;

                const bufferFn = instanceMatrix.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

                const instanceBuffers = [
                    // F.Signature -> bufferAttribute( array, type, stride, offset )
                    bufferFn( buffer, 'vec4', 16, 0 ),
                    bufferFn( buffer, 'vec4', 16, 4 ),
                    bufferFn( buffer, 'vec4', 16, 8 ),
                    bufferFn( buffer, 'vec4', 16, 12 )
                ];

                instanceMatrixNode = mat4( ...instanceBuffers );

            }

            this.instanceMatrixNode = instanceMatrixNode;

        }

        if ( instanceColor && instanceColorNode === null ) {

            const buffer = new InstancedBufferAttribute( instanceColor.array, 3 );

            const bufferFn = instanceColor.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

            this.bufferColor = buffer;

            instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) );

            this.instanceColorNode = instanceColorNode;

        }

        // POSITION

        const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;
        positionLocal.assign( instancePosition );

        // NORMAL

        if ( builder.hasGeometryAttribute( 'normal' ) ) {

            const instanceNormal = transformNormal( normalLocal, instanceMatrixNode );

            // ASSIGNS

            normalLocal.assign( instanceNormal );

        }

        // COLOR

        if ( this.instanceColorNode !== null ) {

            varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode );

        }

    }

InstanceNode.update(): void

JSDoc:

/**
     * Checks if the internal buffers required an update.
     *
     * @param {NodeFrame} frame - The current node frame.
     */

Returns: void

Code
update( /*frame*/ ) {

        if ( this.instanceMatrix.usage !== DynamicDrawUsage && this.buffer !== null && this.instanceMatrix.version !== this.buffer.version ) {

            this.buffer.version = this.instanceMatrix.version;

        }

        if ( this.instanceColor && this.instanceColor.usage !== DynamicDrawUsage && this.bufferColor !== null && this.instanceColor.version !== this.bufferColor.version ) {

            this.bufferColor.version = this.instanceColor.version;

        }

    }

Classes

InstanceNode

Class Code
class InstanceNode extends Node {

    static get type() {

        return 'InstanceNode';

    }

    /**
     * Constructs a new instance node.
     *
     * @param {number} count - The number of instances.
     * @param {InstancedBufferAttribute} instanceMatrix - Instanced buffer attribute representing the instance transformations.
     * @param {?InstancedBufferAttribute} instanceColor - Instanced buffer attribute representing the instance colors.
     */
    constructor( count, instanceMatrix, instanceColor = null ) {

        super( 'void' );

        /**
         * The number of instances.
         *
         * @type {number}
         */
        this.count = count;

        /**
         * Instanced buffer attribute representing the transformation of instances.
         *
         * @type {InstancedBufferAttribute}
         */
        this.instanceMatrix = instanceMatrix;

        /**
         * Instanced buffer attribute representing the color of instances.
         *
         * @type {InstancedBufferAttribute}
         */
        this.instanceColor = instanceColor;

        /**
         * The node that represents the instance matrix data.
         *
         * @type {?Node}
         */
        this.instanceMatrixNode = null;

        /**
         * The node that represents the instance color data.
         *
         * @type {?Node}
         * @default null
         */
        this.instanceColorNode = null;

        /**
         * The update type is set to `frame` since an update
         * of instanced buffer data must be checked per frame.
         *
         * @type {string}
         * @default 'frame'
         */
        this.updateType = NodeUpdateType.FRAME;

        /**
         * A reference to a buffer that is used by `instanceMatrixNode`.
         *
         * @type {?InstancedInterleavedBuffer}
         */
        this.buffer = null;

        /**
         * A reference to a buffer that is used by `instanceColorNode`.
         *
         * @type {?InstancedBufferAttribute}
         */
        this.bufferColor = 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 ) {

        const { count, instanceMatrix, instanceColor } = this;

        let { instanceMatrixNode, instanceColorNode } = this;

        if ( instanceMatrixNode === null ) {

            // Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.

            if ( count <= 1000 ) {

                instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex );

            } else {

                const buffer = new InstancedInterleavedBuffer( instanceMatrix.array, 16, 1 );

                this.buffer = buffer;

                const bufferFn = instanceMatrix.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

                const instanceBuffers = [
                    // F.Signature -> bufferAttribute( array, type, stride, offset )
                    bufferFn( buffer, 'vec4', 16, 0 ),
                    bufferFn( buffer, 'vec4', 16, 4 ),
                    bufferFn( buffer, 'vec4', 16, 8 ),
                    bufferFn( buffer, 'vec4', 16, 12 )
                ];

                instanceMatrixNode = mat4( ...instanceBuffers );

            }

            this.instanceMatrixNode = instanceMatrixNode;

        }

        if ( instanceColor && instanceColorNode === null ) {

            const buffer = new InstancedBufferAttribute( instanceColor.array, 3 );

            const bufferFn = instanceColor.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

            this.bufferColor = buffer;

            instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) );

            this.instanceColorNode = instanceColorNode;

        }

        // POSITION

        const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;
        positionLocal.assign( instancePosition );

        // NORMAL

        if ( builder.hasGeometryAttribute( 'normal' ) ) {

            const instanceNormal = transformNormal( normalLocal, instanceMatrixNode );

            // ASSIGNS

            normalLocal.assign( instanceNormal );

        }

        // COLOR

        if ( this.instanceColorNode !== null ) {

            varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode );

        }

    }

    /**
     * Checks if the internal buffers required an update.
     *
     * @param {NodeFrame} frame - The current node frame.
     */
    update( /*frame*/ ) {

        if ( this.instanceMatrix.usage !== DynamicDrawUsage && this.buffer !== null && this.instanceMatrix.version !== this.buffer.version ) {

            this.buffer.version = this.instanceMatrix.version;

        }

        if ( this.instanceColor && this.instanceColor.usage !== DynamicDrawUsage && this.bufferColor !== null && this.instanceColor.version !== this.bufferColor.version ) {

            this.bufferColor.version = this.instanceColor.version;

        }

    }

}

Methods

setup(builder: NodeBuilder): void
Code
setup( builder ) {

        const { count, instanceMatrix, instanceColor } = this;

        let { instanceMatrixNode, instanceColorNode } = this;

        if ( instanceMatrixNode === null ) {

            // Both WebGPU and WebGL backends have UBO max limited to 64kb. Matrix count number bigger than 1000 ( 16 * 4 * 1000 = 64kb ) will fallback to attribute.

            if ( count <= 1000 ) {

                instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex );

            } else {

                const buffer = new InstancedInterleavedBuffer( instanceMatrix.array, 16, 1 );

                this.buffer = buffer;

                const bufferFn = instanceMatrix.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

                const instanceBuffers = [
                    // F.Signature -> bufferAttribute( array, type, stride, offset )
                    bufferFn( buffer, 'vec4', 16, 0 ),
                    bufferFn( buffer, 'vec4', 16, 4 ),
                    bufferFn( buffer, 'vec4', 16, 8 ),
                    bufferFn( buffer, 'vec4', 16, 12 )
                ];

                instanceMatrixNode = mat4( ...instanceBuffers );

            }

            this.instanceMatrixNode = instanceMatrixNode;

        }

        if ( instanceColor && instanceColorNode === null ) {

            const buffer = new InstancedBufferAttribute( instanceColor.array, 3 );

            const bufferFn = instanceColor.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;

            this.bufferColor = buffer;

            instanceColorNode = vec3( bufferFn( buffer, 'vec3', 3, 0 ) );

            this.instanceColorNode = instanceColorNode;

        }

        // POSITION

        const instancePosition = instanceMatrixNode.mul( positionLocal ).xyz;
        positionLocal.assign( instancePosition );

        // NORMAL

        if ( builder.hasGeometryAttribute( 'normal' ) ) {

            const instanceNormal = transformNormal( normalLocal, instanceMatrixNode );

            // ASSIGNS

            normalLocal.assign( instanceNormal );

        }

        // COLOR

        if ( this.instanceColorNode !== null ) {

            varyingProperty( 'vec3', 'vInstanceColor' ).assign( this.instanceColorNode );

        }

    }
update(): void
Code
update( /*frame*/ ) {

        if ( this.instanceMatrix.usage !== DynamicDrawUsage && this.buffer !== null && this.instanceMatrix.version !== this.buffer.version ) {

            this.buffer.version = this.instanceMatrix.version;

        }

        if ( this.instanceColor && this.instanceColor.usage !== DynamicDrawUsage && this.bufferColor !== null && this.instanceColor.version !== this.bufferColor.version ) {

            this.bufferColor.version = this.instanceColor.version;

        }

    }