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].
+
+