|
@@ -103,6 +103,7 @@ class BatchedMesh extends Mesh {
|
|
|
this._vertexCounts = [];
|
|
|
this._indexStarts = [];
|
|
|
this._indexCounts = [];
|
|
|
+ this._reservedRanges = [];
|
|
|
|
|
|
this._visible = [];
|
|
|
this._active = [];
|
|
@@ -247,6 +248,50 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Make sure the geometry is compatible with the existing combined geometry atributes
|
|
|
+ _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
|
|
|
+ const batchGeometry = this.geometry;
|
|
|
+ if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {
|
|
|
+
|
|
|
+ throw new Error( 'BatchedMesh: All geometries must consistently have "index".' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( const attributeName in batchGeometry.attributes ) {
|
|
|
+
|
|
|
+ if ( attributeName === ID_ATTR_NAME ) {
|
|
|
+
|
|
|
+ continue;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( ! geometry.hasAttribute( attributeName ) ) {
|
|
|
+
|
|
|
+ throw new Error( `BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const srcAttribute = geometry.getAttribute( attributeName );
|
|
|
+ const dstAttribute = batchGeometry.getAttribute( attributeName );
|
|
|
+ if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) {
|
|
|
+
|
|
|
+ throw new Error( 'BatchedMesh: All attributes must have a consistent itemSize and normalized value.' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
getGeometryCount() {
|
|
|
|
|
|
return this._geometryCount;
|
|
@@ -265,10 +310,12 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
|
|
|
|
- applyGeometry( geometry ) {
|
|
|
+ addGeometry( geometry, vertexCount = - 1, indexCount = - 1 ) {
|
|
|
|
|
|
this._initializeGeometry( geometry );
|
|
|
|
|
|
+ this._validateGeometry( geometry );
|
|
|
+
|
|
|
// ensure we're not over geometry
|
|
|
if ( this._geometryCount >= this._maxGeometryCount ) {
|
|
|
|
|
@@ -276,83 +323,163 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
}
|
|
|
|
|
|
- // check that the geometry doesn't have a version of our reserved id attribute
|
|
|
- if ( geometry.getAttribute( ID_ATTR_NAME ) ) {
|
|
|
+ // get the necessary range fo the geometry
|
|
|
+ const range = {
|
|
|
+ vertexStart: - 1,
|
|
|
+ vertexCount: - 1,
|
|
|
+ indexStart: - 1,
|
|
|
+ indexCount: - 1,
|
|
|
+ };
|
|
|
|
|
|
- throw new Error( `BatchedMesh: Geometry cannot use attribute "${ ID_ATTR_NAME }"` );
|
|
|
+ let lastRange = null;
|
|
|
+ const reservedRanges = this._reservedRanges;
|
|
|
+ if ( this._geometryCount !== 0 ) {
|
|
|
+
|
|
|
+ lastRange = reservedRanges[ reservedRanges.length - 1 ];
|
|
|
|
|
|
}
|
|
|
|
|
|
- // check to ensure the geometries are using consistent attributes and indices
|
|
|
- const batchGeometry = this.geometry;
|
|
|
- if ( Boolean( geometry.getIndex() ) !== Boolean( batchGeometry.getIndex() ) ) {
|
|
|
+ if ( vertexCount === - 1 ) {
|
|
|
|
|
|
- throw new Error( 'BatchedMesh: All geometries must consistently have "index".' );
|
|
|
+ range.vertexCount = geometry.getAttribute( 'position' ).count;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ range.vertexCount = vertexCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( const attributeName in batchGeometry.attributes ) {
|
|
|
+ if ( lastRange === null ) {
|
|
|
|
|
|
- if ( attributeName === ID_ATTR_NAME ) {
|
|
|
+ range.vertexStart = 0;
|
|
|
|
|
|
- continue;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ range.vertexStart = lastRange.vertexStart + lastRange.vertexCount;
|
|
|
|
|
|
- if ( ! geometry.hasAttribute( attributeName ) ) {
|
|
|
+ }
|
|
|
|
|
|
- throw new Error( `BatchedMesh: Added geometry missing "${ attributeName }". All geometries must have consistent attributes.` );
|
|
|
+ if ( geometry.getIndex() !== null ) {
|
|
|
+
|
|
|
+ if ( indexCount === - 1 ) {
|
|
|
+
|
|
|
+ range.indexCount = geometry.getIndex().count;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ range.indexCount = indexCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
- const srcAttribute = geometry.getAttribute( attributeName );
|
|
|
- const dstAttribute = batchGeometry.getAttribute( attributeName );
|
|
|
- if ( srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized ) {
|
|
|
+ if ( lastRange === null ) {
|
|
|
|
|
|
- throw new Error( 'BatchedMesh: All attributes must have a consistent itemSize and normalized value.' );
|
|
|
+ range.indexStart = 0;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ range.indexStart = lastRange.indexStart + lastRange.indexCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- // Assuming geometry has position attribute
|
|
|
- const srcPositionAttribute = geometry.getAttribute( 'position' );
|
|
|
- const vertexCount = this._vertexCount;
|
|
|
- const indexCount = this._indexCount;
|
|
|
- const maxVertexCount = this._maxVertexCount;
|
|
|
- const maxIndexCount = this._maxIndexCount;
|
|
|
-
|
|
|
- // check if we're going over our maximum buffer capacity
|
|
|
if (
|
|
|
- geometry.getIndex() !== null &&
|
|
|
- indexCount + geometry.getIndex().count > maxIndexCount ||
|
|
|
- vertexCount + srcPositionAttribute.count > maxVertexCount
|
|
|
+ range.indexStart !== - 1 &&
|
|
|
+ range.indexStart + range.indexCount > this._maxIndexCount ||
|
|
|
+ range.vertexStart + range.vertexCount > this._maxVertexCount
|
|
|
) {
|
|
|
|
|
|
- throw new Error( 'BatchedMesh: Added geometry is larger than available buffer capacity.' );
|
|
|
+ throw new Error( 'BatchedMesh: Reserved space request exceeds the maximum buffer size.' );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ const indexCounts = this._indexCounts;
|
|
|
+ const indexStarts = this._indexStarts;
|
|
|
+ const vertexCounts = this._vertexCounts;
|
|
|
+ const vertexStarts = this._vertexStarts;
|
|
|
+
|
|
|
const visible = this._visible;
|
|
|
const active = this._active;
|
|
|
const matricesTexture = this._matricesTexture;
|
|
|
const matrices = this._matrices;
|
|
|
const matricesArray = this._matricesTexture.image.data;
|
|
|
|
|
|
- const indexCounts = this._indexCounts;
|
|
|
- const indexStarts = this._indexStarts;
|
|
|
- const vertexCounts = this._vertexCounts;
|
|
|
- const vertexStarts = this._vertexStarts;
|
|
|
+ // push new visibility states
|
|
|
+ visible.push( true );
|
|
|
+ active.push( true );
|
|
|
+
|
|
|
+ // update id
|
|
|
+ const geometryId = this._geometryCount;
|
|
|
+ this._geometryCount ++;
|
|
|
+
|
|
|
+ // initialize matrix information
|
|
|
+ matrices.push( new Matrix4() );
|
|
|
+ _identityMatrix.toArray( matricesArray, geometryId * 16 );
|
|
|
+ matricesTexture.needsUpdate = true;
|
|
|
+
|
|
|
+ // add the reserved range
|
|
|
+ reservedRanges.push( range );
|
|
|
+
|
|
|
+ // push new geometry data range
|
|
|
+ vertexStarts.push( range.vertexStart );
|
|
|
+ vertexCounts.push( range.vertexCount );
|
|
|
+
|
|
|
+ if ( geometry.getIndex() !== null ) {
|
|
|
+
|
|
|
+ // push new index range
|
|
|
+ indexStarts.push( range.indexCount );
|
|
|
+ indexCounts.push( range.indexCount );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // set the id for the geometry
|
|
|
+ const idAttribute = this.geometry.getAttribute( ID_ATTR_NAME );
|
|
|
+ for ( let i = 0; i < range.vertexCount; i ++ ) {
|
|
|
+
|
|
|
+ idAttribute.setX( range.vertexStart + i, geometryId );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ idAttribute.needsUpdate = true;
|
|
|
+
|
|
|
+ // update the geometry
|
|
|
+ this.setGeometryAt( geometryId, geometry );
|
|
|
+
|
|
|
+ return geometryId;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ setGeometryAt( id, geometry ) {
|
|
|
+
|
|
|
+ if ( id >= this._geometryCount ) {
|
|
|
+
|
|
|
+ throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ this._validateGeometry( geometry );
|
|
|
+
|
|
|
+ const range = this._reservedRanges[ id ];
|
|
|
+ if (
|
|
|
+ geometry.getIndex() !== null &&
|
|
|
+ geometry.getIndex().count > range.indexCount ||
|
|
|
+ geometry.attributes.position.count > range.vertexCount
|
|
|
+ ) {
|
|
|
+
|
|
|
+ throw new Error( 'BatchedMesh: Reserved space not large enough for provided geometry.' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // copy geometry over
|
|
|
+ const batchGeometry = this.geometry;
|
|
|
+ const srcPositionAttribute = geometry.getAttribute( 'position' );
|
|
|
const hasIndex = batchGeometry.getIndex() !== null;
|
|
|
const dstIndex = batchGeometry.getIndex();
|
|
|
const srcIndex = geometry.getIndex();
|
|
|
|
|
|
- // push new geometry data range
|
|
|
- vertexStarts.push( vertexCount );
|
|
|
- vertexCounts.push( srcPositionAttribute.count );
|
|
|
-
|
|
|
// copy attribute data over
|
|
|
+ const vertexStart = range.vertexStart;
|
|
|
+ const vertexCount = range.vertexCount;
|
|
|
for ( const attributeName in batchGeometry.attributes ) {
|
|
|
|
|
|
if ( attributeName === ID_ATTR_NAME ) {
|
|
@@ -363,54 +490,52 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const srcAttribute = geometry.getAttribute( attributeName );
|
|
|
const dstAttribute = batchGeometry.getAttribute( attributeName );
|
|
|
- copyAttributeData( srcAttribute, dstAttribute, vertexCount );
|
|
|
+ copyAttributeData( srcAttribute, dstAttribute, vertexStart );
|
|
|
|
|
|
- }
|
|
|
+ // fill the rest in with zeroes
|
|
|
+ const itemSize = srcAttribute.itemSize;
|
|
|
+ for ( let i = srcAttribute.count, l = vertexCount; i < l; i ++ ) {
|
|
|
|
|
|
- if ( hasIndex ) {
|
|
|
-
|
|
|
- // push new index range
|
|
|
- indexStarts.push( indexCount );
|
|
|
- indexCounts.push( srcIndex.count );
|
|
|
+ const index = vertexStart + i;
|
|
|
+ for ( let c = 0; c < itemSize; c ++ ) {
|
|
|
|
|
|
- // copy index data over
|
|
|
- for ( let i = 0; i < srcIndex.count; i ++ ) {
|
|
|
+ dstAttribute.setComponent( index, c, 0 );
|
|
|
|
|
|
- dstIndex.setX( indexCount + i, vertexCount + srcIndex.getX( i ) );
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- this._indexCount += srcIndex.count;
|
|
|
- dstIndex.needsUpdate = true;
|
|
|
+ dstAttribute.needsUpdate = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
- // fill in the geometry ids
|
|
|
- const geometryId = this._geometryCount;
|
|
|
- this._geometryCount ++;
|
|
|
+ this._vertexCounts[ id ] = srcPositionAttribute.count;
|
|
|
|
|
|
- const idAttribute = batchGeometry.getAttribute( ID_ATTR_NAME );
|
|
|
- for ( let i = 0; i < srcPositionAttribute.count; i ++ ) {
|
|
|
+ if ( hasIndex ) {
|
|
|
|
|
|
- idAttribute.setX( this._vertexCount + i, geometryId );
|
|
|
+ // fill the rest in with zeroes
|
|
|
+ const indexStart = range.indexStart;
|
|
|
|
|
|
- }
|
|
|
+ // copy index data over
|
|
|
+ for ( let i = 0; i < srcIndex.count; i ++ ) {
|
|
|
|
|
|
- idAttribute.needsUpdate = true;
|
|
|
+ dstIndex.setX( indexStart + i, vertexStart + srcIndex.getX( i ) );
|
|
|
|
|
|
- // extend new range
|
|
|
- this._vertexCount += srcPositionAttribute.count;
|
|
|
+ }
|
|
|
|
|
|
- // push new visibility states
|
|
|
- visible.push( true );
|
|
|
- active.push( true );
|
|
|
+ // fill the rest in with zeroes
|
|
|
+ for ( let i = srcIndex.count, l = range.indexCount; i < l; i ++ ) {
|
|
|
|
|
|
- // initialize matrix information
|
|
|
- matrices.push( new Matrix4() );
|
|
|
- _identityMatrix.toArray( matricesArray, geometryId * 16 );
|
|
|
- matricesTexture.needsUpdate = true;
|
|
|
+ dstIndex.setX( indexStart + i, vertexStart );
|
|
|
|
|
|
- return geometryId;
|
|
|
+ }
|
|
|
+
|
|
|
+ dstIndex.needsUpdate = true;
|
|
|
+ this._indexCounts[ id ] = srcIndex.count;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return id;
|
|
|
|
|
|
}
|
|
|
|