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' ),