diff --git a/src/nodes/accessors/Batch.js b/src/nodes/accessors/Batch.js index ab62e98dadab25..78dd344134d49f 100644 --- a/src/nodes/accessors/Batch.js +++ b/src/nodes/accessors/Batch.js @@ -1,12 +1,16 @@ import { normalLocal } from './Normal.js'; -import { positionLocal } from './Position.js'; +import { positionLocal, positionPrevious } from './Position.js'; import { 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'; +import { OnObjectUpdate } from '../utils/EventNode.js'; +import { DataTexture } from '../../textures/DataTexture.js'; + +const _previousBatchingMatrices = /*@__PURE__*/ new WeakMap(); /** * TSL function that retrieves the batching color for a given instance ID from a colors texture. @@ -41,6 +45,61 @@ const getIndirectIndex = /*@__PURE__*/ Fn( ( [ indirectTexture, id ] ) => { } ); +/** + * Creates the node that reads a batching matrix from the given matrices texture. + * + * @param {Texture} matricesTexture - The matrices texture. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The matrix node. + */ +function createBatchingMatrixNode( matricesTexture, id ) { + + const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); + const j = float( id ).mul( 4 ).toInt().toConst(); + + const x = j.mod( size ).toConst(); + const y = j.div( size ).toConst(); + + return 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 ) ) + ); + +} + +/** + * Retrieves or initializes the previous frame batching matrix node for motion vectors. + * Uses a WeakMap to cache previous frame matrices textures and their TSL nodes. + * + * @param {BatchedMesh} batchMesh - The batched mesh. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The previous frame batching matrix node. + */ +function getPreviousNode( batchMesh, id ) { + + let data = _previousBatchingMatrices.get( batchMesh ); + + if ( data === undefined ) { + + const { image, format, type } = batchMesh._matricesTexture; + + const previousMatricesTexture = new DataTexture( image.data.slice(), image.width, image.height, format, type ); + + data = { + previousMatricesTexture, + node: createBatchingMatrixNode( previousMatricesTexture, id ) + }; + + _previousBatchingMatrices.set( batchMesh, data ); + + } + + return data.node; + +} + /** * TSL object representing a varying property for the batching color vector. * @@ -63,19 +122,7 @@ export const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { const indirectId = getIndirectIndex( batchMesh._indirectTexture, int( batchingIdNode ) ); - const matricesTexture = batchMesh._matricesTexture; - - const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); - const j = float( indirectId ).mul( 4 ).toInt().toConst(); - - const x = j.mod( size ).toConst(); - const y = j.div( size ).toConst(); - 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 batchingMatrix = createBatchingMatrixNode( batchMesh._matricesTexture, indirectId ); const colorsTexture = batchMesh._colorsTexture; @@ -91,6 +138,22 @@ export const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { positionLocal.assign( batchingMatrix.mul( positionLocal ) ); + if ( builder.needsPreviousData() ) { + + OnObjectUpdate( ( { object } ) => { + + const previousBatchData = _previousBatchingMatrices.get( object ); + + previousBatchData.previousMatricesTexture.image.data.set( object._matricesTexture.image.data ); + previousBatchData.previousMatricesTexture.needsUpdate = true; + + } ); + + const previousBatchingMatrixNode = getPreviousNode( batchMesh, indirectId ); + positionPrevious.assign( previousBatchingMatrixNode.mul( positionPrevious ).xyz ); + + } + 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; diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 2ef7cb25743882..7ab4e4756a46fb 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -1203,6 +1203,7 @@ class BatchedMesh extends Mesh { this.validateGeometryId( geometryId ); this._instanceInfo[ instanceId ].geometryIndex = geometryId; + this._visibilityChanged = true; return this;