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

Batch toggle #815

Merged
merged 24 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading