Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BatchedMesh: Add setGeometryAt, addGeometry functions #27078

Merged
merged 21 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 193 additions & 68 deletions examples/jsm/objects/BatchedMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class BatchedMesh extends Mesh {
this._vertexCounts = [];
this._indexStarts = [];
this._indexCounts = [];
this._reservedRanges = [];

this._visible = [];
this._active = [];
Expand Down Expand Up @@ -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;
Expand All @@ -265,94 +310,176 @@ 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 ) {

throw new Error( 'BatchedMesh: Maximum geometry count reached.' );

}

// 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 ) {
Expand All @@ -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;

}

Expand Down
2 changes: 1 addition & 1 deletion examples/webgl_mesh_batch.html
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down