Kaynağa Gözat

BatchedMesh: Add setGeometryAt, addGeometry functions (#27078)

* BatchedMesh: Remove unused fields

* BatchedMesh: Add thrown errors in unimplemented functions

* Add PURE and glsl identifiers

* Add more error handling

* More errors

* Fix batched index overrun

* BatchedMesh: Check for normalized, as well

* Fix screenshot

* Add "setGeometry" function

* Fixes

* Remove "applyGeometry"

* Use "addGeometry" in example

* Swap arguments

* Update set geometry functions

* Cleanup

* Add error messages

* linting

* Reset the BATCHING value

* Remove unnecessary value set

* Swap the batch define true / false order
Garrett Johnson 1 yıl önce
ebeveyn
işleme
baf354647c
2 değiştirilmiş dosya ile 194 ekleme ve 69 silme
  1. 193 68
      examples/jsm/objects/BatchedMesh.js
  2. 1 1
      examples/webgl_mesh_batch.html

+ 193 - 68
examples/jsm/objects/BatchedMesh.js

@@ -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;
 
 	}
 

+ 1 - 1
examples/webgl_mesh_batch.html

@@ -180,7 +180,7 @@
 
 			for ( let i = 0; i < api.count; i ++ ) {
 
-				const id = mesh.applyGeometry( geometries[ i % geometries.length ] );
+				const id = mesh.addGeometry( geometries[ i % geometries.length ] );
 				mesh.setMatrixAt( id, randomizeMatrix( matrix ) );
 
 				const rotationMatrix = new THREE.Matrix4();