From d7521a8477add173200c9e65dce8cbbe0c7a3997 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 29 Sep 2024 21:38:00 +0200 Subject: [PATCH 1/7] Add setInstanceCount function --- src/objects/BatchedMesh.js | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 55dd22adf8ce34..979359625615fc 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -930,6 +930,51 @@ class BatchedMesh extends Mesh { } + setInstanceCount( maxInstanceCount ) { + + // shrink the available instances as much as possible + const availableInstanceIds = this._availableInstanceIds; + const drawInfo = this._drawInfo; + availableInstanceIds.sort( ( a, b ) => a - b ); + while ( availableInstanceIds[ availableInstanceIds.length - 1 ] === drawInfo.length ) { + + drawInfo.pop(); + availableInstanceIds.pop(); + + } + + // throw an error if it can't be shrunk to the desired size + if ( maxInstanceCount < drawInfo.length ) { + + throw new Error(); + + } + + // copy the multi draw counts + const multiDrawCounts = new Int32Array( maxInstanceCount ); + const multiDrawStarts = new Int32Array( maxInstanceCount ); + multiDrawCounts.set( this._multiDrawCounts ); + multiDrawStarts.set( this._multiDrawStarts ); + + this._multiDrawCounts = multiDrawCounts; + this._multiDrawStarts = multiDrawStarts; + this._maxInstanceCount = maxInstanceCount; + + // update texture data for instance sampling + const indirectTexture = this._indirectTexture; + const matricesTexture = this._matricesTexture; + const colorsTexture = this._colorsTexture; + + this._initIndirectTexture(); + this._initMatricesTexture(); + this._initColorsTexture(); + + this._indirectTexture.image.data.set( indirectTexture.image.data ); + this._matricesTexture.image.data.set( matricesTexture.image.data ); + this._colorsTexture.image.data.set( colorsTexture.image.data ); + + } + raycast( raycaster, intersects ) { const drawInfo = this._drawInfo; From d0eaee3a5c11d453acaaa842398c1a6a6ae18424 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 30 Sep 2024 14:55:51 +0200 Subject: [PATCH 2/7] Add setGeometrySize function --- src/objects/BatchedMesh.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 979359625615fc..7e6d2413f7c653 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -975,6 +975,31 @@ class BatchedMesh extends Mesh { } + setGeometrySize( maxVertexCount, maxIndexCount = maxVertexCount * 2 ) { + + const oldGeometry = this.geometry; + oldGeometry.dispose(); + + this._maxVertexCount = maxVertexCount; + this._maxIndexCount = maxIndexCount; + this._geometryInitialized = false; + this._initializeGeometry(); + + const geometry = this.geometry; + if ( oldGeometry.index ) { + + geometry.index.array.set( oldGeometry.index.array ); + + } + + for ( const key in oldGeometry.attributes ) { + + geometry.attributes[ key ].array.set( oldGeometry.attributes[ key ].array ); + + } + + } + raycast( raycaster, intersects ) { const drawInfo = this._drawInfo; From 93fae7248fb7baf6f9d81fcdb7e4217b2edd4434 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 30 Sep 2024 15:13:25 +0200 Subject: [PATCH 3/7] Prefer using earlier ids when reusing ids --- src/objects/BatchedMesh.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 7e6d2413f7c653..6441d6913298e2 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -12,6 +12,12 @@ import { Frustum } from '../math/Frustum.js'; import { Vector3 } from '../math/Vector3.js'; import { Color } from '../math/Color.js'; +function ascIdSort( a, b ) { + + return a - b; + +} + function sortOpaque( a, b ) { return a.z - b.z; @@ -368,7 +374,9 @@ class BatchedMesh extends Mesh { // Prioritize using previously freed instance ids if ( this._availableInstanceIds.length > 0 ) { - drawId = this._availableInstanceIds.pop(); + this._availableInstanceIds.sort( ascIdSort ); + + drawId = this._availableInstanceIds.shift(); this._drawInfo[ drawId ] = instanceDrawInfo; } else { @@ -494,7 +502,9 @@ class BatchedMesh extends Mesh { let geometryId; if ( this._availableGeometryIds.length > 0 ) { - geometryId = this._availableGeometryIds.pop(); + this._availableGeometryIds.sort( ascIdSort ); + + geometryId = this._availableGeometryIds.shift(); reservedRanges[ geometryId ] = reservedRange; drawRanges[ geometryId ] = drawRange; bounds[ geometryId ] = boundsInfo; @@ -935,7 +945,7 @@ class BatchedMesh extends Mesh { // shrink the available instances as much as possible const availableInstanceIds = this._availableInstanceIds; const drawInfo = this._drawInfo; - availableInstanceIds.sort( ( a, b ) => a - b ); + availableInstanceIds.sort( ascIdSort ); while ( availableInstanceIds[ availableInstanceIds.length - 1 ] === drawInfo.length ) { drawInfo.pop(); From 18eb081a9788f7d1858045cdbaa361bb8e2a5c7a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 30 Sep 2024 15:13:33 +0200 Subject: [PATCH 4/7] Check for size validity --- src/objects/BatchedMesh.js | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 6441d6913298e2..6c6d035428960e 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -956,7 +956,7 @@ class BatchedMesh extends Mesh { // throw an error if it can't be shrunk to the desired size if ( maxInstanceCount < drawInfo.length ) { - throw new Error(); + throw new Error( `BatchedMesh: Instance ids outside the range ${ maxInstanceCount } are being used. Cannot shrink instance count.` ); } @@ -987,14 +987,40 @@ class BatchedMesh extends Mesh { setGeometrySize( maxVertexCount, maxIndexCount = maxVertexCount * 2 ) { + // Check if we can shrink to the requested vertex attribute size + const validRanges = [ ...this._reservedRanges ].filter( ( range, i ) => this._drawRanges[ i ].active ); + const requiredVertexLength = Math.max( ...validRanges.map( range => range.vertexStart + range.vertexCount ) ); + if ( requiredVertexLength > maxVertexCount ) { + + throw new Error( `BatchedMesh: Geometry Index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); + + } + + // Check if we can shrink to the requested index attribute size + if ( this.geometry.index ) { + + const requiredIndexLength = Math.max( ...validRanges.map( range => range.indexStart + range.indexCount ) ); + if ( requiredIndexLength > maxIndexCount ) { + + throw new Error( `BatchedMesh: Geometry Index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); + + } + + } + + // + + // dispose of the previous geometry const oldGeometry = this.geometry; oldGeometry.dispose(); + // recreate the geometry needed based on the previous variant this._maxVertexCount = maxVertexCount; this._maxIndexCount = maxIndexCount; this._geometryInitialized = false; - this._initializeGeometry(); + this._initializeGeometry( oldGeometry ); + // copy data from the previous geometry const geometry = this.geometry; if ( oldGeometry.index ) { From 2567d407dc052e42b0f0a21e59f2365f037709db Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 7 Oct 2024 15:22:51 +0200 Subject: [PATCH 5/7] Fix implementation bugs --- src/objects/BatchedMesh.js | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index d0da9f909f5d34..3ddceacce412a2 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -129,6 +129,13 @@ function copyAttributeData( src, target, targetOffset = 0 ) { } +function copyArrayContents( src, target ) { + + const len = Math.min( src.length, target.length ); + target.set( new src.constructor( src.buffer, 0, len ) ); + +} + class BatchedMesh extends Mesh { get maxInstanceCount() { @@ -1035,8 +1042,8 @@ class BatchedMesh extends Mesh { // copy the multi draw counts const multiDrawCounts = new Int32Array( maxInstanceCount ); const multiDrawStarts = new Int32Array( maxInstanceCount ); - multiDrawCounts.set( this._multiDrawCounts ); - multiDrawStarts.set( this._multiDrawStarts ); + copyArrayContents( this._multiDrawCounts, multiDrawCounts ); + copyArrayContents( this._multiDrawStarts, multiDrawStarts ); this._multiDrawCounts = multiDrawCounts; this._multiDrawStarts = multiDrawStarts; @@ -1048,12 +1055,17 @@ class BatchedMesh extends Mesh { const colorsTexture = this._colorsTexture; this._initIndirectTexture(); + copyArrayContents( indirectTexture.image.data, this._indirectTexture.image.data ); + this._initMatricesTexture(); - this._initColorsTexture(); + copyArrayContents( matricesTexture.image.data, this._matricesTexture.image.data ); - this._indirectTexture.image.data.set( indirectTexture.image.data ); - this._matricesTexture.image.data.set( matricesTexture.image.data ); - this._colorsTexture.image.data.set( colorsTexture.image.data ); + if ( colorsTexture ) { + + this._initColorsTexture(); + copyArrayContents( colorsTexture.image.data, this._colorsTexture.image.data ); + + } } @@ -1064,7 +1076,7 @@ class BatchedMesh extends Mesh { const requiredVertexLength = Math.max( ...validRanges.map( range => range.vertexStart + range.vertexCount ) ); if ( requiredVertexLength > maxVertexCount ) { - throw new Error( `BatchedMesh: Geometry Index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); + throw new Error( `BatchedMesh: Geometry vertex values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); } @@ -1074,7 +1086,7 @@ class BatchedMesh extends Mesh { const requiredIndexLength = Math.max( ...validRanges.map( range => range.indexStart + range.indexCount ) ); if ( requiredIndexLength > maxIndexCount ) { - throw new Error( `BatchedMesh: Geometry Index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); + throw new Error( `BatchedMesh: Geometry index values are being used outside the range ${ maxIndexCount }. Cannot shrink further.` ); } @@ -1090,19 +1102,21 @@ class BatchedMesh extends Mesh { this._maxVertexCount = maxVertexCount; this._maxIndexCount = maxIndexCount; this._geometryInitialized = false; + + this.geometry = new BufferGeometry(); this._initializeGeometry( oldGeometry ); // copy data from the previous geometry const geometry = this.geometry; if ( oldGeometry.index ) { - geometry.index.array.set( oldGeometry.index.array ); + copyArrayContents( oldGeometry.index.array, geometry.index.array ); } for ( const key in oldGeometry.attributes ) { - geometry.attributes[ key ].array.set( oldGeometry.attributes[ key ].array ); + copyArrayContents( oldGeometry.attributes[ key ].array, geometry.attributes[ key ].array ); } From 54439320891d373ea33c511739e5ff4d9299413a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 7 Oct 2024 15:23:33 +0200 Subject: [PATCH 6/7] Add comment --- src/objects/BatchedMesh.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 3ddceacce412a2..e9156fd1ea9f1e 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -129,6 +129,7 @@ function copyAttributeData( src, target, targetOffset = 0 ) { } +// safely copies array contents to a potentially smaller array function copyArrayContents( src, target ) { const len = Math.min( src.length, target.length ); From 5be6bad7db45430b2f9d7e5937205339fd808cca Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 7 Oct 2024 22:34:23 +0200 Subject: [PATCH 7/7] Add docs --- docs/api/en/objects/BatchedMesh.html | 24 +++++++++++++++++++++++- src/objects/BatchedMesh.js | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/api/en/objects/BatchedMesh.html b/docs/api/en/objects/BatchedMesh.html index bc69801d7f2c68..342a254553eef9 100644 --- a/docs/api/en/objects/BatchedMesh.html +++ b/docs/api/en/objects/BatchedMesh.html @@ -301,7 +301,6 @@

Calling this will change all instances that are rendering that geometry.

-

[method:this optimize]()

@@ -309,6 +308,29 @@

Repacks the sub geometries in [name] to remove any unused space remaining from previously deleted geometry, freeing up space to add new geometry.

+

+ [method:this setGeometrySize]( maxVertexCount, maxIndexCount ) +

+

+ Resizes the available space in [name]'s vertex and index buffer attributes to the provided sizes. If the provided arguments shrink the geometry buffers + but there is not enough unused space at the end of the geometry attributes then an error is thrown. +

+

+ [page:Integer maxVertexCount] - the max number of vertices to be used by all unique geometries to resize to.
+ [page:Integer maxIndexCount] - the max number of indices to be used by all unique geometries to resize to.
+

+ +

+ [method:this setInstanceCount]( maxInstanceCount ) +

+

+ Resizes the necessary buffers to support the provided number of instances. If the provided arguments shrink the number of instances but there are not enough + unused ids at the end of the list then an error is thrown. +

+

+ [page:Integer maxInstanceCount] - the max number of individual instances that can be added and rendered by the [name].
+

+