From 2bedd511cd3fd3d970a2b9be21a827aacf1cbf20 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:06:57 +0900 Subject: [PATCH 01/16] Remove mixing of fade state and materials --- src/plugins/three/fade/FadeManager.js | 173 ++------------------------ 1 file changed, 10 insertions(+), 163 deletions(-) diff --git a/src/plugins/three/fade/FadeManager.js b/src/plugins/three/fade/FadeManager.js index 50abb9d86..bd8e4b239 100644 --- a/src/plugins/three/fade/FadeManager.js +++ b/src/plugins/three/fade/FadeManager.js @@ -9,7 +9,6 @@ export class FadeManager { this.fadeCount = 0; this._lastTick = - 1; this._fadeState = new Map(); - this._fadeParams = new WeakMap(); this.onFadeComplete = null; this.onFadeStart = null; this.onFadeSetComplete = null; @@ -17,21 +16,6 @@ export class FadeManager { } - // initialize materials in the object - prepareObject( object ) { - - object.traverse( child => { - - if ( child.material ) { - - this.prepareMaterial( child.material ); - - } - - } ); - - } - // delete the object from the fade, reset the material data deleteObject( object ) { @@ -43,103 +27,6 @@ export class FadeManager { this.completeFade( object ); - const fadeParams = this._fadeParams; - object.traverse( child => { - - const material = child.material; - if ( material ) { - - fadeParams.delete( material ); - material.onBeforeCompile = () => {}; - material.needsUpdate = true; - - } - - } ); - - } - - // initialize the material - prepareMaterial( material ) { - - const fadeParams = this._fadeParams; - if ( fadeParams.has( material ) ) { - - return; - - } - - const params = { - fadeIn: { value: 0 }, - fadeOut: { value: 0 }, - }; - - material.defines = { - FEATURE_FADE: 0, - }; - - material.onBeforeCompile = shader => { - - shader.uniforms = { - ...shader.uniforms, - ...params, - }; - - shader.fragmentShader = shader.fragmentShader - .replace( /void main\(/, value => /* glsl */` - #if FEATURE_FADE - - // adapted from https://www.shadertoy.com/view/Mlt3z8 - float bayerDither2x2( vec2 v ) { - - return mod( 3.0 * v.y + 2.0 * v.x, 4.0 ); - - } - - float bayerDither4x4( vec2 v ) { - - vec2 P1 = mod( v, 2.0 ); - vec2 P2 = floor( 0.5 * mod( v, 4.0 ) ); - return 4.0 * bayerDither2x2( P1 ) + bayerDither2x2( P2 ); - - } - - uniform float fadeIn; - uniform float fadeOut; - #endif - - ${ value } - ` ) - .replace( /#include /, value => /* glsl */` - - ${ value } - - #if FEATURE_FADE - - float bayerValue = bayerDither4x4( floor( mod( gl_FragCoord.xy, 4.0 ) ) ); - float bayerBins = 16.0; - float dither = ( 0.5 + bayerValue ) / bayerBins; - if ( dither >= fadeIn ) { - - discard; - - } - - if ( dither < fadeOut ) { - - discard; - - } - - #endif - - ` ); - - - }; - - fadeParams.set( material, params ); - } // Ensure we're storing a fade timer for the provided object @@ -162,20 +49,6 @@ export class FadeManager { fadeState.set( object, state ); - const fadeParams = this._fadeParams; - object.traverse( child => { - - const material = child.material; - if ( material && fadeParams.has( material ) ) { - - const params = fadeParams.get( material ); - params.fadeIn.value = 0; - params.fadeOut.value = 0; - - } - - } ); - return true; } @@ -184,22 +57,15 @@ export class FadeManager { completeFade( object ) { const fadeState = this._fadeState; - if ( ! fadeState.has( object ) ) return; + if ( ! fadeState.has( object ) ) { - const visible = fadeState.get( object ).fadeOutTarget === 0; - - fadeState.delete( object ); - object.traverse( child => { - - const material = child.material; - if ( material && material.defines.FEATURE_FADE !== 0 ) { + return; - material.defines.FEATURE_FADE = 0; - material.needsUpdate = true; + } - } + const visible = fadeState.get( object ).fadeOutTarget === 0; - } ); + fadeState.delete( object ); // fire events this.fadeCount --; @@ -230,7 +96,11 @@ export class FadeManager { forEachObject( cb ) { - this._fadeState.forEach( ( info, scene ) => cb( scene ) ); + this._fadeState.forEach( ( info, scene ) => { + + cb( scene ); + + } ); } @@ -321,7 +191,6 @@ export class FadeManager { this._lastTick = time; const fadeState = this._fadeState; - const fadeParams = this._fadeParams; fadeState.forEach( ( state, object ) => { // tick the fade values @@ -344,28 +213,6 @@ export class FadeManager { state.fadeIn = fadeIn; state.fadeOut = fadeOut; - // update the material fields - const defineValue = Number( fadeOut !== fadeOutTarget || fadeIn !== fadeInTarget ); - object.traverse( child => { - - const material = child.material; - if ( material && fadeParams.has( material ) ) { - - const uniforms = fadeParams.get( material ); - uniforms.fadeIn.value = fadeIn; - uniforms.fadeOut.value = fadeOut; - - if ( defineValue !== material.defines.FEATURE_FADE ) { - - material.defines.FEATURE_FADE = defineValue; - material.needsUpdate = true; - - } - - } - - } ); - // Check if the fade in and fade out animations are complete const fadeOutComplete = fadeOut === 1 || fadeOut === 0; const fadeInComplete = fadeIn === 1 || fadeIn === 0; From 94300a90b8356b835ae4e63b2d6085487df7f3e7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:25:41 +0900 Subject: [PATCH 02/16] Change fade handle to tile --- src/plugins/three/fade/TilesFadePlugin.js | 72 +++++++++++++---------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index 6580f7219..058c010b9 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -8,14 +8,19 @@ const _fromQuat = new Quaternion(); const _toQuat = new Quaternion(); const _scale = new Vector3(); -function onTileVisibilityChange( scene, tile, visible ) { +function onTileVisibilityChange( tile, visible ) { // ensure the tiles are marked as visible on visibility toggle since // it's possible we disable them when adjusting visibility based on frustum - scene.visible = true; + const scene = tile.cached.scene; + if ( scene ) { + + scene.visible = true; // TODO + + } const fadeManager = this._fadeManager; - if ( fadeManager.isFadingOut( scene ) ) { + if ( fadeManager.isFadingOut( tile ) ) { this._fadingOutCount --; @@ -24,7 +29,7 @@ function onTileVisibilityChange( scene, tile, visible ) { if ( ! visible ) { this._fadingOutCount ++; - fadeManager.fadeOut( scene ); + fadeManager.fadeOut( tile ); } else { @@ -35,7 +40,7 @@ function onTileVisibilityChange( scene, tile, visible ) { if ( tile[ HAS_POPPED_IN ] || this.fadeRootTiles ) { - this._fadeManager.fadeIn( scene ); + this._fadeManager.fadeIn( tile ); } @@ -44,7 +49,7 @@ function onTileVisibilityChange( scene, tile, visible ) { } else { - this._fadeManager.fadeIn( scene ); + this._fadeManager.fadeIn( tile ); } @@ -54,24 +59,19 @@ function onTileVisibilityChange( scene, tile, visible ) { function onLoadModel( scene, tile ) { - this._fadeManager.prepareObject( scene ); - this._tileMap.set( scene, tile ); - } -function onDisposeModel( scene ) { +function onDisposeModel( scene, tile ) { - this._fadeManager.deleteObject( scene ); - this._tileMap.delete( scene ); + this._fadeManager.deleteObject( tile ); } -function onFadeComplete( object, visible ) { +function onFadeComplete( tile, visible ) { if ( ! visible ) { // now that the tile is hidden we can run the built-in setTileVisible function for the tile - const tile = this._tileMap.get( object ); this.tiles.invokeOnePlugin( plugin => plugin !== this && plugin.setTileVisible && plugin.setTileVisible( tile, false ) ); this._fadingOutCount --; @@ -115,7 +115,6 @@ function onUpdateAfter() { const fadingBefore = this._fadingBefore; const tiles = this.tiles; const prevCameraTransforms = this._prevCameraTransforms; - const tileMap = this._tileMap; const lruCache = tiles.lruCache; const cameras = tiles.cameras; @@ -140,16 +139,27 @@ function onUpdateAfter() { tiles.visibleTiles.forEach( t => { + // TODO + const scene = t.cached.scene; + if ( ! scene ) { + + return; + + } + // if a tile is fading out then it may not be traversed and thus will not have // the frustum flag set correctly. - const scene = t.cached.scene; - if ( fadeManager.isFadingOut( scene ) ) { + if ( fadeManager.isFadingOut( t ) ) { scene.visible = true; } else { - scene.visible = t.__inFrustum; + if ( scene ) { + + scene.visible = t.__inFrustum; + + } } @@ -201,9 +211,9 @@ function onUpdateAfter() { } ); // prevent faded tiles from being unloaded - fadeManager.forEachObject( scene => { + fadeManager.forEachObject( tile => { - lruCache.markUsed( tileMap.get( scene ) ); + lruCache.markUsed( tile ); } ); @@ -246,7 +256,7 @@ export class TilesFadePlugin { this.tiles = null; this._fadeManager = new FadeManager(); this._prevCameraTransforms = null; - this._tileMap = null; + this._tileMap = null; // TODO this._fadingOutCount = 0; this.maximumFadeOutTiles = options.maximumFadeOutTiles; @@ -292,7 +302,7 @@ export class TilesFadePlugin { } ); this._onLoadModel = e => onLoadModel.call( this, e.scene, e.tile ); - this._onDisposeModel = e => onDisposeModel.call( this, e.scene ); + this._onDisposeModel = e => onDisposeModel.call( this, e.scene, e.tile ); this._onAddCamera = e => onAddCamera.call( this, e.camera ); this._onDeleteCamera = e => onDeleteCamera.call( this, e.camera ); this._onUpdateBefore = () => onUpdateBefore.call( this ); @@ -309,9 +319,8 @@ export class TilesFadePlugin { setTileVisible( tile, visible ) { - const scene = tile.cached.scene; - const wasFading = this._fadeManager.isFading( scene ); - onTileVisibilityChange.call( this, scene, tile, visible ); + const wasFading = this._fadeManager.isFading( tile ); + onTileVisibilityChange.call( this, tile, visible ); // if a tile was already fading then it's already marked as visible and in the scene if ( wasFading ) { @@ -322,7 +331,7 @@ export class TilesFadePlugin { // cancel the visibility change trigger because we're fading and will call this after // fade completes. - const isFading = this._fadeManager.isFading( scene ); + const isFading = this._fadeManager.isFading( tile ); if ( ! visible && isFading ) { return true; @@ -345,11 +354,14 @@ export class TilesFadePlugin { tiles.removeEventListener( 'delete-camera', this._onDeleteCamera ); tiles.removeEventListener( 'update-before', this._onUpdateBefore ); tiles.removeEventListener( 'update-after', this._onUpdateAfter ); - tiles.forEachLoadedModel( scene => { + tiles.forEachLoadedModel( ( scene, tile ) => { + + this._fadeManager.deleteObject( tile ); + if ( scene ) { - this._fadeManager.deleteObject( scene ); - this._tileMap.delete( scene ); - scene.visible = true; + scene.visible = true; // TODO + + } } ); From bc734d5268f7e79f9edba57d47c432489a56f06a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:39:51 +0900 Subject: [PATCH 03/16] Separate material fade with fade tracking --- src/plugins/three/fade/FadeManager.js | 4 +- src/plugins/three/fade/FadeMaterialManager.js | 169 ++++++++++++++++++ src/plugins/three/fade/TilesFadePlugin.js | 14 +- 3 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 src/plugins/three/fade/FadeMaterialManager.js diff --git a/src/plugins/three/fade/FadeManager.js b/src/plugins/three/fade/FadeManager.js index bd8e4b239..9e94bc9ec 100644 --- a/src/plugins/three/fade/FadeManager.js +++ b/src/plugins/three/fade/FadeManager.js @@ -96,9 +96,9 @@ export class FadeManager { forEachObject( cb ) { - this._fadeState.forEach( ( info, scene ) => { + this._fadeState.forEach( ( info, object ) => { - cb( scene ); + cb( object, info ); } ); diff --git a/src/plugins/three/fade/FadeMaterialManager.js b/src/plugins/three/fade/FadeMaterialManager.js new file mode 100644 index 000000000..5eea03d4f --- /dev/null +++ b/src/plugins/three/fade/FadeMaterialManager.js @@ -0,0 +1,169 @@ +export class FadeMaterialManager { + + constructor() { + + this._fadeParams = new WeakMap(); + this.fading = 0; + + } + + // Set the fade parameters for the given scene + setFade( scene, fadeIn, fadeOut ) { + + if ( ! scene ) { + + return; + + } + + const fadeParams = this._fadeParams; + scene.traverse( child => { + + const material = child.material; + if ( material ) { + + const params = fadeParams.get( material ); + params.fadeIn.value = fadeIn; + params.fadeOut.value = fadeOut; + + const fadeInComplete = fadeIn === 0 || fadeIn === 1; + const fadeOutComplete = fadeOut === 0 || fadeOut === 1; + const value = Number( ! fadeInComplete || ! fadeOutComplete ); + if ( material.defines.FEATURE_FADE !== value ) { + + this.fading += value === 1 ? 1 : - 1; + material.defines.FEATURE_FADE = value; + material.needsUpdate = true; + + } + + } + + } ); + + } + + // initialize materials in the object + prepareScene( scene ) { + + scene.traverse( child => { + + if ( child.material ) { + + this.prepareMaterial( child.material ); + + } + + } ); + + } + + // delete the object from the fade, reset the material data + deleteScene( scene ) { + + if ( ! scene ) { + + return; + + } + + const fadeParams = this._fadeParams; + scene.traverse( child => { + + const material = child.material; + if ( material ) { + + fadeParams.delete( material ); + material.onBeforeCompile = () => {}; + material.needsUpdate = true; + + } + + } ); + + } + + // initialize the material + prepareMaterial( material ) { + + const fadeParams = this._fadeParams; + if ( fadeParams.has( material ) ) { + + return; + + } + + const params = { + fadeIn: { value: 0 }, + fadeOut: { value: 0 }, + }; + + material.defines = { + FEATURE_FADE: 0, + }; + + material.onBeforeCompile = shader => { + + shader.uniforms = { + ...shader.uniforms, + ...params, + }; + + shader.fragmentShader = shader.fragmentShader + .replace( /void main\(/, value => /* glsl */` + #if FEATURE_FADE + + // adapted from https://www.shadertoy.com/view/Mlt3z8 + float bayerDither2x2( vec2 v ) { + + return mod( 3.0 * v.y + 2.0 * v.x, 4.0 ); + + } + + float bayerDither4x4( vec2 v ) { + + vec2 P1 = mod( v, 2.0 ); + vec2 P2 = floor( 0.5 * mod( v, 4.0 ) ); + return 4.0 * bayerDither2x2( P1 ) + bayerDither2x2( P2 ); + + } + + uniform float fadeIn; + uniform float fadeOut; + #endif + + ${ value } + ` ) + .replace( /#include /, value => /* glsl */` + + ${ value } + + #if FEATURE_FADE + + float bayerValue = bayerDither4x4( floor( mod( gl_FragCoord.xy, 4.0 ) ) ); + float bayerBins = 16.0; + float dither = ( 0.5 + bayerValue ) / bayerBins; + if ( dither >= fadeIn ) { + + discard; + + } + + if ( dither < fadeOut ) { + + discard; + + } + + #endif + + ` ); + + + }; + + fadeParams.set( material, params ); + + } + +} diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index 058c010b9..d236a040e 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -1,5 +1,6 @@ import { Matrix4, Vector3, Quaternion } from 'three'; import { FadeManager } from './FadeManager.js'; +import { FadeMaterialManager } from './FadeMaterialManager.js'; const HAS_POPPED_IN = Symbol( 'HAS_POPPED_IN' ); const _fromPos = new Vector3(); @@ -59,16 +60,22 @@ function onTileVisibilityChange( tile, visible ) { function onLoadModel( scene, tile ) { + this._fadeMaterialManager.prepareScene( scene ); + } function onDisposeModel( scene, tile ) { this._fadeManager.deleteObject( tile ); + this._fadeMaterialManager.deleteScene( scene ); } function onFadeComplete( tile, visible ) { + // mark the fade as finished + this._fadeMaterialManager.setFade( tile.cached.scene, 0, 0 ); + if ( ! visible ) { // now that the tile is hidden we can run the built-in setTileVisible function for the tile @@ -111,6 +118,7 @@ function onUpdateBefore() { function onUpdateAfter() { const fadeManager = this._fadeManager; + const fadeMaterialManager = this._fadeMaterialManager; const displayActiveTiles = this._displayActiveTiles; const fadingBefore = this._fadingBefore; const tiles = this.tiles; @@ -210,10 +218,11 @@ function onUpdateAfter() { } ); - // prevent faded tiles from being unloaded - fadeManager.forEachObject( tile => { + fadeManager.forEachObject( ( tile, { fadeIn, fadeOut } ) => { + // prevent faded tiles from being unloaded lruCache.markUsed( tile ); + fadeMaterialManager.setFade( tile.cached.scene, fadeIn, fadeOut ); } ); @@ -255,6 +264,7 @@ export class TilesFadePlugin { this.tiles = null; this._fadeManager = new FadeManager(); + this._fadeMaterialManager = new FadeMaterialManager(); this._prevCameraTransforms = null; this._tileMap = null; // TODO this._fadingOutCount = 0; From 654666d272edac91b79ef2d600392f3037419f29 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:44:00 +0900 Subject: [PATCH 04/16] Wrap the material with batched mesh uniforms --- src/plugins/three/fade/FadeMaterialManager.js | 73 +---------- src/plugins/three/fade/wrapFadeMaterial.js | 122 ++++++++++++++++++ 2 files changed, 125 insertions(+), 70 deletions(-) create mode 100644 src/plugins/three/fade/wrapFadeMaterial.js diff --git a/src/plugins/three/fade/FadeMaterialManager.js b/src/plugins/three/fade/FadeMaterialManager.js index 5eea03d4f..f6e432753 100644 --- a/src/plugins/three/fade/FadeMaterialManager.js +++ b/src/plugins/three/fade/FadeMaterialManager.js @@ -1,3 +1,5 @@ +import { wrapFadeMaterial } from './wrapFadeMaterial.js'; + export class FadeMaterialManager { constructor() { @@ -93,76 +95,7 @@ export class FadeMaterialManager { } - const params = { - fadeIn: { value: 0 }, - fadeOut: { value: 0 }, - }; - - material.defines = { - FEATURE_FADE: 0, - }; - - material.onBeforeCompile = shader => { - - shader.uniforms = { - ...shader.uniforms, - ...params, - }; - - shader.fragmentShader = shader.fragmentShader - .replace( /void main\(/, value => /* glsl */` - #if FEATURE_FADE - - // adapted from https://www.shadertoy.com/view/Mlt3z8 - float bayerDither2x2( vec2 v ) { - - return mod( 3.0 * v.y + 2.0 * v.x, 4.0 ); - - } - - float bayerDither4x4( vec2 v ) { - - vec2 P1 = mod( v, 2.0 ); - vec2 P2 = floor( 0.5 * mod( v, 4.0 ) ); - return 4.0 * bayerDither2x2( P1 ) + bayerDither2x2( P2 ); - - } - - uniform float fadeIn; - uniform float fadeOut; - #endif - - ${ value } - ` ) - .replace( /#include /, value => /* glsl */` - - ${ value } - - #if FEATURE_FADE - - float bayerValue = bayerDither4x4( floor( mod( gl_FragCoord.xy, 4.0 ) ) ); - float bayerBins = 16.0; - float dither = ( 0.5 + bayerValue ) / bayerBins; - if ( dither >= fadeIn ) { - - discard; - - } - - if ( dither < fadeOut ) { - - discard; - - } - - #endif - - ` ); - - - }; - - fadeParams.set( material, params ); + fadeParams.set( material, wrapFadeMaterial( material ) ); } diff --git a/src/plugins/three/fade/wrapFadeMaterial.js b/src/plugins/three/fade/wrapFadeMaterial.js new file mode 100644 index 000000000..f40f68c64 --- /dev/null +++ b/src/plugins/three/fade/wrapFadeMaterial.js @@ -0,0 +1,122 @@ +export function wrapFadeMaterial( material ) { + + const params = { + fadeIn: { value: 0 }, + fadeOut: { value: 0 }, + fadeTexture: { value: null }, + }; + + material.defines = { + FEATURE_FADE: 0, + }; + + material.onBeforeCompile = shader => { + + shader.uniforms = { + ...shader.uniforms, + ...params, + }; + + shader.vertexShader = shader.vertexShader + .replace( + /void\s+main()\s+{/, + value => /* glsl */` + #ifdef USE_BATCHING + + varying float vBatchId; + + #endif + + ${ value } + + #ifdef USE_BATCHING + + // add 0.5 to the value to avoid floating error that may cause flickering + vBatchId = batchId + 0.5; + + #endif + ` + ); + + shader.fragmentShader = shader.fragmentShader + .replace( /void main\(/, value => /* glsl */` + #if FEATURE_FADE + + // adapted from https://www.shadertoy.com/view/Mlt3z8 + float bayerDither2x2( vec2 v ) { + + return mod( 3.0 * v.y + 2.0 * v.x, 4.0 ); + + } + + float bayerDither4x4( vec2 v ) { + + vec2 P1 = mod( v, 2.0 ); + vec2 P2 = floor( 0.5 * mod( v, 4.0 ) ); + return 4.0 * bayerDither2x2( P1 ) + bayerDither2x2( P2 ); + + } + + #ifdef USE_BATCHING + + uniform sampler2D fadeTexture; + varying float vBatchId; + vec2 getFadeValues( const in float i ) { + + int size = textureSize( fadeTexture, 0 ).x; + int j = int( i ); + int x = j % size; + int y = j / size; + return texelFetch( fadeTexture, ivec2( x, y ), 0 ).rg; + + } + + #else + + uniform float fadeIn; + uniform float fadeOut; + + #endif + + #endif + + ${ value } + ` ) + .replace( /#include /, value => /* glsl */` + + ${ value } + + #if FEATURE_FADE + + #ifdef USE_BATCHING + + vec2 fadeValues = getFadeValues( vBatchId ); + float fadeIn = fadeValues.r; + float fadeOut = fadeValues.g; + + #endif + + float bayerValue = bayerDither4x4( floor( mod( gl_FragCoord.xy, 4.0 ) ) ); + float bayerBins = 16.0; + float dither = ( 0.5 + bayerValue ) / bayerBins; + if ( dither >= fadeIn ) { + + discard; + + } + + if ( dither < fadeOut ) { + + discard; + + } + + #endif + + ` ); + + }; + + return params; + +} From 6de4a95169aab46834b71f449c7b4ed47b89d5c9 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:52:34 +0900 Subject: [PATCH 05/16] Add a FadeBatchedMesh class --- src/plugins/three/fade/FadeBatchedMesh.js | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/plugins/three/fade/FadeBatchedMesh.js diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js new file mode 100644 index 000000000..7642057fc --- /dev/null +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -0,0 +1,45 @@ +import { PassThroughBatchedMesh } from './PassThroughBatchedMesh.js'; +import { InstanceDataTexture } from './InstanceDataTexture.js'; +import { FloatType, RGFormat } from 'three'; + +export class FadeBatchedMesh extends PassThroughBatchedMesh { + + constructor( ...args ) { + + super( ...args ); + + this.fadeTexture = null; + this._initFadeTexture(); + + } + + _initFadeTexture() { + + let size = Math.sqrt( this._maxInstanceCount ); + size = Math.ceil( size ); + + // 4 floats per RGBA pixel initialized to white + const fadeArray = new Float32Array( size * size * 4 ).fill( 1 ); + const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); + + this.fadeTexture = fadeTexture; + + } + + setInstanceCount( ...args ) { + + super.setInstanceCount( ...args ); + + // update texture data for instance sampling + const oldFadeTexture = this.fadeTexture; + oldFadeTexture.dispose(); + this._initFadeTexture(); + + const src = oldFadeTexture.image.data; + const dst = this.fadeTexture.image.data; + const len = Math.min( src.length, dst.length ); + dst.set( new src.constructor( src.buffer, 0, len ) ); + + } + +} From 2bc6cccb1a0589855646423c819b5623e94ab841 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 14:52:41 +0900 Subject: [PATCH 06/16] Add instance data texture classe --- src/plugins/three/fade/InstanceDataTexture.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/plugins/three/fade/InstanceDataTexture.js diff --git a/src/plugins/three/fade/InstanceDataTexture.js b/src/plugins/three/fade/InstanceDataTexture.js new file mode 100644 index 000000000..65a55d348 --- /dev/null +++ b/src/plugins/three/fade/InstanceDataTexture.js @@ -0,0 +1,32 @@ +import { DataTexture } from 'three'; + +export class InstanceDataTexture extends DataTexture { + + setValueAt( instance, ...values ) { + + const { data, width, height } = this.image; + const itemSize = Math.floor( data.length / ( width * height ) ); + let needsUpdate = false; + for ( let i = 0; i < itemSize; i ++ ) { + + const index = instance * itemSize + i; + const prevValue = data[ index ]; + const newValue = values[ i ] || 0; + if ( prevValue !== newValue ) { + + data[ index ] = newValue; + needsUpdate = true; + + } + + } + + if ( needsUpdate ) { + + this.needsUpdate = true; + + } + + } + +} From 246f4fa4a89e677ac34b1038d2d0ad43ca79063a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 15:59:11 +0900 Subject: [PATCH 07/16] Add fade helpers --- src/plugins/three/fade/FadeBatchedMesh.js | 5 +++++ .../three/fade/PassThroughBatchedMesh.js | 19 +++++++++++++++++++ src/plugins/three/fade/wrapFadeMaterial.js | 5 ++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/plugins/three/fade/PassThroughBatchedMesh.js diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index 7642057fc..be9853223 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -1,6 +1,7 @@ import { PassThroughBatchedMesh } from './PassThroughBatchedMesh.js'; import { InstanceDataTexture } from './InstanceDataTexture.js'; import { FloatType, RGFormat } from 'three'; +import { wrapFadeMaterial } from './wrapFadeMaterial.js'; export class FadeBatchedMesh extends PassThroughBatchedMesh { @@ -11,6 +12,10 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { this.fadeTexture = null; this._initFadeTexture(); + const material = this.material; + wrapFadeMaterial( material, material.onBeforeCompile ); + material.needsUpdate = true; + } _initFadeTexture() { diff --git a/src/plugins/three/fade/PassThroughBatchedMesh.js b/src/plugins/three/fade/PassThroughBatchedMesh.js new file mode 100644 index 000000000..a77ed3bda --- /dev/null +++ b/src/plugins/three/fade/PassThroughBatchedMesh.js @@ -0,0 +1,19 @@ +import { BatchedMesh, MeshBasicMaterial } from 'three'; + +// NOTE this will not automatically delete instances on geometry delete. +export class PassThroughBatchedMesh extends BatchedMesh { + + get geometry() { + + return this.other.geometry; + + } + + constructor( maxInstanceCount, other, material = new MeshBasicMaterial() ) { + + super( maxInstanceCount, 0, 0, material ); + this.other = other; + + } + +} diff --git a/src/plugins/three/fade/wrapFadeMaterial.js b/src/plugins/three/fade/wrapFadeMaterial.js index f40f68c64..4f0adcbe5 100644 --- a/src/plugins/three/fade/wrapFadeMaterial.js +++ b/src/plugins/three/fade/wrapFadeMaterial.js @@ -1,4 +1,4 @@ -export function wrapFadeMaterial( material ) { +export function wrapFadeMaterial( material, previousOnBeforeCompile ) { const params = { fadeIn: { value: 0 }, @@ -7,11 +7,14 @@ export function wrapFadeMaterial( material ) { }; material.defines = { + ...( material.defines || {} ), FEATURE_FADE: 0, }; material.onBeforeCompile = shader => { + previousOnBeforeCompile( shader ); + shader.uniforms = { ...shader.uniforms, ...params, From 1d5c6d9d716296e2d1760334f66e043fdc55c5bf Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 18:43:27 +0900 Subject: [PATCH 08/16] Get basic implementation working --- src/plugins/three/fade/FadeBatchedMesh.js | 7 + .../three/fade/PassThroughBatchedMesh.js | 140 +++++++++++++++++- src/plugins/three/fade/TilesFadePlugin.js | 120 +++++++++++++-- src/plugins/three/fade/wrapFadeMaterial.js | 6 +- 4 files changed, 253 insertions(+), 20 deletions(-) diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index be9853223..bf928d0ff 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -47,4 +47,11 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { } + dispose() { + + super.dispose(); + this.fadeTexture.dispose(); + + } + } diff --git a/src/plugins/three/fade/PassThroughBatchedMesh.js b/src/plugins/three/fade/PassThroughBatchedMesh.js index a77ed3bda..560828a2d 100644 --- a/src/plugins/three/fade/PassThroughBatchedMesh.js +++ b/src/plugins/three/fade/PassThroughBatchedMesh.js @@ -1,18 +1,144 @@ -import { BatchedMesh, MeshBasicMaterial } from 'three'; +import { MeshBasicMaterial } from 'three'; // NOTE this will not automatically delete instances on geometry delete. -export class PassThroughBatchedMesh extends BatchedMesh { +export class PassThroughBatchedMesh { - get geometry() { + constructor( other, material = new MeshBasicMaterial() ) { - return this.other.geometry; + this.other = other; + this.material = material; + this.visible = true; + this.parent = null; + this._instanceInfo = []; + this._availableInstanceIds = []; + this._visibilityChanged = true; + + material.color.set( 0xff0000 ); + + const proxyTarget = new Proxy( this, { + + get( target, key ) { + + if ( key in target ) { + + return target[ key ]; + + } else { + + const value = other[ key ]; + if ( value instanceof Function ) { + + return ( ...args ) => { + + target.syncInstances(); + return value.call( proxyTarget, ...args ); + + }; + + } else { + + return other[ key ]; + + } + + } + + }, + + set( target, key, value ) { + + if ( key in target ) { + + target[ key ] = value; + + } else { + + other[ key ] = value; + + } + + return true; + + }, + + + deleteProperty( target, key ) { + + if ( key in target ) { + + return delete target[ key ]; + + } else { + + return delete other[ key ]; + + } + + }, + + // ownKeys() { + + // }, + + // has(target, key) { + + // }, + + // defineProperty(target, key, descriptor) { + // }, + + // getOwnPropertyDescriptor(target, key) { + // }, + + + } ); + + return proxyTarget; } - constructor( maxInstanceCount, other, material = new MeshBasicMaterial() ) { + syncInstances() { - super( maxInstanceCount, 0, 0, material ); - this.other = other; + const instanceInfo = this._instanceInfo; + const otherInstanceInfo = this.other._instanceInfo; + while ( otherInstanceInfo.length > instanceInfo.length ) { + + const index = instanceInfo.length; + instanceInfo.push( new Proxy( { visible: false }, { + + get( target, key ) { + + if ( key in target ) { + + return target[ key ]; + + } else { + + return otherInstanceInfo[ index ][ key ]; + + } + + }, + + set( target, key, value ) { + + if ( key in target ) { + + target[ key ] = value; + + } else { + + otherInstanceInfo[ index ][ key ] = value; + + } + + return true; + + } + + } ) ); + + } } diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index d236a040e..01091894b 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -1,6 +1,7 @@ import { Matrix4, Vector3, Quaternion } from 'three'; import { FadeManager } from './FadeManager.js'; import { FadeMaterialManager } from './FadeMaterialManager.js'; +import { FadeBatchedMesh } from './FadeBatchedMesh.js'; const HAS_POPPED_IN = Symbol( 'HAS_POPPED_IN' ); const _fromPos = new Vector3(); @@ -11,15 +12,6 @@ const _scale = new Vector3(); function onTileVisibilityChange( tile, visible ) { - // ensure the tiles are marked as visible on visibility toggle since - // it's possible we disable them when adjusting visibility based on frustum - const scene = tile.cached.scene; - if ( scene ) { - - scene.visible = true; // TODO - - } - const fadeManager = this._fadeManager; if ( fadeManager.isFadingOut( tile ) ) { @@ -76,6 +68,13 @@ function onFadeComplete( tile, visible ) { // mark the fade as finished this._fadeMaterialManager.setFade( tile.cached.scene, 0, 0 ); + this.forEachBatchIds( tile, ( id, batchedMesh, plugin ) => { + + batchedMesh.setVisibleAt( id, false ); + plugin.batchedMesh.setVisibleAt( id, visible ); + + } ); + if ( ! visible ) { // now that the tile is hidden we can run the built-in setTileVisible function for the tile @@ -224,6 +223,17 @@ function onUpdateAfter() { lruCache.markUsed( tile ); fadeMaterialManager.setFade( tile.cached.scene, fadeIn, fadeOut ); + const isFading = fadeIn !== 0 && fadeIn !== 1 || fadeOut !== 0 && fadeIn !== 1; + this.forEachBatchIds( tile, ( id, batchedMesh ) => { + + if ( isFading ) { + + batchedMesh.fadeTexture.setValueAt( id, fadeIn, fadeOut ); + + } + + } ); + } ); } @@ -260,13 +270,13 @@ export class TilesFadePlugin { }; this.name = 'FADE_TILES_PLUGIN'; - this.priority = - 1; + this.priority = - 2; this.tiles = null; + this.batchedMesh = null; this._fadeManager = new FadeManager(); this._fadeMaterialManager = new FadeMaterialManager(); this._prevCameraTransforms = null; - this._tileMap = null; // TODO this._fadingOutCount = 0; this.maximumFadeOutTiles = options.maximumFadeOutTiles; @@ -296,7 +306,6 @@ export class TilesFadePlugin { this.tiles = tiles; this._fadeManager = fadeManager; - this._tileMap = new Map(); this._prevCameraTransforms = new Map(); tiles.cameras.forEach( camera => { @@ -317,6 +326,25 @@ export class TilesFadePlugin { this._onDeleteCamera = e => onDeleteCamera.call( this, e.camera ); this._onUpdateBefore = () => onUpdateBefore.call( this ); this._onUpdateAfter = () => onUpdateAfter.call( this ); + this._onTileVisibilityChange = ( { tile, visible } ) => { + + // ensure the tiles are marked as visible on visibility toggle since + // it's possible we disable them when adjusting visibility based on frustum + const scene = tile.cached.scene; + if ( scene ) { + + scene.visible = true; // TODO + + } + + this.forEachBatchIds( tile, ( id, batchedMesh, plugin ) => { + + batchedMesh.setVisibleAt( id, visible ); + plugin.batchedMesh.setVisibleAt( id, false ); + + } ); + + }; tiles.addEventListener( 'load-model', this._onLoadModel ); tiles.addEventListener( 'dispose-model', this._onDisposeModel ); @@ -324,6 +352,45 @@ export class TilesFadePlugin { tiles.addEventListener( 'delete-camera', this._onDeleteCamera ); tiles.addEventListener( 'update-before', this._onUpdateBefore ); tiles.addEventListener( 'update-after', this._onUpdateAfter ); + tiles.addEventListener( 'tile-visibility-change', this._onTileVisibilityChange ); + + } + + initBatchedMesh() { + + const batchedPlugin = this.tiles.getPluginByName( 'BATCHED_MESH_PLUGIN' ); + const otherBatchedMesh = batchedPlugin.batchedMesh; + + if ( batchedPlugin && batchedPlugin.batchedMesh ) { + + if ( this.batchedMesh === null ) { + + this._onBatchedMeshDispose = () => { + + this.batchedMesh.dispose(); + this.batchedMesh.removeFromParent(); + otherBatchedMesh.removeEventListener( 'dispose', this._onBatchedMeshDispose ); + + }; + + const material = otherBatchedMesh.material.clone(); + material.onBeforeCompile = otherBatchedMesh.material.onBeforeCompile; + + this.batchedMesh = new FadeBatchedMesh( otherBatchedMesh, material ); + this.tiles.group.add( this.batchedMesh ); + + } + + } else { + + if ( this.batchedMesh !== null ) { + + this._onBatchedMeshDispose(); + this._onBatchedMeshDispose = null; + + } + + } } @@ -358,12 +425,19 @@ export class TilesFadePlugin { this._fadeManager.completeAllFades(); + if ( this.batchedMesh !== null ) { + + this._onBatchedMeshDispose(); + + } + tiles.removeEventListener( 'load-model', this._onLoadModel ); tiles.removeEventListener( 'dispose-model', this._onDisposeModel ); tiles.removeEventListener( 'add-camera', this._onAddCamera ); tiles.removeEventListener( 'delete-camera', this._onDeleteCamera ); tiles.removeEventListener( 'update-before', this._onUpdateBefore ); tiles.removeEventListener( 'update-after', this._onUpdateAfter ); + tiles.removeEventListener( 'tile-visibility-change', this._onTileVisibilityChange ); tiles.forEachLoadedModel( ( scene, tile ) => { this._fadeManager.deleteObject( tile ); @@ -377,4 +451,26 @@ export class TilesFadePlugin { } + forEachBatchIds( tile, cb ) { + + this.initBatchedMesh(); + + if ( this.batchedMesh ) { + + const batchedPlugin = this.tiles.getPluginByName( 'BATCHED_MESH_PLUGIN' ); + const instanceIds = batchedPlugin._tileToInstanceId.get( tile ); + if ( instanceIds ) { + + instanceIds.forEach( id => { + + cb( id, this.batchedMesh, batchedPlugin ); + + } ); + + } + + } + + } + } diff --git a/src/plugins/three/fade/wrapFadeMaterial.js b/src/plugins/three/fade/wrapFadeMaterial.js index 4f0adcbe5..936c65670 100644 --- a/src/plugins/three/fade/wrapFadeMaterial.js +++ b/src/plugins/three/fade/wrapFadeMaterial.js @@ -13,7 +13,11 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { material.onBeforeCompile = shader => { - previousOnBeforeCompile( shader ); + if ( previousOnBeforeCompile ) { + + previousOnBeforeCompile( shader ); + + } shader.uniforms = { ...shader.uniforms, From 249cddc8f8b723719d6dff85637a2ae608da2b31 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 18:51:56 +0900 Subject: [PATCH 09/16] fix --- src/plugins/three/fade/TilesFadePlugin.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index 01091894b..fa487c815 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -358,10 +358,8 @@ export class TilesFadePlugin { initBatchedMesh() { - const batchedPlugin = this.tiles.getPluginByName( 'BATCHED_MESH_PLUGIN' ); - const otherBatchedMesh = batchedPlugin.batchedMesh; - - if ( batchedPlugin && batchedPlugin.batchedMesh ) { + const otherBatchedMesh = this.tiles.getPluginByName( 'BATCHED_MESH_PLUGIN' )?.batchedMesh; + if ( otherBatchedMesh ) { if ( this.batchedMesh === null ) { From 785cc348c146826342b999f92f4f1fcaa19a78a7 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 21:56:49 +0900 Subject: [PATCH 10/16] some fixes --- src/plugins/three/fade/FadeBatchedMesh.js | 7 +++++-- src/plugins/three/fade/PassThroughBatchedMesh.js | 2 -- src/plugins/three/fade/wrapFadeMaterial.js | 12 ++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index bf928d0ff..02c68b9df 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -13,7 +13,10 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { this._initFadeTexture(); const material = this.material; - wrapFadeMaterial( material, material.onBeforeCompile ); + const params = wrapFadeMaterial( material, material.onBeforeCompile ); + params.fadeTexture.value = this.fadeTexture; + material.defines.FEATURE_FADE = 1; + material.defines.USE_BATCHING_FRAG = 1; material.needsUpdate = true; } @@ -24,7 +27,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { size = Math.ceil( size ); // 4 floats per RGBA pixel initialized to white - const fadeArray = new Float32Array( size * size * 4 ).fill( 1 ); + const fadeArray = new Float32Array( size * size * 2 ).fill( 1 ); const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); this.fadeTexture = fadeTexture; diff --git a/src/plugins/three/fade/PassThroughBatchedMesh.js b/src/plugins/three/fade/PassThroughBatchedMesh.js index 560828a2d..fea627fa3 100644 --- a/src/plugins/three/fade/PassThroughBatchedMesh.js +++ b/src/plugins/three/fade/PassThroughBatchedMesh.js @@ -13,8 +13,6 @@ export class PassThroughBatchedMesh { this._availableInstanceIds = []; this._visibilityChanged = true; - material.color.set( 0xff0000 ); - const proxyTarget = new Proxy( this, { get( target, key ) { diff --git a/src/plugins/three/fade/wrapFadeMaterial.js b/src/plugins/three/fade/wrapFadeMaterial.js index 936c65670..263cc7869 100644 --- a/src/plugins/three/fade/wrapFadeMaterial.js +++ b/src/plugins/three/fade/wrapFadeMaterial.js @@ -26,9 +26,9 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { shader.vertexShader = shader.vertexShader .replace( - /void\s+main()\s+{/, + /void\s+main\(\)\s+{/, value => /* glsl */` - #ifdef USE_BATCHING + #ifdef USE_BATCHING_FRAG varying float vBatchId; @@ -36,10 +36,10 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { ${ value } - #ifdef USE_BATCHING + #ifdef USE_BATCHING_FRAG // add 0.5 to the value to avoid floating error that may cause flickering - vBatchId = batchId + 0.5; + vBatchId = getIndirectIndex( gl_DrawID ) + 0.5; #endif ` @@ -64,7 +64,7 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { } - #ifdef USE_BATCHING + #ifdef USE_BATCHING_FRAG uniform sampler2D fadeTexture; varying float vBatchId; @@ -95,7 +95,7 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { #if FEATURE_FADE - #ifdef USE_BATCHING + #ifdef USE_BATCHING_FRAG vec2 fadeValues = getFadeValues( vBatchId ); float fadeIn = fadeValues.r; From 8364e7576418ebc2cac075a1c1c50ac4dffc3ebf Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 22:13:19 +0900 Subject: [PATCH 11/16] Working --- src/plugins/three/fade/FadeBatchedMesh.js | 48 +++++++++++-------- .../three/fade/PassThroughBatchedMesh.js | 1 - src/plugins/three/fade/TilesFadePlugin.js | 24 ++++++---- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index 02c68b9df..ead95ab19 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -9,16 +9,25 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { super( ...args ); - this.fadeTexture = null; - this._initFadeTexture(); - const material = this.material; const params = wrapFadeMaterial( material, material.onBeforeCompile ); - params.fadeTexture.value = this.fadeTexture; material.defines.FEATURE_FADE = 1; material.defines.USE_BATCHING_FRAG = 1; material.needsUpdate = true; + this.fadeTexture = null; + this._fadeParams = params; + + this._initFadeTexture(); + + + } + + setFadeAt( index, fadeIn, fadeOut ) { + + this._initFadeTexture(); + this.fadeTexture.setValueAt( index, fadeIn, fadeOut ); + } _initFadeTexture() { @@ -26,33 +35,32 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { let size = Math.sqrt( this._maxInstanceCount ); size = Math.ceil( size ); - // 4 floats per RGBA pixel initialized to white - const fadeArray = new Float32Array( size * size * 2 ).fill( 1 ); - const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); + const oldFadeTexture = this.fadeTexture; + if ( ! this.fadeTexture || this.fadeTexture.image.data.length !== size * size * 2 ) { - this.fadeTexture = fadeTexture; + // 4 floats per RGBA pixel initialized to white + const fadeArray = new Float32Array( size * size * 2 ); + const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); - } + if ( oldFadeTexture ) { - setInstanceCount( ...args ) { + const src = oldFadeTexture.image.data; + const dst = this.fadeTexture.image.data; + const len = Math.min( src.length, dst.length ); + dst.set( new src.constructor( src.buffer, 0, len ) ); - super.setInstanceCount( ...args ); + } - // update texture data for instance sampling - const oldFadeTexture = this.fadeTexture; - oldFadeTexture.dispose(); - this._initFadeTexture(); + this.fadeTexture = fadeTexture; + this._fadeParams.fadeTexture.value = fadeTexture; + fadeTexture.needsUpdate = true; - const src = oldFadeTexture.image.data; - const dst = this.fadeTexture.image.data; - const len = Math.min( src.length, dst.length ); - dst.set( new src.constructor( src.buffer, 0, len ) ); + } } dispose() { - super.dispose(); this.fadeTexture.dispose(); } diff --git a/src/plugins/three/fade/PassThroughBatchedMesh.js b/src/plugins/three/fade/PassThroughBatchedMesh.js index fea627fa3..fc7778df4 100644 --- a/src/plugins/three/fade/PassThroughBatchedMesh.js +++ b/src/plugins/three/fade/PassThroughBatchedMesh.js @@ -10,7 +10,6 @@ export class PassThroughBatchedMesh { this.visible = true; this.parent = null; this._instanceInfo = []; - this._availableInstanceIds = []; this._visibilityChanged = true; const proxyTarget = new Proxy( this, { diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index fa487c815..e39e78fe5 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -39,7 +39,6 @@ function onTileVisibilityChange( tile, visible ) { tile[ HAS_POPPED_IN ] = true; - } else { this._fadeManager.fadeIn( tile ); @@ -223,19 +222,25 @@ function onUpdateAfter() { lruCache.markUsed( tile ); fadeMaterialManager.setFade( tile.cached.scene, fadeIn, fadeOut ); - const isFading = fadeIn !== 0 && fadeIn !== 1 || fadeOut !== 0 && fadeIn !== 1; - this.forEachBatchIds( tile, ( id, batchedMesh ) => { - - if ( isFading ) { + const isFading = fadeManager.isFading( tile ); + this.forEachBatchIds( tile, ( id, batchedMesh, plugin ) => { - batchedMesh.fadeTexture.setValueAt( id, fadeIn, fadeOut ); - - } + batchedMesh.setFadeAt( id, fadeIn, fadeOut ); + batchedMesh.setVisibleAt( id, isFading ); + plugin.batchedMesh.setVisibleAt( id, ! isFading ); } ); } ); + if ( this.batchedMesh ) { + + const material = this.tiles.getPluginByName( 'BATCHED_MESH_PLUGIN' ).batchedMesh.material; + this.batchedMesh.material.map = material.map; + this.batchedMesh.material.needsUpdate = true; + + } + } export class TilesFadePlugin { @@ -339,7 +344,8 @@ export class TilesFadePlugin { this.forEachBatchIds( tile, ( id, batchedMesh, plugin ) => { - batchedMesh.setVisibleAt( id, visible ); + batchedMesh.setFadeAt( id, 0, 0 ); + batchedMesh.setVisibleAt( id, true ); plugin.batchedMesh.setVisibleAt( id, false ); } ); From aa0b7cb45be846a111331c8bf055503092c100ca Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 22:23:48 +0900 Subject: [PATCH 12/16] updats --- src/plugins/three/fade/TilesFadePlugin.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/plugins/three/fade/TilesFadePlugin.js b/src/plugins/three/fade/TilesFadePlugin.js index e39e78fe5..c18c0a131 100644 --- a/src/plugins/three/fade/TilesFadePlugin.js +++ b/src/plugins/three/fade/TilesFadePlugin.js @@ -147,27 +147,22 @@ function onUpdateAfter() { // TODO const scene = t.cached.scene; - if ( ! scene ) { - - return; - - } // if a tile is fading out then it may not be traversed and thus will not have // the frustum flag set correctly. - if ( fadeManager.isFadingOut( t ) ) { - - scene.visible = true; + const isFadingOut = fadeManager.isFadingOut( t ); + if ( scene ) { - } else { + scene.visible = isFadingOut || t.__inFrustum; - if ( scene ) { + } - scene.visible = t.__inFrustum; + this.forEachBatchIds( ( id, batchedMesh, plugin ) => { - } + batchedMesh.setVisibleAt( id, isFadingOut || t.__inFrustum ); + plugin.batchedMesh.setVisibleAt( id, t.__inFrustum ); - } + } ); } ); From 7b569e72b19f1dd3870d9b03188f42f41b116153 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 22:26:10 +0900 Subject: [PATCH 13/16] fix assignment --- src/base/TilesRendererBase.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/TilesRendererBase.js b/src/base/TilesRendererBase.js index 8a681dc08..60bce8212 100644 --- a/src/base/TilesRendererBase.js +++ b/src/base/TilesRendererBase.js @@ -472,7 +472,7 @@ export class TilesRendererBase { if ( parentTile === null ) { tile.__depth = 0; - tile.__depthFromRenderedParent = 0; + tile.__depthFromRenderedParent = ( tile.__hasRenderableContent ? 1 : 0 ); tile.refine = tile.refine || 'REPLACE'; } else { From 146296b92f5da631a12420aa91a343e2ec1ee36f Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 23:09:24 +0900 Subject: [PATCH 14/16] 32 -> 8 bit fade texture --- example/googleMapsExample.js | 5 +---- src/plugins/three/fade/FadeBatchedMesh.js | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/example/googleMapsExample.js b/example/googleMapsExample.js index f4f8786d5..44562ca91 100644 --- a/example/googleMapsExample.js +++ b/example/googleMapsExample.js @@ -65,6 +65,7 @@ function reinstantiateTiles() { tiles.registerPlugin( new TileCompressionPlugin() ); tiles.registerPlugin( new UpdateOnChangePlugin() ); tiles.registerPlugin( new UnloadTilesPlugin() ); + tiles.registerPlugin( new TilesFadePlugin() ); tiles.registerPlugin( new GLTFExtensionsPlugin( { // Note the DRACO compression files need to be supplied via an explicit source. // We use unpkg here but in practice should be provided by the application. @@ -80,10 +81,6 @@ function reinstantiateTiles() { instanceCount: 250, } ) ); - } else { - - tiles.registerPlugin( new TilesFadePlugin() ); - } tiles.group.rotation.x = - Math.PI / 2; diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index ead95ab19..7853554d8 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -26,7 +26,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { setFadeAt( index, fadeIn, fadeOut ) { this._initFadeTexture(); - this.fadeTexture.setValueAt( index, fadeIn, fadeOut ); + this.fadeTexture.setValueAt( index, fadeIn * 255, fadeOut * 255 ); } @@ -39,7 +39,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { if ( ! this.fadeTexture || this.fadeTexture.image.data.length !== size * size * 2 ) { // 4 floats per RGBA pixel initialized to white - const fadeArray = new Float32Array( size * size * 2 ); + const fadeArray = new Uint8Array( size * size * 2 ); const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); if ( oldFadeTexture ) { From 66c6642b398d548b1f9a1aa5a230e63793ab8d2c Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 24 Dec 2024 23:14:02 +0900 Subject: [PATCH 15/16] Fix texture type --- src/plugins/three/fade/FadeBatchedMesh.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index 7853554d8..14f6cc06a 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -1,6 +1,6 @@ import { PassThroughBatchedMesh } from './PassThroughBatchedMesh.js'; import { InstanceDataTexture } from './InstanceDataTexture.js'; -import { FloatType, RGFormat } from 'three'; +import { RGFormat, UnsignedByteType } from 'three'; import { wrapFadeMaterial } from './wrapFadeMaterial.js'; export class FadeBatchedMesh extends PassThroughBatchedMesh { @@ -40,7 +40,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { // 4 floats per RGBA pixel initialized to white const fadeArray = new Uint8Array( size * size * 2 ); - const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, FloatType ); + const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, UnsignedByteType ); if ( oldFadeTexture ) { From 23f4e504718ca088b3d7a4abcae3bf93631a9331 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Wed, 25 Dec 2024 11:52:52 +0900 Subject: [PATCH 16/16] comments, clarification --- src/plugins/three/fade/FadeBatchedMesh.js | 55 ++++++++++++++++--- src/plugins/three/fade/FadeMaterialManager.js | 3 + src/plugins/three/fade/InstanceDataTexture.js | 32 ----------- .../three/fade/PassThroughBatchedMesh.js | 27 ++++----- src/plugins/three/fade/wrapFadeMaterial.js | 4 ++ 5 files changed, 65 insertions(+), 56 deletions(-) delete mode 100644 src/plugins/three/fade/InstanceDataTexture.js diff --git a/src/plugins/three/fade/FadeBatchedMesh.js b/src/plugins/three/fade/FadeBatchedMesh.js index 14f6cc06a..b2f8e5b8c 100644 --- a/src/plugins/three/fade/FadeBatchedMesh.js +++ b/src/plugins/three/fade/FadeBatchedMesh.js @@ -1,6 +1,5 @@ import { PassThroughBatchedMesh } from './PassThroughBatchedMesh.js'; -import { InstanceDataTexture } from './InstanceDataTexture.js'; -import { RGFormat, UnsignedByteType } from 'three'; +import { RGFormat, UnsignedByteType, DataTexture } from 'three'; import { wrapFadeMaterial } from './wrapFadeMaterial.js'; export class FadeBatchedMesh extends PassThroughBatchedMesh { @@ -9,20 +8,20 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { super( ...args ); + // construct a version of the material that supports fading const material = this.material; const params = wrapFadeMaterial( material, material.onBeforeCompile ); material.defines.FEATURE_FADE = 1; material.defines.USE_BATCHING_FRAG = 1; material.needsUpdate = true; + // fade parameters this.fadeTexture = null; this._fadeParams = params; - this._initFadeTexture(); - - } + // Set the fade state setFadeAt( index, fadeIn, fadeOut ) { this._initFadeTexture(); @@ -30,20 +29,26 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { } + // initialize the texture and resize it if needed _initFadeTexture() { + // calculate the new size let size = Math.sqrt( this._maxInstanceCount ); size = Math.ceil( size ); + const length = size * size * 2; const oldFadeTexture = this.fadeTexture; - if ( ! this.fadeTexture || this.fadeTexture.image.data.length !== size * size * 2 ) { + if ( ! oldFadeTexture || oldFadeTexture.image.data.length !== length ) { - // 4 floats per RGBA pixel initialized to white - const fadeArray = new Uint8Array( size * size * 2 ); + // 2 bytes per RG pixel + const fadeArray = new Uint8Array( length ); const fadeTexture = new InstanceDataTexture( fadeArray, size, size, RGFormat, UnsignedByteType ); + // copy the data from the old fade texture if it exists if ( oldFadeTexture ) { + oldFadeTexture.dispose(); + const src = oldFadeTexture.image.data; const dst = this.fadeTexture.image.data; const len = Math.min( src.length, dst.length ); @@ -51,6 +56,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { } + // assign the new fade texture to the uniform, member variable this.fadeTexture = fadeTexture; this._fadeParams.fadeTexture.value = fadeTexture; fadeTexture.needsUpdate = true; @@ -59,6 +65,7 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { } + // dispose the fade texture. Super cannot be used here due to proxy dispose() { this.fadeTexture.dispose(); @@ -66,3 +73,35 @@ export class FadeBatchedMesh extends PassThroughBatchedMesh { } } + +// Version of data texture that can assign pixel values +class InstanceDataTexture extends DataTexture { + + setValueAt( instance, ...values ) { + + const { data, width, height } = this.image; + const itemSize = Math.floor( data.length / ( width * height ) ); + let needsUpdate = false; + for ( let i = 0; i < itemSize; i ++ ) { + + const index = instance * itemSize + i; + const prevValue = data[ index ]; + const newValue = values[ i ] || 0; + if ( prevValue !== newValue ) { + + data[ index ] = newValue; + needsUpdate = true; + + } + + } + + if ( needsUpdate ) { + + this.needsUpdate = true; + + } + + } + +} diff --git a/src/plugins/three/fade/FadeMaterialManager.js b/src/plugins/three/fade/FadeMaterialManager.js index f6e432753..27fb33e48 100644 --- a/src/plugins/three/fade/FadeMaterialManager.js +++ b/src/plugins/three/fade/FadeMaterialManager.js @@ -1,5 +1,6 @@ import { wrapFadeMaterial } from './wrapFadeMaterial.js'; +// Class for managing and updating extended fade parameters export class FadeMaterialManager { constructor() { @@ -18,6 +19,7 @@ export class FadeMaterialManager { } + // traverse the scene and update the fade parameters of all materials const fadeParams = this._fadeParams; scene.traverse( child => { @@ -69,6 +71,7 @@ export class FadeMaterialManager { } + // revert the materials const fadeParams = this._fadeParams; scene.traverse( child => { diff --git a/src/plugins/three/fade/InstanceDataTexture.js b/src/plugins/three/fade/InstanceDataTexture.js deleted file mode 100644 index 65a55d348..000000000 --- a/src/plugins/three/fade/InstanceDataTexture.js +++ /dev/null @@ -1,32 +0,0 @@ -import { DataTexture } from 'three'; - -export class InstanceDataTexture extends DataTexture { - - setValueAt( instance, ...values ) { - - const { data, width, height } = this.image; - const itemSize = Math.floor( data.length / ( width * height ) ); - let needsUpdate = false; - for ( let i = 0; i < itemSize; i ++ ) { - - const index = instance * itemSize + i; - const prevValue = data[ index ]; - const newValue = values[ i ] || 0; - if ( prevValue !== newValue ) { - - data[ index ] = newValue; - needsUpdate = true; - - } - - } - - if ( needsUpdate ) { - - this.needsUpdate = true; - - } - - } - -} diff --git a/src/plugins/three/fade/PassThroughBatchedMesh.js b/src/plugins/three/fade/PassThroughBatchedMesh.js index fc7778df4..36777e796 100644 --- a/src/plugins/three/fade/PassThroughBatchedMesh.js +++ b/src/plugins/three/fade/PassThroughBatchedMesh.js @@ -1,17 +1,22 @@ import { MeshBasicMaterial } from 'three'; -// NOTE this will not automatically delete instances on geometry delete. +// A hacky version of BatchedMesh that passes through functions and geometry and other fields from the underlying +// BatchedMesh. Calls to "this" or "super" will not work in subfunctions. export class PassThroughBatchedMesh { constructor( other, material = new MeshBasicMaterial() ) { + // the other batched mesh this.other = other; + + // guarded fields this.material = material; this.visible = true; this.parent = null; this._instanceInfo = []; this._visibilityChanged = true; + // the proxy instance tht pass through arguments to the underlying mesh const proxyTarget = new Proxy( this, { get( target, key ) { @@ -22,6 +27,7 @@ export class PassThroughBatchedMesh { } else { + // sync instances on function call and call functions on "this" instance const value = other[ key ]; if ( value instanceof Function ) { @@ -58,7 +64,6 @@ export class PassThroughBatchedMesh { }, - deleteProperty( target, key ) { if ( key in target ) { @@ -73,20 +78,10 @@ export class PassThroughBatchedMesh { }, - // ownKeys() { - - // }, - - // has(target, key) { - - // }, - - // defineProperty(target, key, descriptor) { - // }, - - // getOwnPropertyDescriptor(target, key) { - // }, - + // ownKeys() {}, + // has(target, key) {}, + // defineProperty(target, key, descriptor) {}, + // getOwnPropertyDescriptor(target, key) {}, } ); diff --git a/src/plugins/three/fade/wrapFadeMaterial.js b/src/plugins/three/fade/wrapFadeMaterial.js index 263cc7869..7208fc102 100644 --- a/src/plugins/three/fade/wrapFadeMaterial.js +++ b/src/plugins/three/fade/wrapFadeMaterial.js @@ -1,3 +1,5 @@ +// Adjusts the provided material to support fading in and out using a bayer pattern. Providing a "previous" +// before compile can be used to chain shader adjustments. Returns the added uniforms used for fading. export function wrapFadeMaterial( material, previousOnBeforeCompile ) { const params = { @@ -64,8 +66,10 @@ export function wrapFadeMaterial( material, previousOnBeforeCompile ) { } + // the USE_BATCHING define is not available in fragment shaders #ifdef USE_BATCHING_FRAG + // functions for reading the fade state of a given batch id uniform sampler2D fadeTexture; varying float vBatchId; vec2 getFadeValues( const in float i ) {