diff --git a/docs/api/en/objects/BatchedMesh.html b/docs/api/en/objects/BatchedMesh.html index 8c0a095b7e520..bd2c2aae60794 100644 --- a/docs/api/en/objects/BatchedMesh.html +++ b/docs/api/en/objects/BatchedMesh.html @@ -129,6 +129,19 @@

[method:this setCustomSort]( [param:Function sortFunction] )

in the list include a "z" field to perform a depth-ordered sort with.

+

+ [method:undefined getColorAt]( [param:Integer index], [param:Color color] ) +

+

+ [page:Integer index]: The index of a geometry. Values have to be in the + range [0, count]. +

+

+ [page:Color color]: This color object will be set to the color of the + defined geometry. +

+

Get the color of the defined geometry.

+

[method:Matrix4 getMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )

@@ -151,6 +164,18 @@

Get whether the given instance is marked as "visible" or not.

+

+ [method:undefined setColorAt]( [param:Integer index], [param:Color color] ) +

+

+ [page:Integer index]: The index of a geometry. Values have to be in the + range [0, count]. +

+

[page:Color color]: The color of a single geometry.

+

+ Sets the given color to the defined geometry. +

+

[method:this setMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )

diff --git a/examples/jsm/renderers/common/RenderObject.js b/examples/jsm/renderers/common/RenderObject.js index 4628c2fe5edfb..7d8bdbf5d796e 100644 --- a/examples/jsm/renderers/common/RenderObject.js +++ b/examples/jsm/renderers/common/RenderObject.js @@ -225,6 +225,7 @@ export default class RenderObject { if ( object.isBatchedMesh ) { cacheKey += object._matricesTexture.uuid + ','; + cacheKey += object._colorsTexture.uuid + ','; } diff --git a/src/core/Object3D.js b/src/core/Object3D.js index 6f47db133e62c..9cd4a5486b20e 100644 --- a/src/core/Object3D.js +++ b/src/core/Object3D.js @@ -761,6 +761,8 @@ class Object3D extends EventDispatcher { object.matricesTexture = this._matricesTexture.toJSON( meta ); + if ( this._colorsTexture !== null ) object.colorsTexture = this._colorsTexture.toJSON( meta ); + if ( this.boundingSphere !== null ) { object.boundingSphere = { diff --git a/src/loaders/ObjectLoader.js b/src/loaders/ObjectLoader.js index d430386baac68..4f34700d8de84 100644 --- a/src/loaders/ObjectLoader.js +++ b/src/loaders/ObjectLoader.js @@ -946,6 +946,7 @@ class ObjectLoader extends Loader { object._geometryCount = data.geometryCount; object._matricesTexture = getTexture( data.matricesTexture.uuid ); + if ( data.colorsTexture !== undefined ) object._colorsTexture = getTexture( data.colorsTexture.uuid ); break; diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js index 89e549476f271..8f09bd4f6a381 100644 --- a/src/objects/BatchedMesh.js +++ b/src/objects/BatchedMesh.js @@ -5,6 +5,7 @@ import { FloatType } from '../constants.js'; import { Matrix4 } from '../math/Matrix4.js'; import { Mesh } from './Mesh.js'; import { RGBAFormat } from '../constants.js'; +import { ColorManagement } from '../math/ColorManagement.js'; import { Box3 } from '../math/Box3.js'; import { Sphere } from '../math/Sphere.js'; import { Frustum } from '../math/Frustum.js'; @@ -160,6 +161,9 @@ class BatchedMesh extends Mesh { this._initMatricesTexture(); + // Local color per geometry by using data texture + this._colorsTexture = null; + } _initMatricesTexture() { @@ -182,6 +186,19 @@ class BatchedMesh extends Mesh { } + _initColorsTexture() { + + let size = Math.sqrt( this._maxGeometryCount ); + size = Math.ceil( size ); + + const colorsArray = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel + const colorsTexture = new DataTexture( colorsArray, size, size, RGBAFormat, FloatType ); + colorsTexture.colorSpace = ColorManagement.workingColorSpace; + + this._colorsTexture = colorsTexture; + + } + _initializeGeometry( reference ) { const geometry = this.geometry; @@ -746,6 +763,49 @@ class BatchedMesh extends Mesh { } + setColorAt( geometryId, color ) { + + if ( this._colorsTexture === null ) { + + this._initColorsTexture(); + + } + + // @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 geometryCount = this._geometryCount; + if ( geometryId >= geometryCount || active[ geometryId ] === false ) { + + return this; + + } + + color.toArray( colorsArray, geometryId * 4 ); + colorsTexture.needsUpdate = true; + + return this; + + } + + getColorAt( geometryId, color ) { + + const active = this._active; + const colorsArray = this._colorsTexture.image.data; + const geometryCount = this._geometryCount; + if ( geometryId >= geometryCount || active[ geometryId ] === false ) { + + return null; + + } + + return color.fromArray( colorsArray, geometryId * 4 ); + + } + setVisibleAt( geometryId, value ) { const visibility = this._visibility; @@ -886,6 +946,13 @@ class BatchedMesh extends Mesh { this._matricesTexture = source._matricesTexture.clone(); this._matricesTexture.image.data = this._matricesTexture.image.slice(); + if ( this._colorsTexture !== null ) { + + this._colorsTexture = source._colorsTexture.clone(); + this._colorsTexture.image.data = this._colorsTexture.image.slice(); + + } + return this; } @@ -897,6 +964,14 @@ class BatchedMesh extends Mesh { this._matricesTexture.dispose(); this._matricesTexture = null; + + if ( this._colorsTexture !== null ) { + + this._colorsTexture.dispose(); + this._colorsTexture = null; + + } + return this; } diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index d12b90e5b94e8..e43ba7233626e 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -1716,6 +1716,7 @@ class WebGLRenderer { materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.batching = parameters.batching; + materialProperties.batchingColor = parameters.batchingColor; materialProperties.instancing = parameters.instancing; materialProperties.instancingColor = parameters.instancingColor; materialProperties.instancingMorph = parameters.instancingMorph; @@ -1805,6 +1806,14 @@ class WebGLRenderer { needsProgramChange = true; + } else if ( object.isBatchedMesh && materialProperties.batchingColor === true && object.colorTexture === null ) { + + needsProgramChange = true; + + } else if ( object.isBatchedMesh && materialProperties.batchingColor === false && object.colorTexture !== null ) { + + needsProgramChange = true; + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { needsProgramChange = true; @@ -1997,6 +2006,13 @@ class WebGLRenderer { p_uniforms.setOptional( _gl, object, 'batchingTexture' ); p_uniforms.setValue( _gl, 'batchingTexture', object._matricesTexture, textures ); + p_uniforms.setOptional( _gl, object, 'batchingColorTexture' ); + if ( object._colorsTexture !== null ) { + + p_uniforms.setValue( _gl, 'batchingColorTexture', object._colorsTexture, textures ); + + } + } const morphAttributes = geometry.morphAttributes; diff --git a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js index fb602a0f7d880..89c7829b61ab7 100644 --- a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js @@ -16,4 +16,19 @@ export default /* glsl */` } #endif + +#ifdef USE_BATCHING_COLOR + + uniform sampler2D batchingColorTexture; + vec3 getBatchingColor( const in float i ) { + + int size = textureSize( batchingColorTexture, 0 ).x; + int j = int( i ); + int x = j % size; + int y = j / size; + return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb; + + } + +#endif `; diff --git a/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js index 80c4934d555ee..944eedf9bf7c0 100644 --- a/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js @@ -3,7 +3,7 @@ export default /* glsl */` varying vec4 vColor; -#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) +#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) varying vec3 vColor; diff --git a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js index b288b7c472c0f..daf7164e60cb7 100644 --- a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js @@ -3,7 +3,7 @@ export default /* glsl */` vColor = vec4( 1.0 ); -#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) +#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) vColor = vec3( 1.0 ); @@ -19,5 +19,13 @@ export default /* glsl */` vColor.xyz *= instanceColor.xyz; +#endif + +#ifdef USE_BATCHING_COLOR + + vec3 batchingColor = getBatchingColor( batchId ); + + vColor.xyz *= batchingColor.xyz; + #endif `; diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index cfccc448bfb3a..b133801128fd9 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -522,6 +522,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.extensionClipCullDistance ? '#define USE_CLIP_DISTANCE' : '', parameters.batching ? '#define USE_BATCHING' : '', + parameters.batchingColor ? '#define USE_BATCHING_COLOR' : '', parameters.instancing ? '#define USE_INSTANCING' : '', parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', parameters.instancingMorph ? '#define USE_INSTANCING_MORPH' : '', @@ -799,7 +800,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexColors || parameters.instancingColor || parameters.batchingColor ? '#define USE_COLOR' : '', parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', parameters.vertexUv1s ? '#define USE_UV1' : '', parameters.vertexUv2s ? '#define USE_UV2' : '', diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index f4701d9006314..82a19b766e4aa 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -192,6 +192,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities precision: precision, batching: IS_BATCHEDMESH, + batchingColor: IS_BATCHEDMESH && object._colorsTexture !== null, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, instancingMorph: IS_INSTANCEDMESH && object.morphTexture !== null, @@ -510,6 +511,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities _programLayers.enable( 19 ); if ( parameters.dispersion ) _programLayers.enable( 20 ); + if ( parameters.batchingColor ) + _programLayers.enable( 21 ); array.push( _programLayers.mask ); _programLayers.disableAll();