Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for fading BatchedMesh #894

Merged
merged 17 commits into from
Dec 25, 2024
5 changes: 1 addition & 4 deletions example/googleMapsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -80,10 +81,6 @@ function reinstantiateTiles() {
instanceCount: 250,
} ) );

} else {

tiles.registerPlugin( new TilesFadePlugin() );

}

tiles.group.rotation.x = - Math.PI / 2;
Expand Down
2 changes: 1 addition & 1 deletion src/base/TilesRendererBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
107 changes: 107 additions & 0 deletions src/plugins/three/fade/FadeBatchedMesh.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { PassThroughBatchedMesh } from './PassThroughBatchedMesh.js';
import { RGFormat, UnsignedByteType, DataTexture } from 'three';
import { wrapFadeMaterial } from './wrapFadeMaterial.js';

export class FadeBatchedMesh extends PassThroughBatchedMesh {

constructor( ...args ) {

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;

}

// Set the fade state
setFadeAt( index, fadeIn, fadeOut ) {

this._initFadeTexture();
this.fadeTexture.setValueAt( index, fadeIn * 255, fadeOut * 255 );

}

// 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 ( ! oldFadeTexture || oldFadeTexture.image.data.length !== length ) {

// 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 );
dst.set( new src.constructor( src.buffer, 0, len ) );

}

// assign the new fade texture to the uniform, member variable
this.fadeTexture = fadeTexture;
this._fadeParams.fadeTexture.value = fadeTexture;
fadeTexture.needsUpdate = true;

}

}

// dispose the fade texture. Super cannot be used here due to proxy
dispose() {

this.fadeTexture.dispose();

}

}

// 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;

}

}

}
173 changes: 10 additions & 163 deletions src/plugins/three/fade/FadeManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,13 @@ 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;
this.onFadeSetStart = null;

}

// 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 ) {

Expand All @@ -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 <dithering_fragment>/, 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
Expand All @@ -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;

}
Expand All @@ -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 --;
Expand Down Expand Up @@ -230,7 +96,11 @@ export class FadeManager {

forEachObject( cb ) {

this._fadeState.forEach( ( info, scene ) => cb( scene ) );
this._fadeState.forEach( ( info, object ) => {

cb( object, info );

} );

}

Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down
Loading
Loading