Skip to content

Commit

Permalink
Batch toggle (#815)
Browse files Browse the repository at this point in the history
* Add a batched mesh toggle

* Add draw call count

* Google tiles improvement

* Update

* use instanceCount

* add commented field

* tex fixes

* Use the new members

* Update

* Remove unused field

* Use copyTextureToTexture3D

* Handle already-deleted instances

* Init render target

* remove empty line

* fix merge issue

* Rename function

* Fix disposal

* Adjust expanding batched mesh

* Update example

* Update three.js package.json

* Remove commented lint
  • Loading branch information
gkjohnson authored Oct 31, 2024
1 parent 01c677f commit cb24815
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 246 deletions.
108 changes: 57 additions & 51 deletions example/googleMapsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import {
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import { estimateBytesUsed } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import { CameraTransitionManager } from './src/camera/CameraTransitionManager.js';
import { TileCompressionPlugin } from './src/plugins/TileCompressionPlugin.js';
import { UpdateOnChangePlugin } from './src/plugins/UpdateOnChangePlugin.js';
import { TilesFadePlugin } from './src/plugins/fade/TilesFadePlugin.js';
import { BatchedTilesPlugin } from './src/plugins/batched/BatchedTilesPlugin.js';

let controls, scene, renderer, tiles, transition;
let statsContainer, stats;
Expand All @@ -35,8 +35,10 @@ const params = {
enableCacheDisplay: false,
enableRendererStats: false,
apiKey: apiKey,
useBatchedMesh: Boolean( new URLSearchParams( window.location.hash.replace( /^#/, '' ) ).get( 'batched' ) ),
errorTarget: 40,

'reload': reinstantiateTiles,
reload: reinstantiateTiles,

};

Expand All @@ -59,7 +61,17 @@ function reinstantiateTiles() {
tiles.registerPlugin( new GoogleCloudAuthPlugin( { apiToken: params.apiKey, autoRefreshToken: true } ) );
tiles.registerPlugin( new TileCompressionPlugin() );
tiles.registerPlugin( new UpdateOnChangePlugin() );
tiles.registerPlugin( new TilesFadePlugin() );

if ( params.useBatchedMesh ) {

tiles.registerPlugin( new BatchedTilesPlugin( { renderer } ) );

} else {

tiles.registerPlugin( new TilesFadePlugin() );

}

tiles.group.rotation.x = - Math.PI / 2;

// Note the DRACO compression files need to be supplied via an explicit source.
Expand Down Expand Up @@ -138,13 +150,19 @@ function init() {

} );

const mapsOptions = gui.addFolder( 'Google Tiles' );
const mapsOptions = gui.addFolder( 'Google Photorealistic Tiles' );
mapsOptions.add( params, 'apiKey' );
mapsOptions.add( params, 'useBatchedMesh' ).listen();
mapsOptions.add( params, 'reload' );

const exampleOptions = gui.addFolder( 'Example Options' );
exampleOptions.add( params, 'enableCacheDisplay' );
exampleOptions.add( params, 'enableRendererStats' );
exampleOptions.add( params, 'errorTarget', 5, 100, 1 ).onChange( () => {

tiles.getPluginByName( 'UPDATE_ON_CHANGE_PLUGIN' ).needsUpdate = true;

} );

statsContainer = document.createElement( 'div' );
document.getElementById( 'info' ).appendChild( statsContainer );
Expand Down Expand Up @@ -207,22 +225,34 @@ function updateHash() {
cartographicResult.lon *= MathUtils.RAD2DEG;

// update hash
const params = new URLSearchParams();
params.set( 'lat', cartographicResult.lat.toFixed( 4 ) );
params.set( 'lon', cartographicResult.lon.toFixed( 4 ) );
params.set( 'height', cartographicResult.height.toFixed( 2 ) );
params.set( 'az', orientationResult.azimuth.toFixed( 2 ) );
params.set( 'el', orientationResult.elevation.toFixed( 2 ) );
params.set( 'roll', orientationResult.roll.toFixed( 2 ) );
window.history.replaceState( undefined, undefined, `#${ params }` );
const urlParams = new URLSearchParams();
urlParams.set( 'lat', cartographicResult.lat.toFixed( 4 ) );
urlParams.set( 'lon', cartographicResult.lon.toFixed( 4 ) );
urlParams.set( 'height', cartographicResult.height.toFixed( 2 ) );
urlParams.set( 'az', orientationResult.azimuth.toFixed( 2 ) );
urlParams.set( 'el', orientationResult.elevation.toFixed( 2 ) );
urlParams.set( 'roll', orientationResult.roll.toFixed( 2 ) );

if ( params.useBatchedMesh ) {

urlParams.set( 'batched', 1 );

}
window.history.replaceState( undefined, undefined, `#${ urlParams }` );

}

function initFromHash() {

const hash = window.location.hash.replace( /^#/, '' );
const params = new URLSearchParams( hash );
if ( ! params.has( 'lat' ) && ! params.has( 'lon' ) ) {
const urlParams = new URLSearchParams( hash );
if ( urlParams.has( 'batched' ) ) {

params.useBatchedMesh = Boolean( urlParams.get( 'batched' ) );

}

if ( ! urlParams.has( 'lat' ) && ! urlParams.has( 'lon' ) ) {

return;

Expand All @@ -233,16 +263,16 @@ function initFromHash() {

// get the position fields
const camera = transition.camera;
const lat = parseFloat( params.get( 'lat' ) );
const lon = parseFloat( params.get( 'lon' ) );
const height = parseFloat( params.get( 'height' ) ) || 1000;
const lat = parseFloat( urlParams.get( 'lat' ) );
const lon = parseFloat( urlParams.get( 'lon' ) );
const height = parseFloat( urlParams.get( 'height' ) ) || 1000;

if ( params.has( 'az' ) && params.has( 'el' ) ) {
if ( urlParams.has( 'az' ) && urlParams.has( 'el' ) ) {

// get the az el fields for rotation if present
const az = parseFloat( params.get( 'az' ) );
const el = parseFloat( params.get( 'el' ) );
const roll = parseFloat( params.get( 'roll' ) ) || 0;
const az = parseFloat( urlParams.get( 'az' ) );
const el = parseFloat( urlParams.get( 'el' ) );
const roll = parseFloat( urlParams.get( 'roll' ) ) || 0;

// extract the east-north-up frame into matrix world
WGS84_ELLIPSOID.getRotationMatrixFromAzElRoll(
Expand Down Expand Up @@ -287,6 +317,7 @@ function animate() {

// update tiles
camera.updateMatrixWorld();
tiles.errorTarget = params.errorTarget;
tiles.update();

renderer.render( scene, camera );
Expand All @@ -299,48 +330,23 @@ function animate() {
function updateHtml() {

// render html text updates
const cacheFullness = tiles.lruCache.itemList.length / tiles.lruCache.maxSize;
let str = '';

if ( params.enableCacheDisplay ) {

const lruCache = tiles.lruCache;
const cacheFullness = lruCache.cachedBytes / lruCache.maxBytesSize;
str += `Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }<br/>`;

const geomSet = new Set();
tiles.traverse( tile => {

const scene = tile.cached.scene;
if ( scene ) {

scene.traverse( c => {

if ( c.geometry ) {

geomSet.add( c.geometry );

}

} );

}

} );

let count = 0;
geomSet.forEach( g => {

count += estimateBytesUsed( g );

} );
str += `Cache: ${ ( 100 * cacheFullness ).toFixed( 2 ) }% ~${ ( count / 1000 / 1000 ).toFixed( 2 ) }mb<br/>`;
str += `Cache: ${ ( 100 * cacheFullness ).toFixed( 2 ) }% ~${ ( lruCache.cachedBytes / 1000 / 1000 ).toFixed( 2 ) }mb<br/>`;

}

if ( params.enableRendererStats ) {

const memory = renderer.info.memory;
const render = renderer.info.render;
const programCount = renderer.info.programs.length;
str += `Geometries: ${ memory.geometries } Textures: ${ memory.textures } Programs: ${ programCount }`;
str += `Geometries: ${ memory.geometries } Textures: ${ memory.textures } Programs: ${ programCount } Draw Calls: ${ render.calls }`;

}

Expand Down
1 change: 1 addition & 0 deletions example/src/plugins/UpdateOnChangePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class UpdateOnChangePlugin {

constructor() {

this.name = 'UPDATE_ON_CHANGE_PLUGIN';
this.tiles = null;
this.needsUpdate = false;
this.cameraMatrices = new Map();
Expand Down
51 changes: 0 additions & 51 deletions example/src/plugins/batched/ArrayTextureCopyMaterial.js

This file was deleted.

52 changes: 21 additions & 31 deletions example/src/plugins/batched/BatchedTilesPlugin.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { WebGLArrayRenderTarget, MeshBasicMaterial, Group, DataTexture, REVISION } from 'three';
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
import { ExpandingBatchedMesh } from './ExpandingBatchedMesh.js';
import { ArrayTextureCopyMaterial } from './ArrayTextureCopyMaterial.js';
import { convertMapToArrayTexture, isColorWhite } from './utilities.js';

const _textureRenderQuad = new FullScreenQuad( new MeshBasicMaterial() );
const _layerCopyQuad = new FullScreenQuad( new ArrayTextureCopyMaterial() );
const _whiteTex = new DataTexture( new Uint8Array( [ 255, 255, 255, 255 ] ), 1, 1 );
_whiteTex.needsUpdate = true;

Expand Down Expand Up @@ -133,11 +131,11 @@ export class BatchedTilesPlugin {
const texture = material.map;
if ( texture ) {

this.renderTextureToLayer( texture, instanceId );
this.assignTextureToLayer( texture, instanceId );

} else {

this.renderTextureToLayer( _whiteTex, instanceId );
this.assignTextureToLayer( _whiteTex, instanceId );

}

Expand Down Expand Up @@ -205,7 +203,7 @@ export class BatchedTilesPlugin {
}

// init the batched mesh
const { instanceCount, vertexCount, indexCount, tiles } = this;
const { instanceCount, vertexCount, indexCount, tiles, renderer } = this;
const material = this.material ? this.material : new target.material.constructor();
const batchedMesh = new ExpandingBatchedMesh( instanceCount, instanceCount * vertexCount, instanceCount * indexCount, material );
batchedMesh.name = 'BatchTilesPlugin';
Expand All @@ -215,19 +213,20 @@ export class BatchedTilesPlugin {

// init the array texture render target
const map = target.material.map;
const textureOptions = {
const textureOptions = {
colorSpace: map.colorSpace,
wrapS: map.wrapS,
wrapT: map.wrapT,
wrapR: map.wrapS,
// TODO: Generating mipmaps for the volume every time a new texture is added is extremely slow
// generateMipmaps: map.generateMipmaps,
// minFilter: map.minFilter,
// magFilter: map.magFilter,
magFilter: map.magFilter,
};

const arrayTarget = new WebGLArrayRenderTarget( map.image.width, map.image.height, instanceCount );
Object.assign( arrayTarget.texture, textureOptions );
renderer.initRenderTarget( arrayTarget );

// init the material
material.map = arrayTarget.texture;
Expand All @@ -239,7 +238,7 @@ export class BatchedTilesPlugin {
}

// render the given into the given layer
renderTextureToLayer( texture, layer ) {
assignTextureToLayer( texture, layer ) {

this.expandArrayTargetIfNeeded();

Expand Down Expand Up @@ -278,20 +277,9 @@ export class BatchedTilesPlugin {
const newArrayTarget = new WebGLArrayRenderTarget( arrayTarget.width, arrayTarget.height, targetDepth );
Object.assign( newArrayTarget.texture, textureOptions );

// render each old layer into the new texture target
const currentRenderTarget = renderer.getRenderTarget();
for ( let i = 0; i < arrayTarget.depth; i ++ ) {

_layerCopyQuad.material.map = arrayTarget.texture;
_layerCopyQuad.material.layer = i;
renderer.setRenderTarget( newArrayTarget, i );
_layerCopyQuad.render( renderer );

}

// reset the state
renderer.setRenderTarget( currentRenderTarget );
_layerCopyQuad.material.map = null;
// copy the contents
renderer.initRenderTarget( newArrayTarget );
renderer.copyTextureToTexture( arrayTarget.texture, newArrayTarget.texture );

// replace the old array target
arrayTarget.dispose();
Expand Down Expand Up @@ -324,23 +312,25 @@ export class BatchedTilesPlugin {

dispose() {

if ( this.arrayTarget ) {
const { arrayTarget, tiles, batchedMesh } = this;
if ( arrayTarget ) {

this.arrayTarget.dispose();
arrayTarget.dispose();

}

if ( this.batchedMesh ) {
if ( batchedMesh ) {

this.batchedMesh.material.dispose();
this.batchedMesh.dispose();
this.batchedMesh.removeFromParent();
batchedMesh.material.dispose();
batchedMesh.geometry.dispose();
batchedMesh.dispose();
batchedMesh.removeFromParent();

}

this.tiles.removeEventListener( 'load-model', this._onLoadModel );
this.tiles.removeEventListener( 'dispose-model', this._onDisposeModel );
this.tiles.removeEventListener( 'tile-visibility-change', this._onVisibilityChange );
tiles.removeEventListener( 'load-model', this._onLoadModel );
tiles.removeEventListener( 'dispose-model', this._onDisposeModel );
tiles.removeEventListener( 'tile-visibility-change', this._onVisibilityChange );

}

Expand Down
Loading

0 comments on commit cb24815

Please sign in to comment.