|
@@ -1,7 +1,7 @@
|
|
import { BufferAttribute } from '../core/BufferAttribute.js';
|
|
import { BufferAttribute } from '../core/BufferAttribute.js';
|
|
import { BufferGeometry } from '../core/BufferGeometry.js';
|
|
import { BufferGeometry } from '../core/BufferGeometry.js';
|
|
import { DataTexture } from '../textures/DataTexture.js';
|
|
import { DataTexture } from '../textures/DataTexture.js';
|
|
-import { FloatType } from '../constants.js';
|
|
|
|
|
|
+import { FloatType, RedIntegerFormat, UnsignedIntType } from '../constants.js';
|
|
import { Matrix4 } from '../math/Matrix4.js';
|
|
import { Matrix4 } from '../math/Matrix4.js';
|
|
import { Mesh } from './Mesh.js';
|
|
import { Mesh } from './Mesh.js';
|
|
import { RGBAFormat } from '../constants.js';
|
|
import { RGBAFormat } from '../constants.js';
|
|
@@ -34,7 +34,7 @@ class MultiDrawRenderList {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- push( drawRange, z ) {
|
|
|
|
|
|
+ push( drawRange, z, index ) {
|
|
|
|
|
|
const pool = this.pool;
|
|
const pool = this.pool;
|
|
const list = this.list;
|
|
const list = this.list;
|
|
@@ -45,6 +45,7 @@ class MultiDrawRenderList {
|
|
start: - 1,
|
|
start: - 1,
|
|
count: - 1,
|
|
count: - 1,
|
|
z: - 1,
|
|
z: - 1,
|
|
|
|
+ index: - 1,
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
@@ -57,6 +58,7 @@ class MultiDrawRenderList {
|
|
item.start = drawRange.start;
|
|
item.start = drawRange.start;
|
|
item.count = drawRange.count;
|
|
item.count = drawRange.count;
|
|
item.z = z;
|
|
item.z = z;
|
|
|
|
+ item.index = index;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -69,7 +71,6 @@ class MultiDrawRenderList {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-const ID_ATTR_NAME = 'batchId';
|
|
|
|
const _matrix = /*@__PURE__*/ new Matrix4();
|
|
const _matrix = /*@__PURE__*/ new Matrix4();
|
|
const _invMatrixWorld = /*@__PURE__*/ new Matrix4();
|
|
const _invMatrixWorld = /*@__PURE__*/ new Matrix4();
|
|
const _identityMatrix = /*@__PURE__*/ new Matrix4();
|
|
const _identityMatrix = /*@__PURE__*/ new Matrix4();
|
|
@@ -124,13 +125,13 @@ function copyAttributeData( src, target, targetOffset = 0 ) {
|
|
|
|
|
|
class BatchedMesh extends Mesh {
|
|
class BatchedMesh extends Mesh {
|
|
|
|
|
|
- get maxGeometryCount() {
|
|
|
|
|
|
+ get maxItemCount() {
|
|
|
|
|
|
- return this._maxGeometryCount;
|
|
|
|
|
|
+ return this._maxItemCount;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- constructor( maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {
|
|
|
|
|
|
+ constructor( maxDrawCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {
|
|
|
|
|
|
super( new BufferGeometry(), material );
|
|
super( new BufferGeometry(), material );
|
|
|
|
|
|
@@ -141,32 +142,33 @@ class BatchedMesh extends Mesh {
|
|
this.boundingSphere = null;
|
|
this.boundingSphere = null;
|
|
this.customSort = null;
|
|
this.customSort = null;
|
|
|
|
|
|
|
|
+ // stores visible, active, and geometry id per object
|
|
|
|
+ this._drawInfo = [];
|
|
|
|
+
|
|
|
|
+ // geometry information
|
|
this._drawRanges = [];
|
|
this._drawRanges = [];
|
|
this._reservedRanges = [];
|
|
this._reservedRanges = [];
|
|
-
|
|
|
|
- this._visibility = [];
|
|
|
|
- this._active = [];
|
|
|
|
this._bounds = [];
|
|
this._bounds = [];
|
|
|
|
|
|
- this._maxGeometryCount = maxGeometryCount;
|
|
|
|
|
|
+ this._maxItemCount = maxDrawCount;
|
|
this._maxVertexCount = maxVertexCount;
|
|
this._maxVertexCount = maxVertexCount;
|
|
this._maxIndexCount = maxIndexCount;
|
|
this._maxIndexCount = maxIndexCount;
|
|
|
|
|
|
this._geometryInitialized = false;
|
|
this._geometryInitialized = false;
|
|
this._geometryCount = 0;
|
|
this._geometryCount = 0;
|
|
- this._multiDrawCounts = new Int32Array( maxGeometryCount );
|
|
|
|
- this._multiDrawStarts = new Int32Array( maxGeometryCount );
|
|
|
|
|
|
+ this._multiDrawCounts = new Int32Array( maxDrawCount );
|
|
|
|
+ this._multiDrawStarts = new Int32Array( maxDrawCount );
|
|
this._multiDrawCount = 0;
|
|
this._multiDrawCount = 0;
|
|
this._multiDrawInstances = null;
|
|
this._multiDrawInstances = null;
|
|
this._visibilityChanged = true;
|
|
this._visibilityChanged = true;
|
|
|
|
|
|
// Local matrix per geometry by using data texture
|
|
// Local matrix per geometry by using data texture
|
|
this._matricesTexture = null;
|
|
this._matricesTexture = null;
|
|
|
|
+ this._indirectTexture = null;
|
|
|
|
+ this._colorsTexture = null;
|
|
|
|
|
|
this._initMatricesTexture();
|
|
this._initMatricesTexture();
|
|
-
|
|
|
|
- // Local color per geometry by using data texture
|
|
|
|
- this._colorsTexture = null;
|
|
|
|
|
|
+ this._initIndirectTexture();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -179,7 +181,7 @@ class BatchedMesh extends Mesh {
|
|
// 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32)
|
|
// 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32)
|
|
// 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64)
|
|
// 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64)
|
|
|
|
|
|
- let size = Math.sqrt( this._maxGeometryCount * 4 ); // 4 pixels needed for 1 matrix
|
|
|
|
|
|
+ let size = Math.sqrt( this._maxItemCount * 4 ); // 4 pixels needed for 1 matrix
|
|
size = Math.ceil( size / 4 ) * 4;
|
|
size = Math.ceil( size / 4 ) * 4;
|
|
size = Math.max( size, 4 );
|
|
size = Math.max( size, 4 );
|
|
|
|
|
|
@@ -190,9 +192,21 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ _initIndirectTexture() {
|
|
|
|
+
|
|
|
|
+ let size = Math.sqrt( this._maxItemCount );
|
|
|
|
+ size = Math.ceil( size );
|
|
|
|
+
|
|
|
|
+ const indirectArray = new Uint32Array( size * size );
|
|
|
|
+ const indirectTexture = new DataTexture( indirectArray, size, size, RedIntegerFormat, UnsignedIntType );
|
|
|
|
+
|
|
|
|
+ this._indirectTexture = indirectTexture;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
_initColorsTexture() {
|
|
_initColorsTexture() {
|
|
|
|
|
|
- let size = Math.sqrt( this._maxGeometryCount );
|
|
|
|
|
|
+ let size = Math.sqrt( this._maxIndexCount );
|
|
size = Math.ceil( size );
|
|
size = Math.ceil( size );
|
|
|
|
|
|
// 4 floats per RGBA pixel initialized to white
|
|
// 4 floats per RGBA pixel initialized to white
|
|
@@ -208,7 +222,6 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const geometry = this.geometry;
|
|
const geometry = this.geometry;
|
|
const maxVertexCount = this._maxVertexCount;
|
|
const maxVertexCount = this._maxVertexCount;
|
|
- const maxGeometryCount = this._maxGeometryCount;
|
|
|
|
const maxIndexCount = this._maxIndexCount;
|
|
const maxIndexCount = this._maxIndexCount;
|
|
if ( this._geometryInitialized === false ) {
|
|
if ( this._geometryInitialized === false ) {
|
|
|
|
|
|
@@ -234,11 +247,6 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- const idArray = maxGeometryCount > 65536
|
|
|
|
- ? new Uint32Array( maxVertexCount )
|
|
|
|
- : new Uint16Array( maxVertexCount );
|
|
|
|
- geometry.setAttribute( ID_ATTR_NAME, new BufferAttribute( idArray, 1 ) );
|
|
|
|
-
|
|
|
|
this._geometryInitialized = true;
|
|
this._geometryInitialized = true;
|
|
|
|
|
|
}
|
|
}
|
|
@@ -248,13 +256,6 @@ class BatchedMesh extends Mesh {
|
|
// Make sure the geometry is compatible with the existing combined geometry attributes
|
|
// Make sure the geometry is compatible with the existing combined geometry attributes
|
|
_validateGeometry( geometry ) {
|
|
_validateGeometry( geometry ) {
|
|
|
|
|
|
- // check that the geometry doesn't have a version of our reserved id attribute
|
|
|
|
- if ( geometry.getAttribute( ID_ATTR_NAME ) ) {
|
|
|
|
-
|
|
|
|
- throw new Error( `BatchedMesh: Geometry cannot use attribute "${ ID_ATTR_NAME }"` );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// check to ensure the geometries are using consistent attributes and indices
|
|
// check to ensure the geometries are using consistent attributes and indices
|
|
const batchGeometry = this.geometry;
|
|
const batchGeometry = this.geometry;
|
|
if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {
|
|
if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {
|
|
@@ -265,12 +266,6 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
for ( const attributeName in batchGeometry.attributes ) {
|
|
for ( const attributeName in batchGeometry.attributes ) {
|
|
|
|
|
|
- if ( attributeName === ID_ATTR_NAME ) {
|
|
|
|
-
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if ( ! geometry.hasAttribute( attributeName ) ) {
|
|
if ( ! geometry.hasAttribute( attributeName ) ) {
|
|
|
|
|
|
throw new Error( `BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
|
|
throw new Error( `BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
|
|
@@ -306,12 +301,12 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const geometryCount = this._geometryCount;
|
|
const geometryCount = this._geometryCount;
|
|
const boundingBox = this.boundingBox;
|
|
const boundingBox = this.boundingBox;
|
|
- const active = this._active;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
|
|
boundingBox.makeEmpty();
|
|
boundingBox.makeEmpty();
|
|
for ( let i = 0; i < geometryCount; i ++ ) {
|
|
for ( let i = 0; i < geometryCount; i ++ ) {
|
|
|
|
|
|
- if ( active[ i ] === false ) continue;
|
|
|
|
|
|
+ if ( drawInfo[ i ].active === false ) continue;
|
|
|
|
|
|
this.getMatrixAt( i, _matrix );
|
|
this.getMatrixAt( i, _matrix );
|
|
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
|
|
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
|
|
@@ -331,12 +326,12 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const geometryCount = this._geometryCount;
|
|
const geometryCount = this._geometryCount;
|
|
const boundingSphere = this.boundingSphere;
|
|
const boundingSphere = this.boundingSphere;
|
|
- const active = this._active;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
|
|
boundingSphere.makeEmpty();
|
|
boundingSphere.makeEmpty();
|
|
for ( let i = 0; i < geometryCount; i ++ ) {
|
|
for ( let i = 0; i < geometryCount; i ++ ) {
|
|
|
|
|
|
- if ( active[ i ] === false ) continue;
|
|
|
|
|
|
+ if ( drawInfo[ i ].active === false ) continue;
|
|
|
|
|
|
this.getMatrixAt( i, _matrix );
|
|
this.getMatrixAt( i, _matrix );
|
|
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );
|
|
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );
|
|
@@ -346,6 +341,34 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ addInstance( id ) {
|
|
|
|
+
|
|
|
|
+ // ensure we're not over geometry
|
|
|
|
+ if ( this._drawInfo.length >= this._maxItemCount ) {
|
|
|
|
+
|
|
|
|
+ throw new Error( 'BatchedMesh: Maximum item count reached.' );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._drawInfo.push( {
|
|
|
|
+
|
|
|
|
+ visible: true,
|
|
|
|
+ active: true,
|
|
|
|
+ geometryIndex: this._drawInfo[ id ].geometryIndex,
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ // initialize the matrix
|
|
|
|
+ const drawId = this._drawInfo.length - 1;
|
|
|
|
+ const matricesTexture = this._matricesTexture;
|
|
|
|
+ const matricesArray = matricesTexture.image.data;
|
|
|
|
+ _identityMatrix.toArray( matricesArray, drawId * 16 );
|
|
|
|
+ matricesTexture.needsUpdate = true;
|
|
|
|
+
|
|
|
|
+ return drawId;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
addGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {
|
|
addGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {
|
|
|
|
|
|
this._initializeGeometry( geometry );
|
|
this._initializeGeometry( geometry );
|
|
@@ -353,9 +376,9 @@ class BatchedMesh extends Mesh {
|
|
this._validateGeometry( geometry );
|
|
this._validateGeometry( geometry );
|
|
|
|
|
|
// ensure we're not over geometry
|
|
// ensure we're not over geometry
|
|
- if ( this._geometryCount >= this._maxGeometryCount ) {
|
|
|
|
|
|
+ if ( this._drawInfo.length >= this._maxItemCount ) {
|
|
|
|
|
|
- throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
|
|
|
|
|
|
+ throw new Error( 'BatchedMesh: Maximum item count reached.' );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -433,32 +456,10 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- const visibility = this._visibility;
|
|
|
|
- const active = this._active;
|
|
|
|
- const matricesTexture = this._matricesTexture;
|
|
|
|
- const matricesArray = this._matricesTexture.image.data;
|
|
|
|
- const colorsTexture = this._colorsTexture;
|
|
|
|
-
|
|
|
|
- // push new visibility states
|
|
|
|
- visibility.push( true );
|
|
|
|
- active.push( true );
|
|
|
|
-
|
|
|
|
// update id
|
|
// update id
|
|
const geometryId = this._geometryCount;
|
|
const geometryId = this._geometryCount;
|
|
this._geometryCount ++;
|
|
this._geometryCount ++;
|
|
|
|
|
|
- // initialize matrix information
|
|
|
|
- _identityMatrix.toArray( matricesArray, geometryId * 16 );
|
|
|
|
- matricesTexture.needsUpdate = true;
|
|
|
|
-
|
|
|
|
- // initialize the color to white
|
|
|
|
- if ( colorsTexture !== null ) {
|
|
|
|
-
|
|
|
|
- _whiteColor.toArray( colorsTexture.image.data, geometryId * 4 );
|
|
|
|
- colorsTexture.needsUpdate = true;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// add the reserved range and draw range objects
|
|
// add the reserved range and draw range objects
|
|
reservedRanges.push( reservedRange );
|
|
reservedRanges.push( reservedRange );
|
|
drawRanges.push( {
|
|
drawRanges.push( {
|
|
@@ -473,26 +474,41 @@ class BatchedMesh extends Mesh {
|
|
sphere: new Sphere()
|
|
sphere: new Sphere()
|
|
} );
|
|
} );
|
|
|
|
|
|
- // set the id for the geometry
|
|
|
|
- const idAttribute = this.geometry.getAttribute( ID_ATTR_NAME );
|
|
|
|
- for ( let i = 0; i < reservedRange.vertexCount; i ++ ) {
|
|
|
|
|
|
+ // push new draw info states
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ const matricesTexture = this._matricesTexture;
|
|
|
|
+ const matricesArray = this._matricesTexture.image.data;
|
|
|
|
+ const colorsTexture = this._colorsTexture;
|
|
|
|
+ const drawId = drawInfo.length;
|
|
|
|
+ drawInfo.push( {
|
|
|
|
+ visible: true,
|
|
|
|
+ active: true,
|
|
|
|
+ geometryIndex: geometryId,
|
|
|
|
+ } );
|
|
|
|
|
|
- idAttribute.setX( reservedRange.vertexStart + i, geometryId );
|
|
|
|
|
|
+ // initialize matrix information
|
|
|
|
+ _identityMatrix.toArray( matricesArray, drawId * 16 );
|
|
|
|
+ matricesTexture.needsUpdate = true;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // initialize the color to white
|
|
|
|
+ if ( colorsTexture !== null ) {
|
|
|
|
|
|
- idAttribute.needsUpdate = true;
|
|
|
|
|
|
+ _whiteColor.toArray( colorsTexture.image.data, drawId * 4 );
|
|
|
|
+ colorsTexture.needsUpdate = true;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
// update the geometry
|
|
// update the geometry
|
|
- this.setGeometryAt( geometryId, geometry );
|
|
|
|
|
|
+ this.setGeometryAt( drawId, geometry );
|
|
|
|
|
|
- return geometryId;
|
|
|
|
|
|
+ return drawId;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
setGeometryAt( id, geometry ) {
|
|
setGeometryAt( id, geometry ) {
|
|
|
|
|
|
- if ( id >= this._geometryCount ) {
|
|
|
|
|
|
+ const geometryId = this._drawInfo[ id ].geometryIndex;
|
|
|
|
+ if ( geometryId >= this._geometryCount ) {
|
|
|
|
|
|
throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
|
|
throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
|
|
|
|
|
|
@@ -504,7 +520,7 @@ class BatchedMesh extends Mesh {
|
|
const hasIndex = batchGeometry.getIndex() !== null;
|
|
const hasIndex = batchGeometry.getIndex() !== null;
|
|
const dstIndex = batchGeometry.getIndex();
|
|
const dstIndex = batchGeometry.getIndex();
|
|
const srcIndex = geometry.getIndex();
|
|
const srcIndex = geometry.getIndex();
|
|
- const reservedRange = this._reservedRanges[ id ];
|
|
|
|
|
|
+ const reservedRange = this._reservedRanges[ geometryId ];
|
|
if (
|
|
if (
|
|
hasIndex &&
|
|
hasIndex &&
|
|
srcIndex.count > reservedRange.indexCount ||
|
|
srcIndex.count > reservedRange.indexCount ||
|
|
@@ -520,12 +536,6 @@ class BatchedMesh extends Mesh {
|
|
const vertexCount = reservedRange.vertexCount;
|
|
const vertexCount = reservedRange.vertexCount;
|
|
for ( const attributeName in batchGeometry.attributes ) {
|
|
for ( const attributeName in batchGeometry.attributes ) {
|
|
|
|
|
|
- if ( attributeName === ID_ATTR_NAME ) {
|
|
|
|
-
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// copy attribute data
|
|
// copy attribute data
|
|
const srcAttribute = geometry.getAttribute( attributeName );
|
|
const srcAttribute = geometry.getAttribute( attributeName );
|
|
const dstAttribute = batchGeometry.getAttribute( attributeName );
|
|
const dstAttribute = batchGeometry.getAttribute( attributeName );
|
|
@@ -574,7 +584,7 @@ class BatchedMesh extends Mesh {
|
|
}
|
|
}
|
|
|
|
|
|
// store the bounding boxes
|
|
// store the bounding boxes
|
|
- const bound = this._bounds[ id ];
|
|
|
|
|
|
+ const bound = this._bounds[ geometryId ];
|
|
if ( geometry.boundingBox !== null ) {
|
|
if ( geometry.boundingBox !== null ) {
|
|
|
|
|
|
bound.box.copy( geometry.boundingBox );
|
|
bound.box.copy( geometry.boundingBox );
|
|
@@ -598,67 +608,46 @@ class BatchedMesh extends Mesh {
|
|
}
|
|
}
|
|
|
|
|
|
// set drawRange count
|
|
// set drawRange count
|
|
- const drawRange = this._drawRanges[ id ];
|
|
|
|
|
|
+ const drawRange = this._drawRanges[ geometryId ];
|
|
const posAttr = geometry.getAttribute( 'position' );
|
|
const posAttr = geometry.getAttribute( 'position' );
|
|
drawRange.count = hasIndex ? srcIndex.count : posAttr.count;
|
|
drawRange.count = hasIndex ? srcIndex.count : posAttr.count;
|
|
this._visibilityChanged = true;
|
|
this._visibilityChanged = true;
|
|
|
|
|
|
- return id;
|
|
|
|
|
|
+ return geometryId;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- deleteGeometry( geometryId ) {
|
|
|
|
|
|
+ deleteInstance( id ) {
|
|
|
|
|
|
// Note: User needs to call optimize() afterward to pack the data.
|
|
// Note: User needs to call optimize() afterward to pack the data.
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
- if ( geometryId >= active.length || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- active[ geometryId ] = false;
|
|
|
|
|
|
+ drawInfo[ id ].active = false;
|
|
this._visibilityChanged = true;
|
|
this._visibilityChanged = true;
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- getInstanceCountAt( id ) {
|
|
|
|
-
|
|
|
|
- if ( this._multiDrawInstances === null ) return null;
|
|
|
|
-
|
|
|
|
- return this._multiDrawInstances[ id ];
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- setInstanceCountAt( id, instanceCount ) {
|
|
|
|
-
|
|
|
|
- if ( this._multiDrawInstances === null ) {
|
|
|
|
-
|
|
|
|
- this._multiDrawInstances = new Int32Array( this._maxGeometryCount ).fill( 1 );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this._multiDrawInstances[ id ] = instanceCount;
|
|
|
|
-
|
|
|
|
- return id;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// get bounding box and compute it if it doesn't exist
|
|
// get bounding box and compute it if it doesn't exist
|
|
getBoundingBoxAt( id, target ) {
|
|
getBoundingBoxAt( id, target ) {
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
- if ( active[ id ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return null;
|
|
return null;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// compute bounding box
|
|
// compute bounding box
|
|
- const bound = this._bounds[ id ];
|
|
|
|
|
|
+ const geometryId = drawInfo[ id ].geometryIndex;
|
|
|
|
+ const bound = this._bounds[ geometryId ];
|
|
const box = bound.box;
|
|
const box = bound.box;
|
|
const geometry = this.geometry;
|
|
const geometry = this.geometry;
|
|
if ( bound.boxInitialized === false ) {
|
|
if ( bound.boxInitialized === false ) {
|
|
@@ -667,7 +656,7 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const index = geometry.index;
|
|
const index = geometry.index;
|
|
const position = geometry.attributes.position;
|
|
const position = geometry.attributes.position;
|
|
- const drawRange = this._drawRanges[ id ];
|
|
|
|
|
|
+ const drawRange = this._drawRanges[ geometryId ];
|
|
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
|
|
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
|
|
|
|
|
|
let iv = i;
|
|
let iv = i;
|
|
@@ -693,27 +682,28 @@ class BatchedMesh extends Mesh {
|
|
// get bounding sphere and compute it if it doesn't exist
|
|
// get bounding sphere and compute it if it doesn't exist
|
|
getBoundingSphereAt( id, target ) {
|
|
getBoundingSphereAt( id, target ) {
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
- if ( active[ id ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return null;
|
|
return null;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// compute bounding sphere
|
|
// compute bounding sphere
|
|
- const bound = this._bounds[ id ];
|
|
|
|
|
|
+ const geometryId = drawInfo[ id ].geometryIndex;
|
|
|
|
+ const bound = this._bounds[ geometryId ];
|
|
const sphere = bound.sphere;
|
|
const sphere = bound.sphere;
|
|
const geometry = this.geometry;
|
|
const geometry = this.geometry;
|
|
if ( bound.sphereInitialized === false ) {
|
|
if ( bound.sphereInitialized === false ) {
|
|
|
|
|
|
sphere.makeEmpty();
|
|
sphere.makeEmpty();
|
|
|
|
|
|
- this.getBoundingBoxAt( id, _box );
|
|
|
|
|
|
+ this.getBoundingBoxAt( geometryId, _box );
|
|
_box.getCenter( sphere.center );
|
|
_box.getCenter( sphere.center );
|
|
|
|
|
|
const index = geometry.index;
|
|
const index = geometry.index;
|
|
const position = geometry.attributes.position;
|
|
const position = geometry.attributes.position;
|
|
- const drawRange = this._drawRanges[ id ];
|
|
|
|
|
|
+ const drawRange = this._drawRanges[ geometryId ];
|
|
|
|
|
|
let maxRadiusSq = 0;
|
|
let maxRadiusSq = 0;
|
|
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
|
|
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
|
|
@@ -740,44 +730,42 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- setMatrixAt( geometryId, matrix ) {
|
|
|
|
|
|
+ setMatrixAt( id, matrix ) {
|
|
|
|
|
|
// @TODO: Map geometryId to index of the arrays because
|
|
// @TODO: Map geometryId to index of the arrays because
|
|
// optimize() can make geometryId mismatch the index
|
|
// optimize() can make geometryId mismatch the index
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
const matricesTexture = this._matricesTexture;
|
|
const matricesTexture = this._matricesTexture;
|
|
const matricesArray = this._matricesTexture.image.data;
|
|
const matricesArray = this._matricesTexture.image.data;
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
- if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- matrix.toArray( matricesArray, geometryId * 16 );
|
|
|
|
|
|
+ matrix.toArray( matricesArray, id * 16 );
|
|
matricesTexture.needsUpdate = true;
|
|
matricesTexture.needsUpdate = true;
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- getMatrixAt( geometryId, matrix ) {
|
|
|
|
|
|
+ getMatrixAt( id, matrix ) {
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
const matricesArray = this._matricesTexture.image.data;
|
|
const matricesArray = this._matricesTexture.image.data;
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
- if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return null;
|
|
return null;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- return matrix.fromArray( matricesArray, geometryId * 16 );
|
|
|
|
|
|
+ return matrix.fromArray( matricesArray, id * 16 );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- setColorAt( geometryId, color ) {
|
|
|
|
|
|
+ setColorAt( id, color ) {
|
|
|
|
|
|
if ( this._colorsTexture === null ) {
|
|
if ( this._colorsTexture === null ) {
|
|
|
|
|
|
@@ -785,89 +773,79 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // @TODO: Map geometryId to index of the arrays because
|
|
|
|
- // optimize() can make geometryId mismatch the index
|
|
|
|
|
|
+ // @TODO: Map id to index of the arrays because
|
|
|
|
+ // optimize() can make id mismatch the index
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
const colorsTexture = this._colorsTexture;
|
|
const colorsTexture = this._colorsTexture;
|
|
const colorsArray = this._colorsTexture.image.data;
|
|
const colorsArray = this._colorsTexture.image.data;
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
- if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- color.toArray( colorsArray, geometryId * 4 );
|
|
|
|
|
|
+ color.toArray( colorsArray, id * 4 );
|
|
colorsTexture.needsUpdate = true;
|
|
colorsTexture.needsUpdate = true;
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- getColorAt( geometryId, color ) {
|
|
|
|
|
|
+ getColorAt( id, color ) {
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
const colorsArray = this._colorsTexture.image.data;
|
|
const colorsArray = this._colorsTexture.image.data;
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
- if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return null;
|
|
return null;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- return color.fromArray( colorsArray, geometryId * 4 );
|
|
|
|
|
|
+ return color.fromArray( colorsArray, id * 4 );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- setVisibleAt( geometryId, value ) {
|
|
|
|
-
|
|
|
|
- const visibility = this._visibility;
|
|
|
|
- const active = this._active;
|
|
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
|
|
+ setVisibleAt( id, value ) {
|
|
|
|
|
|
// if the geometry is out of range, not active, or visibility state
|
|
// if the geometry is out of range, not active, or visibility state
|
|
// does not change then return early
|
|
// does not change then return early
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
if (
|
|
if (
|
|
- geometryId >= geometryCount ||
|
|
|
|
- active[ geometryId ] === false ||
|
|
|
|
- visibility[ geometryId ] === value
|
|
|
|
|
|
+ id >= drawInfo.length ||
|
|
|
|
+ drawInfo[ id ].active === false ||
|
|
|
|
+ drawInfo[ id ].visible === value
|
|
) {
|
|
) {
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- visibility[ geometryId ] = value;
|
|
|
|
|
|
+ drawInfo[ id ].visible = value;
|
|
this._visibilityChanged = true;
|
|
this._visibilityChanged = true;
|
|
|
|
|
|
return this;
|
|
return this;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- getVisibleAt( geometryId ) {
|
|
|
|
-
|
|
|
|
- const visibility = this._visibility;
|
|
|
|
- const active = this._active;
|
|
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
|
|
+ getVisibleAt( id ) {
|
|
|
|
|
|
// return early if the geometry is out of range or not active
|
|
// return early if the geometry is out of range or not active
|
|
- if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
|
|
+ if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
|
|
|
|
|
|
return false;
|
|
return false;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- return visibility[ geometryId ];
|
|
|
|
|
|
+ return drawInfo[ id ].visible;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
raycast( raycaster, intersects ) {
|
|
raycast( raycaster, intersects ) {
|
|
|
|
|
|
- const visibility = this._visibility;
|
|
|
|
- const active = this._active;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
const drawRanges = this._drawRanges;
|
|
const drawRanges = this._drawRanges;
|
|
- const geometryCount = this._geometryCount;
|
|
|
|
const matrixWorld = this.matrixWorld;
|
|
const matrixWorld = this.matrixWorld;
|
|
const batchGeometry = this.geometry;
|
|
const batchGeometry = this.geometry;
|
|
|
|
|
|
@@ -887,15 +865,16 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- for ( let i = 0; i < geometryCount; i ++ ) {
|
|
|
|
|
|
+ for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( ! visibility[ i ] || ! active[ i ] ) {
|
|
|
|
|
|
+ if ( ! drawInfo[ i ].visible || ! drawInfo[ i ].active ) {
|
|
|
|
|
|
continue;
|
|
continue;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- const drawRange = drawRanges[ i ];
|
|
|
|
|
|
+ const geometryId = drawInfo[ i ].geometryIndex;
|
|
|
|
+ const drawRange = drawRanges[ geometryId ];
|
|
_mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
|
|
_mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
|
|
|
|
|
|
// ge the intersects
|
|
// ge the intersects
|
|
@@ -938,8 +917,7 @@ class BatchedMesh extends Mesh {
|
|
this._drawRanges = source._drawRanges.map( range => ( { ...range } ) );
|
|
this._drawRanges = source._drawRanges.map( range => ( { ...range } ) );
|
|
this._reservedRanges = source._reservedRanges.map( range => ( { ...range } ) );
|
|
this._reservedRanges = source._reservedRanges.map( range => ( { ...range } ) );
|
|
|
|
|
|
- this._visibility = source._visibility.slice();
|
|
|
|
- this._active = source._active.slice();
|
|
|
|
|
|
+ this._drawInfo = source._info.map( inf => ( { ...inf } ) );
|
|
this._bounds = source._bounds.map( bound => ( {
|
|
this._bounds = source._bounds.map( bound => ( {
|
|
boxInitialized: bound.boxInitialized,
|
|
boxInitialized: bound.boxInitialized,
|
|
box: bound.box.clone(),
|
|
box: bound.box.clone(),
|
|
@@ -948,7 +926,7 @@ class BatchedMesh extends Mesh {
|
|
sphere: bound.sphere.clone()
|
|
sphere: bound.sphere.clone()
|
|
} ) );
|
|
} ) );
|
|
|
|
|
|
- this._maxGeometryCount = source._maxGeometryCount;
|
|
|
|
|
|
+ this._maxItemCount = source._maxItemCount;
|
|
this._maxVertexCount = source._maxVertexCount;
|
|
this._maxVertexCount = source._maxVertexCount;
|
|
this._maxIndexCount = source._maxIndexCount;
|
|
this._maxIndexCount = source._maxIndexCount;
|
|
|
|
|
|
@@ -979,6 +957,9 @@ class BatchedMesh extends Mesh {
|
|
this._matricesTexture.dispose();
|
|
this._matricesTexture.dispose();
|
|
this._matricesTexture = null;
|
|
this._matricesTexture = null;
|
|
|
|
|
|
|
|
+ this._indirectTexture.dispose();
|
|
|
|
+ this._indirectTexture = null;
|
|
|
|
+
|
|
if ( this._colorsTexture !== null ) {
|
|
if ( this._colorsTexture !== null ) {
|
|
|
|
|
|
this._colorsTexture.dispose();
|
|
this._colorsTexture.dispose();
|
|
@@ -1005,12 +986,13 @@ class BatchedMesh extends Mesh {
|
|
const index = geometry.getIndex();
|
|
const index = geometry.getIndex();
|
|
const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;
|
|
const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;
|
|
|
|
|
|
- const active = this._active;
|
|
|
|
- const visibility = this._visibility;
|
|
|
|
|
|
+ const drawInfo = this._drawInfo;
|
|
const multiDrawStarts = this._multiDrawStarts;
|
|
const multiDrawStarts = this._multiDrawStarts;
|
|
const multiDrawCounts = this._multiDrawCounts;
|
|
const multiDrawCounts = this._multiDrawCounts;
|
|
const drawRanges = this._drawRanges;
|
|
const drawRanges = this._drawRanges;
|
|
const perObjectFrustumCulled = this.perObjectFrustumCulled;
|
|
const perObjectFrustumCulled = this.perObjectFrustumCulled;
|
|
|
|
+ const indirectTexture = this._indirectTexture;
|
|
|
|
+ const indirectArray = indirectTexture.image.data;
|
|
|
|
|
|
// prepare the frustum in the local frame
|
|
// prepare the frustum in the local frame
|
|
if ( perObjectFrustumCulled ) {
|
|
if ( perObjectFrustumCulled ) {
|
|
@@ -1033,9 +1015,9 @@ class BatchedMesh extends Mesh {
|
|
_vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _invMatrixWorld );
|
|
_vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _invMatrixWorld );
|
|
_forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _invMatrixWorld );
|
|
_forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _invMatrixWorld );
|
|
|
|
|
|
- for ( let i = 0, l = visibility.length; i < l; i ++ ) {
|
|
|
|
|
|
+ for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( visibility[ i ] && active[ i ] ) {
|
|
|
|
|
|
+ if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
|
|
|
|
|
|
// get the bounds in world space
|
|
// get the bounds in world space
|
|
this.getMatrixAt( i, _matrix );
|
|
this.getMatrixAt( i, _matrix );
|
|
@@ -1053,7 +1035,7 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
// get the distance from camera used for sorting
|
|
// get the distance from camera used for sorting
|
|
const z = _temp.subVectors( _sphere.center, _vector ).dot( _forward );
|
|
const z = _temp.subVectors( _sphere.center, _vector ).dot( _forward );
|
|
- _renderList.push( drawRanges[ i ], z );
|
|
|
|
|
|
+ _renderList.push( drawRanges[ drawInfo[ i ].geometryIndex ], z, i );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1079,6 +1061,7 @@ class BatchedMesh extends Mesh {
|
|
const item = list[ i ];
|
|
const item = list[ i ];
|
|
multiDrawStarts[ count ] = item.start * bytesPerElement;
|
|
multiDrawStarts[ count ] = item.start * bytesPerElement;
|
|
multiDrawCounts[ count ] = item.count;
|
|
multiDrawCounts[ count ] = item.count;
|
|
|
|
+ indirectArray[ count ] = item.index;
|
|
count ++;
|
|
count ++;
|
|
|
|
|
|
}
|
|
}
|
|
@@ -1087,9 +1070,9 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
} else {
|
|
} else {
|
|
|
|
|
|
- for ( let i = 0, l = visibility.length; i < l; i ++ ) {
|
|
|
|
|
|
+ for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( visibility[ i ] && active[ i ] ) {
|
|
|
|
|
|
+ if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
|
|
|
|
|
|
// determine whether the batched geometry is within the frustum
|
|
// determine whether the batched geometry is within the frustum
|
|
let culled = false;
|
|
let culled = false;
|
|
@@ -1104,9 +1087,10 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
if ( ! culled ) {
|
|
if ( ! culled ) {
|
|
|
|
|
|
- const range = drawRanges[ i ];
|
|
|
|
|
|
+ const range = drawRanges[ drawInfo[ i ].geometryIndex ];
|
|
multiDrawStarts[ count ] = range.start * bytesPerElement;
|
|
multiDrawStarts[ count ] = range.start * bytesPerElement;
|
|
multiDrawCounts[ count ] = range.count;
|
|
multiDrawCounts[ count ] = range.count;
|
|
|
|
+ indirectArray[ count ] = i;
|
|
count ++;
|
|
count ++;
|
|
|
|
|
|
}
|
|
}
|
|
@@ -1117,6 +1101,7 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ indirectTexture.needsUpdate = true;
|
|
this._multiDrawCount = count;
|
|
this._multiDrawCount = count;
|
|
this._visibilityChanged = false;
|
|
this._visibilityChanged = false;
|
|
|
|
|