@@ -2,7 +2,7 @@ import Node from '../core/Node.js';
22import { varyingProperty } from '../core/PropertyNode.js' ;
33import { instancedBufferAttribute , instancedDynamicBufferAttribute } from './BufferAttributeNode.js' ;
44import { normalLocal , transformNormal } from './Normal.js' ;
5- import { positionLocal } from './Position.js' ;
5+ import { positionLocal , positionPrevious } from './Position.js' ;
66import { nodeProxy , vec3 , mat4 } from '../tsl/TSLBase.js' ;
77import { NodeUpdateType } from '../core/constants.js' ;
88import { buffer } from '../accessors/BufferNode.js' ;
@@ -99,6 +99,14 @@ class InstanceNode extends Node {
9999 */
100100 this . bufferColor = null ;
101101
102+ /**
103+ * The previous instance matrices. Required for computing motion vectors.
104+ *
105+ * @type {?Node }
106+ * @default null
107+ */
108+ this . previousInstanceMatrixNode = null ;
109+
102110 }
103111
104112 /**
@@ -136,51 +144,22 @@ class InstanceNode extends Node {
136144 */
137145 setup ( builder ) {
138146
139- const { instanceMatrix, instanceColor, isStorageMatrix, isStorageColor } = this ;
140-
141- const { count } = instanceMatrix ;
142-
143147 let { instanceMatrixNode, instanceColorNode } = this ;
144148
145- if ( instanceMatrixNode === null ) {
146-
147- if ( isStorageMatrix ) {
148-
149- instanceMatrixNode = storage ( instanceMatrix , 'mat4' , Math . max ( count , 1 ) ) . element ( instanceIndex ) ;
150-
151- } else {
152-
153- // Both backends have ~64kb UBO limit; fallback to attributes above 1000 matrices.
154-
155- if ( count <= 1000 ) {
156-
157- instanceMatrixNode = buffer ( instanceMatrix . array , 'mat4' , Math . max ( count , 1 ) ) . element ( instanceIndex ) ;
158-
159- } else {
160-
161- const interleaved = new InstancedInterleavedBuffer ( instanceMatrix . array , 16 , 1 ) ;
162-
163- this . buffer = interleaved ;
149+ // instance matrix
164150
165- const bufferFn = instanceMatrix . usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute ;
166-
167- const instanceBuffers = [
168- bufferFn ( interleaved , 'vec4' , 16 , 0 ) ,
169- bufferFn ( interleaved , 'vec4' , 16 , 4 ) ,
170- bufferFn ( interleaved , 'vec4' , 16 , 8 ) ,
171- bufferFn ( interleaved , 'vec4' , 16 , 12 )
172- ] ;
173-
174- instanceMatrixNode = mat4 ( ...instanceBuffers ) ;
175-
176- }
151+ if ( instanceMatrixNode === null ) {
177152
178- }
153+ instanceMatrixNode = this . _createInstanceMatrixNode ( true ) ;
179154
180155 this . instanceMatrixNode = instanceMatrixNode ;
181156
182157 }
183158
159+ // instance color
160+
161+ const { instanceColor, isStorageColor } = this ;
162+
184163 if ( instanceColor && instanceColorNode === null ) {
185164
186165 if ( isStorageColor ) {
@@ -208,6 +187,12 @@ class InstanceNode extends Node {
208187 const instancePosition = instanceMatrixNode . mul ( positionLocal ) . xyz ;
209188 positionLocal . assign ( instancePosition ) ;
210189
190+ if ( builder . needsPreviousData ( ) ) {
191+
192+ positionPrevious . assign ( this . getPreviousInstancedPosition ( builder ) ) ;
193+
194+ }
195+
211196 // NORMAL
212197
213198 if ( builder . hasGeometryAttribute ( 'normal' ) ) {
@@ -235,7 +220,7 @@ class InstanceNode extends Node {
235220 *
236221 * @param {NodeFrame } frame - The current node frame.
237222 */
238- update ( /* frame*/ ) {
223+ update ( frame ) {
239224
240225 if ( this . buffer !== null && this . isStorageMatrix !== true ) {
241226
@@ -265,6 +250,85 @@ class InstanceNode extends Node {
265250
266251 }
267252
253+ if ( this . previousInstanceMatrixNode !== null ) {
254+
255+ frame . object . previousInstanceMatrix . array . set ( this . instanceMatrix . array ) ;
256+
257+ }
258+
259+ }
260+
261+ /**
262+ * Computes the transformed/instanced vertex position of the previous frame.
263+ *
264+ * @param {NodeBuilder } builder - The current node builder.
265+ * @return {Node<vec3> } The instanced position from the previous frame.
266+ */
267+ getPreviousInstancedPosition ( builder ) {
268+
269+ const instancedMesh = builder . object ;
270+
271+ if ( this . previousInstanceMatrixNode === null ) {
272+
273+ instancedMesh . previousInstanceMatrix = this . instanceMatrix . clone ( ) ;
274+
275+ this . previousInstanceMatrixNode = this . _createInstanceMatrixNode ( false ) ;
276+
277+ }
278+
279+ return this . previousInstanceMatrixNode . mul ( positionPrevious ) . xyz ;
280+
281+ }
282+
283+ /**
284+ * Creates a node representing the instance matrix data.
285+ *
286+ * @private
287+ * @param {boolean } assignBuffer - Whether the created interleaved buffer should be assigned to the `buffer` member or not.
288+ * @return {Node } The instance matrix node.
289+ */
290+ _createInstanceMatrixNode ( assignBuffer ) {
291+
292+ let instanceMatrixNode ;
293+
294+ const { instanceMatrix } = this ;
295+ const { count } = instanceMatrix ;
296+
297+ if ( this . isStorageMatrix ) {
298+
299+ instanceMatrixNode = storage ( instanceMatrix , 'mat4' , Math . max ( count , 1 ) ) . element ( instanceIndex ) ;
300+
301+ } else {
302+
303+ // Both backends have ~64kb UBO limit; fallback to attributes above 1000 matrices.
304+
305+ if ( count <= 1000 ) {
306+
307+ instanceMatrixNode = buffer ( instanceMatrix . array , 'mat4' , Math . max ( count , 1 ) ) . element ( instanceIndex ) ;
308+
309+ } else {
310+
311+ const interleaved = new InstancedInterleavedBuffer ( instanceMatrix . array , 16 , 1 ) ;
312+
313+ if ( assignBuffer === true ) this . buffer = interleaved ;
314+
315+ const bufferFn = instanceMatrix . usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute ;
316+
317+ const instanceBuffers = [
318+ bufferFn ( interleaved , 'vec4' , 16 , 0 ) ,
319+ bufferFn ( interleaved , 'vec4' , 16 , 4 ) ,
320+ bufferFn ( interleaved , 'vec4' , 16 , 8 ) ,
321+ bufferFn ( interleaved , 'vec4' , 16 , 12 )
322+ ] ;
323+
324+ instanceMatrixNode = mat4 ( ...instanceBuffers ) ;
325+
326+ }
327+
328+ }
329+
330+ return instanceMatrixNode ;
331+
268332 }
269333
270334}
0 commit comments