diff --git a/examples/webgl_mesh_batch.html b/examples/webgl_mesh_batch.html
index 9dca9cc1084c25..0bf42b24eba25b 100644
--- a/examples/webgl_mesh_batch.html
+++ b/examples/webgl_mesh_batch.html
@@ -179,8 +179,8 @@
function initBatchedMesh() {
const geometryCount = api.count;
- const vertexCount = geometries.length * 512;
- const indexCount = geometries.length * 1024;
+ const vertexCount = api.count * 512;
+ const indexCount = api.count * 1024;
const euler = new THREE.Euler();
const matrix = new THREE.Matrix4();
@@ -194,17 +194,7 @@
for ( let i = 0; i < api.count; i ++ ) {
- let id;
- if ( i < geometries.length ) {
-
- id = mesh.addGeometry( geometries[ i % geometries.length ] );
-
- } else {
-
- id = mesh.addInstance( i % geometries.length );
-
- }
-
+ const id = mesh.addGeometry( geometries[ i % geometries.length ] );
mesh.setMatrixAt( id, randomizeMatrix( matrix ) );
const rotationMatrix = new THREE.Matrix4();
diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js
index e6696825eabce3..19cba48eef205b 100644
--- a/src/objects/BatchedMesh.js
+++ b/src/objects/BatchedMesh.js
@@ -1,7 +1,7 @@
import { BufferAttribute } from '../core/BufferAttribute.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
import { DataTexture } from '../textures/DataTexture.js';
-import { FloatType, RedIntegerFormat, UnsignedIntType } from '../constants.js';
+import { FloatType } from '../constants.js';
import { Matrix4 } from '../math/Matrix4.js';
import { Mesh } from './Mesh.js';
import { RGBAFormat } from '../constants.js';
@@ -34,7 +34,7 @@ class MultiDrawRenderList {
}
- push( drawRange, z, index ) {
+ push( drawRange, z ) {
const pool = this.pool;
const list = this.list;
@@ -45,7 +45,6 @@ class MultiDrawRenderList {
start: - 1,
count: - 1,
z: - 1,
- index: - 1,
} );
@@ -58,7 +57,6 @@ class MultiDrawRenderList {
item.start = drawRange.start;
item.count = drawRange.count;
item.z = z;
- item.index = index;
}
@@ -71,6 +69,7 @@ class MultiDrawRenderList {
}
+const ID_ATTR_NAME = 'batchId';
const _matrix = /*@__PURE__*/ new Matrix4();
const _invMatrixWorld = /*@__PURE__*/ new Matrix4();
const _identityMatrix = /*@__PURE__*/ new Matrix4();
@@ -125,13 +124,13 @@ function copyAttributeData( src, target, targetOffset = 0 ) {
class BatchedMesh extends Mesh {
- get maxItemCount() {
+ get maxGeometryCount() {
- return this._maxItemCount;
+ return this._maxGeometryCount;
}
- constructor( maxDrawCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {
+ constructor( maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material ) {
super( new BufferGeometry(), material );
@@ -142,33 +141,32 @@ class BatchedMesh extends Mesh {
this.boundingSphere = null;
this.customSort = null;
- // stores visible, active, and geometry id per object
- this._drawInfo = [];
-
- // geometry information
this._drawRanges = [];
this._reservedRanges = [];
+
+ this._visibility = [];
+ this._active = [];
this._bounds = [];
- this._maxItemCount = maxDrawCount;
+ this._maxGeometryCount = maxGeometryCount;
this._maxVertexCount = maxVertexCount;
this._maxIndexCount = maxIndexCount;
this._geometryInitialized = false;
this._geometryCount = 0;
- this._multiDrawCounts = new Int32Array( maxDrawCount );
- this._multiDrawStarts = new Int32Array( maxDrawCount );
+ this._multiDrawCounts = new Int32Array( maxGeometryCount );
+ this._multiDrawStarts = new Int32Array( maxGeometryCount );
this._multiDrawCount = 0;
this._multiDrawInstances = null;
this._visibilityChanged = true;
// Local matrix per geometry by using data texture
this._matricesTexture = null;
- this._indirectTexture = null;
- this._colorsTexture = null;
this._initMatricesTexture();
- this._initIndirectTexture();
+
+ // Local color per geometry by using data texture
+ this._colorsTexture = null;
}
@@ -181,7 +179,7 @@ class BatchedMesh extends Mesh {
// 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32)
// 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64)
- let size = Math.sqrt( this._maxItemCount * 4 ); // 4 pixels needed for 1 matrix
+ let size = Math.sqrt( this._maxGeometryCount * 4 ); // 4 pixels needed for 1 matrix
size = Math.ceil( size / 4 ) * 4;
size = Math.max( size, 4 );
@@ -192,21 +190,9 @@ 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() {
- let size = Math.sqrt( this._maxIndexCount );
+ let size = Math.sqrt( this._maxGeometryCount );
size = Math.ceil( size );
// 4 floats per RGBA pixel initialized to white
@@ -222,6 +208,7 @@ class BatchedMesh extends Mesh {
const geometry = this.geometry;
const maxVertexCount = this._maxVertexCount;
+ const maxGeometryCount = this._maxGeometryCount;
const maxIndexCount = this._maxIndexCount;
if ( this._geometryInitialized === false ) {
@@ -247,6 +234,11 @@ 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;
}
@@ -256,6 +248,13 @@ class BatchedMesh extends Mesh {
// Make sure the geometry is compatible with the existing combined geometry attributes
_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() ) ) {
@@ -266,6 +265,12 @@ class BatchedMesh extends Mesh {
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.` );
@@ -301,12 +306,12 @@ class BatchedMesh extends Mesh {
const geometryCount = this._geometryCount;
const boundingBox = this.boundingBox;
- const drawInfo = this._drawInfo;
+ const active = this._active;
boundingBox.makeEmpty();
for ( let i = 0; i < geometryCount; i ++ ) {
- if ( drawInfo[ i ].active === false ) continue;
+ if ( active[ i ] === false ) continue;
this.getMatrixAt( i, _matrix );
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
@@ -326,12 +331,12 @@ class BatchedMesh extends Mesh {
const geometryCount = this._geometryCount;
const boundingSphere = this.boundingSphere;
- const drawInfo = this._drawInfo;
+ const active = this._active;
boundingSphere.makeEmpty();
for ( let i = 0; i < geometryCount; i ++ ) {
- if ( drawInfo[ i ].active === false ) continue;
+ if ( active[ i ] === false ) continue;
this.getMatrixAt( i, _matrix );
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );
@@ -341,34 +346,6 @@ 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 ) {
this._initializeGeometry( geometry );
@@ -376,9 +353,9 @@ class BatchedMesh extends Mesh {
this._validateGeometry( geometry );
// ensure we're not over geometry
- if ( this._drawInfo.length >= this._maxItemCount ) {
+ if ( this._geometryCount >= this._maxGeometryCount ) {
- throw new Error( 'BatchedMesh: Maximum item count reached.' );
+ throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
}
@@ -456,10 +433,32 @@ 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
const geometryId = 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
reservedRanges.push( reservedRange );
drawRanges.push( {
@@ -474,41 +473,26 @@ class BatchedMesh extends Mesh {
sphere: new Sphere()
} );
- // 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,
- } );
-
- // initialize matrix information
- _identityMatrix.toArray( matricesArray, drawId * 16 );
- matricesTexture.needsUpdate = true;
+ // set the id for the geometry
+ const idAttribute = this.geometry.getAttribute( ID_ATTR_NAME );
+ for ( let i = 0; i < reservedRange.vertexCount; i ++ ) {
- // initialize the color to white
- if ( colorsTexture !== null ) {
-
- _whiteColor.toArray( colorsTexture.image.data, drawId * 4 );
- colorsTexture.needsUpdate = true;
+ idAttribute.setX( reservedRange.vertexStart + i, geometryId );
}
+ idAttribute.needsUpdate = true;
+
// update the geometry
- this.setGeometryAt( drawId, geometry );
+ this.setGeometryAt( geometryId, geometry );
- return drawId;
+ return geometryId;
}
setGeometryAt( id, geometry ) {
- const geometryId = this._drawInfo[ id ].geometryIndex;
- if ( geometryId >= this._geometryCount ) {
+ if ( id >= this._geometryCount ) {
throw new Error( 'BatchedMesh: Maximum geometry count reached.' );
@@ -520,7 +504,7 @@ class BatchedMesh extends Mesh {
const hasIndex = batchGeometry.getIndex() !== null;
const dstIndex = batchGeometry.getIndex();
const srcIndex = geometry.getIndex();
- const reservedRange = this._reservedRanges[ geometryId ];
+ const reservedRange = this._reservedRanges[ id ];
if (
hasIndex &&
srcIndex.count > reservedRange.indexCount ||
@@ -536,6 +520,12 @@ class BatchedMesh extends Mesh {
const vertexCount = reservedRange.vertexCount;
for ( const attributeName in batchGeometry.attributes ) {
+ if ( attributeName === ID_ATTR_NAME ) {
+
+ continue;
+
+ }
+
// copy attribute data
const srcAttribute = geometry.getAttribute( attributeName );
const dstAttribute = batchGeometry.getAttribute( attributeName );
@@ -584,7 +574,7 @@ class BatchedMesh extends Mesh {
}
// store the bounding boxes
- const bound = this._bounds[ geometryId ];
+ const bound = this._bounds[ id ];
if ( geometry.boundingBox !== null ) {
bound.box.copy( geometry.boundingBox );
@@ -608,46 +598,67 @@ class BatchedMesh extends Mesh {
}
// set drawRange count
- const drawRange = this._drawRanges[ geometryId ];
+ const drawRange = this._drawRanges[ id ];
const posAttr = geometry.getAttribute( 'position' );
drawRange.count = hasIndex ? srcIndex.count : posAttr.count;
this._visibilityChanged = true;
- return geometryId;
+ return id;
}
- deleteInstance( id ) {
+ deleteGeometry( geometryId ) {
// Note: User needs to call optimize() afterward to pack the data.
- const drawInfo = this._drawInfo;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ const active = this._active;
+ if ( geometryId >= active.length || active[ geometryId ] === false ) {
return this;
}
- drawInfo[ id ].active = false;
+ active[ geometryId ] = false;
this._visibilityChanged = true;
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
getBoundingBoxAt( id, target ) {
- const drawInfo = this._drawInfo;
- if ( drawInfo[ id ].active === false ) {
+ const active = this._active;
+ if ( active[ id ] === false ) {
return null;
}
// compute bounding box
- const geometryId = drawInfo[ id ].geometryIndex;
- const bound = this._bounds[ geometryId ];
+ const bound = this._bounds[ id ];
const box = bound.box;
const geometry = this.geometry;
if ( bound.boxInitialized === false ) {
@@ -656,7 +667,7 @@ class BatchedMesh extends Mesh {
const index = geometry.index;
const position = geometry.attributes.position;
- const drawRange = this._drawRanges[ geometryId ];
+ const drawRange = this._drawRanges[ id ];
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
let iv = i;
@@ -682,28 +693,27 @@ class BatchedMesh extends Mesh {
// get bounding sphere and compute it if it doesn't exist
getBoundingSphereAt( id, target ) {
- const drawInfo = this._drawInfo;
- if ( drawInfo[ id ].active === false ) {
+ const active = this._active;
+ if ( active[ id ] === false ) {
return null;
}
// compute bounding sphere
- const geometryId = drawInfo[ id ].geometryIndex;
- const bound = this._bounds[ geometryId ];
+ const bound = this._bounds[ id ];
const sphere = bound.sphere;
const geometry = this.geometry;
if ( bound.sphereInitialized === false ) {
sphere.makeEmpty();
- this.getBoundingBoxAt( geometryId, _box );
+ this.getBoundingBoxAt( id, _box );
_box.getCenter( sphere.center );
const index = geometry.index;
const position = geometry.attributes.position;
- const drawRange = this._drawRanges[ geometryId ];
+ const drawRange = this._drawRanges[ id ];
let maxRadiusSq = 0;
for ( let i = drawRange.start, l = drawRange.start + drawRange.count; i < l; i ++ ) {
@@ -730,42 +740,44 @@ class BatchedMesh extends Mesh {
}
- setMatrixAt( id, matrix ) {
+ setMatrixAt( geometryId, matrix ) {
// @TODO: Map geometryId to index of the arrays because
// optimize() can make geometryId mismatch the index
- const drawInfo = this._drawInfo;
+ const active = this._active;
const matricesTexture = this._matricesTexture;
const matricesArray = this._matricesTexture.image.data;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
return this;
}
- matrix.toArray( matricesArray, id * 16 );
+ matrix.toArray( matricesArray, geometryId * 16 );
matricesTexture.needsUpdate = true;
return this;
}
- getMatrixAt( id, matrix ) {
+ getMatrixAt( geometryId, matrix ) {
- const drawInfo = this._drawInfo;
+ const active = this._active;
const matricesArray = this._matricesTexture.image.data;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
return null;
}
- return matrix.fromArray( matricesArray, id * 16 );
+ return matrix.fromArray( matricesArray, geometryId * 16 );
}
- setColorAt( id, color ) {
+ setColorAt( geometryId, color ) {
if ( this._colorsTexture === null ) {
@@ -773,79 +785,89 @@ class BatchedMesh extends Mesh {
}
- // @TODO: Map id to index of the arrays because
- // optimize() can make id mismatch the index
+ // @TODO: Map geometryId to index of the arrays because
+ // optimize() can make geometryId mismatch the index
+ const active = this._active;
const colorsTexture = this._colorsTexture;
const colorsArray = this._colorsTexture.image.data;
- const drawInfo = this._drawInfo;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
return this;
}
- color.toArray( colorsArray, id * 4 );
+ color.toArray( colorsArray, geometryId * 4 );
colorsTexture.needsUpdate = true;
return this;
}
- getColorAt( id, color ) {
+ getColorAt( geometryId, color ) {
+ const active = this._active;
const colorsArray = this._colorsTexture.image.data;
- const drawInfo = this._drawInfo;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
return null;
}
- return color.fromArray( colorsArray, id * 4 );
+ return color.fromArray( colorsArray, geometryId * 4 );
}
- setVisibleAt( id, value ) {
+ setVisibleAt( geometryId, value ) {
+
+ const visibility = this._visibility;
+ const active = this._active;
+ const geometryCount = this._geometryCount;
// if the geometry is out of range, not active, or visibility state
// does not change then return early
- const drawInfo = this._drawInfo;
if (
- id >= drawInfo.length ||
- drawInfo[ id ].active === false ||
- drawInfo[ id ].visible === value
+ geometryId >= geometryCount ||
+ active[ geometryId ] === false ||
+ visibility[ geometryId ] === value
) {
return this;
}
- drawInfo[ id ].visible = value;
+ visibility[ geometryId ] = value;
this._visibilityChanged = true;
return this;
}
- getVisibleAt( id ) {
+ getVisibleAt( geometryId ) {
+
+ const visibility = this._visibility;
+ const active = this._active;
+ const geometryCount = this._geometryCount;
// return early if the geometry is out of range or not active
- const drawInfo = this._drawInfo;
- if ( id >= drawInfo.length || drawInfo[ id ].active === false ) {
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
return false;
}
- return drawInfo[ id ].visible;
+ return visibility[ geometryId ];
}
raycast( raycaster, intersects ) {
- const drawInfo = this._drawInfo;
+ const visibility = this._visibility;
+ const active = this._active;
const drawRanges = this._drawRanges;
+ const geometryCount = this._geometryCount;
const matrixWorld = this.matrixWorld;
const batchGeometry = this.geometry;
@@ -865,16 +887,15 @@ class BatchedMesh extends Mesh {
}
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
+ for ( let i = 0; i < geometryCount; i ++ ) {
- if ( ! drawInfo[ i ].visible || ! drawInfo[ i ].active ) {
+ if ( ! visibility[ i ] || ! active[ i ] ) {
continue;
}
- const geometryId = drawInfo[ i ].geometryIndex;
- const drawRange = drawRanges[ geometryId ];
+ const drawRange = drawRanges[ i ];
_mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
// ge the intersects
@@ -917,7 +938,8 @@ class BatchedMesh extends Mesh {
this._drawRanges = source._drawRanges.map( range => ( { ...range } ) );
this._reservedRanges = source._reservedRanges.map( range => ( { ...range } ) );
- this._drawInfo = source._info.map( inf => ( { ...inf } ) );
+ this._visibility = source._visibility.slice();
+ this._active = source._active.slice();
this._bounds = source._bounds.map( bound => ( {
boxInitialized: bound.boxInitialized,
box: bound.box.clone(),
@@ -926,7 +948,7 @@ class BatchedMesh extends Mesh {
sphere: bound.sphere.clone()
} ) );
- this._maxItemCount = source._maxItemCount;
+ this._maxGeometryCount = source._maxGeometryCount;
this._maxVertexCount = source._maxVertexCount;
this._maxIndexCount = source._maxIndexCount;
@@ -957,9 +979,6 @@ class BatchedMesh extends Mesh {
this._matricesTexture.dispose();
this._matricesTexture = null;
- this._indirectTexture.dispose();
- this._indirectTexture = null;
-
if ( this._colorsTexture !== null ) {
this._colorsTexture.dispose();
@@ -986,13 +1005,12 @@ class BatchedMesh extends Mesh {
const index = geometry.getIndex();
const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;
- const drawInfo = this._drawInfo;
+ const active = this._active;
+ const visibility = this._visibility;
const multiDrawStarts = this._multiDrawStarts;
const multiDrawCounts = this._multiDrawCounts;
const drawRanges = this._drawRanges;
const perObjectFrustumCulled = this.perObjectFrustumCulled;
- const indirectTexture = this._indirectTexture;
- const indirectArray = indirectTexture.image.data;
// prepare the frustum in the local frame
if ( perObjectFrustumCulled ) {
@@ -1015,9 +1033,9 @@ class BatchedMesh extends Mesh {
_vector.setFromMatrixPosition( camera.matrixWorld ).applyMatrix4( _invMatrixWorld );
_forward.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _invMatrixWorld );
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
+ for ( let i = 0, l = visibility.length; i < l; i ++ ) {
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
+ if ( visibility[ i ] && active[ i ] ) {
// get the bounds in world space
this.getMatrixAt( i, _matrix );
@@ -1035,7 +1053,7 @@ class BatchedMesh extends Mesh {
// get the distance from camera used for sorting
const z = _temp.subVectors( _sphere.center, _vector ).dot( _forward );
- _renderList.push( drawRanges[ drawInfo[ i ].geometryIndex ], z, i );
+ _renderList.push( drawRanges[ i ], z );
}
@@ -1061,7 +1079,6 @@ class BatchedMesh extends Mesh {
const item = list[ i ];
multiDrawStarts[ count ] = item.start * bytesPerElement;
multiDrawCounts[ count ] = item.count;
- indirectArray[ count ] = item.index;
count ++;
}
@@ -1070,9 +1087,9 @@ class BatchedMesh extends Mesh {
} else {
- for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
+ for ( let i = 0, l = visibility.length; i < l; i ++ ) {
- if ( drawInfo[ i ].visible && drawInfo[ i ].active ) {
+ if ( visibility[ i ] && active[ i ] ) {
// determine whether the batched geometry is within the frustum
let culled = false;
@@ -1087,10 +1104,9 @@ class BatchedMesh extends Mesh {
if ( ! culled ) {
- const range = drawRanges[ drawInfo[ i ].geometryIndex ];
+ const range = drawRanges[ i ];
multiDrawStarts[ count ] = range.start * bytesPerElement;
multiDrawCounts[ count ] = range.count;
- indirectArray[ count ] = i;
count ++;
}
@@ -1101,7 +1117,6 @@ class BatchedMesh extends Mesh {
}
- indirectTexture.needsUpdate = true;
this._multiDrawCount = count;
this._visibilityChanged = false;
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index 86a49d7adfe9f7..ca422d97caa424 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -861,25 +861,7 @@ class WebGLRenderer {
} else {
- if ( ! extensions.get( 'WEBGL_multi_draw' ) ) {
-
- const starts = object._multiDrawStarts;
- const counts = object._multiDrawCounts;
- const drawCount = object._multiDrawCount;
- const bytesPerElement = index ? attributes.get( index ).bytesPerElement : 1;
- const uniforms = properties.get( material ).currentProgram.getUniforms();
- for ( let i = 0; i < drawCount; i ++ ) {
-
- uniforms.setValue( _gl, '_gl_DrawID', i );
- renderer.render( starts[ i ] / bytesPerElement, counts[ i ] );
-
- }
-
- } else {
-
- renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount );
-
- }
+ renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount );
}
@@ -2035,9 +2017,6 @@ class WebGLRenderer {
p_uniforms.setOptional( _gl, object, 'batchingTexture' );
p_uniforms.setValue( _gl, 'batchingTexture', object._matricesTexture, textures );
- p_uniforms.setOptional( _gl, object, 'batchingIdTexture' );
- p_uniforms.setValue( _gl, 'batchingIdTexture', object._indirectTexture, textures );
-
p_uniforms.setOptional( _gl, object, 'batchingColorTexture' );
if ( object._colorsTexture !== null ) {
diff --git a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
index 5ae4c0ac38d660..89c7829b61ab7f 100644
--- a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
@@ -1,12 +1,7 @@
export default /* glsl */`
#ifdef USE_BATCHING
- #if ! defined( GL_ANGLE_multi_draw )
- #define gl_DrawID _gl_DrawID
- uniform int _gl_DrawID;
- #endif
-
+ attribute float batchId;
uniform highp sampler2D batchingTexture;
- uniform highp usampler2D batchingIdTexture;
mat4 getBatchingMatrix( const in float i ) {
int size = textureSize( batchingTexture, 0 ).x;
@@ -20,16 +15,6 @@ export default /* glsl */`
return mat4( v1, v2, v3, v4 );
}
-
- float getIndirectIndex( const in int i ) {
-
- int size = textureSize( batchingIdTexture, 0 ).x;
- int x = i % size;
- int y = i / size;
- return float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r );
-
- }
-
#endif
#ifdef USE_BATCHING_COLOR
diff --git a/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js
index 7364c8b9042fff..6eb4aadad2c5ba 100644
--- a/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/batching_vertex.glsl.js
@@ -1,5 +1,5 @@
export default /* glsl */`
#ifdef USE_BATCHING
- mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) );
+ mat4 batchingMatrix = getBatchingMatrix( batchId );
#endif
`;
diff --git a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
index 42a26e56b38414..daf7164e60cb79 100644
--- a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
@@ -23,7 +23,7 @@ export default /* glsl */`
#ifdef USE_BATCHING_COLOR
- vec3 batchingColor = getBatchingColor( getIndirectIndex( gl_DrawID ) );
+ vec3 batchingColor = getBatchingColor( batchId );
vColor.xyz *= batchingColor.xyz;
diff --git a/src/renderers/webgl/WebGLBufferRenderer.js b/src/renderers/webgl/WebGLBufferRenderer.js
index 1f5802a6dfd660..29825eef2e27e4 100644
--- a/src/renderers/webgl/WebGLBufferRenderer.js
+++ b/src/renderers/webgl/WebGLBufferRenderer.js
@@ -31,16 +31,29 @@ function WebGLBufferRenderer( gl, extensions, info ) {
if ( drawCount === 0 ) return;
const extension = extensions.get( 'WEBGL_multi_draw' );
- extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );
- let elementCount = 0;
- for ( let i = 0; i < drawCount; i ++ ) {
+ if ( extension === null ) {
- elementCount += counts[ i ];
+ for ( let i = 0; i < drawCount; i ++ ) {
- }
+ this.render( starts[ i ], counts[ i ] );
+
+ }
- info.update( elementCount, mode, 1 );
+ } else {
+
+ extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );
+
+ let elementCount = 0;
+ for ( let i = 0; i < drawCount; i ++ ) {
+
+ elementCount += counts[ i ];
+
+ }
+
+ info.update( elementCount, mode, 1 );
+
+ }
}
diff --git a/src/renderers/webgl/WebGLIndexedBufferRenderer.js b/src/renderers/webgl/WebGLIndexedBufferRenderer.js
index c6a1e8a59e4edc..7ba8d2cf6e19dc 100644
--- a/src/renderers/webgl/WebGLIndexedBufferRenderer.js
+++ b/src/renderers/webgl/WebGLIndexedBufferRenderer.js
@@ -40,17 +40,29 @@ function WebGLIndexedBufferRenderer( gl, extensions, info ) {
if ( drawCount === 0 ) return;
const extension = extensions.get( 'WEBGL_multi_draw' );
- extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount );
- let elementCount = 0;
- for ( let i = 0; i < drawCount; i ++ ) {
+ if ( extension === null ) {
+
+ for ( let i = 0; i < drawCount; i ++ ) {
- elementCount += counts[ i ];
+ this.render( starts[ i ] / bytesPerElement, counts[ i ] );
- }
+ }
+
+ } else {
+
+ extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount );
- info.update( elementCount, mode, 1 );
+ let elementCount = 0;
+ for ( let i = 0; i < drawCount; i ++ ) {
+
+ elementCount += counts[ i ];
+ }
+
+ info.update( elementCount, mode, 1 );
+
+ }
}
diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js
index ade7725871c8a8..82a19b766e4aa4 100644
--- a/src/renderers/webgl/WebGLPrograms.js
+++ b/src/renderers/webgl/WebGLPrograms.js
@@ -352,7 +352,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
index0AttributeName: material.index0AttributeName,
extensionClipCullDistance: HAS_EXTENSIONS && material.extensions.clipCullDistance === true && extensions.has( 'WEBGL_clip_cull_distance' ),
- extensionMultiDraw: ( HAS_EXTENSIONS && material.extensions.multiDraw === true || IS_BATCHEDMESH ) && extensions.has( 'WEBGL_multi_draw' ),
+ extensionMultiDraw: HAS_EXTENSIONS && material.extensions.multiDraw === true && extensions.has( 'WEBGL_multi_draw' ),
rendererExtensionParallelShaderCompile: extensions.has( 'KHR_parallel_shader_compile' ),