Git Product home page Git Product logo

3dtilesrendererjs's Introduction

3d-tiles-renderer

npm version build

Three.js renderer implementation for the 3D Tiles format. The renderer supports most of the 3D Tiles spec features with a few exceptions. See Issue #15 for information on which features are not yet implemented.

If a tile set or geometry does not load or render properly please make an issue! Example data is needed for adding and testing features.

Examples

Dingo Gap Mars dataset with multiple tile sets

Kitchen sink example with all options here

Rendering in VR example here

External Tiles Providers

Personal Google Tiles API Key or Cesium Ion API Key required

Cesium Ion 3D Tiles

Google Photorealistic Tiles

Google Globe Tiles

Customization

Custom material example

Rendering shadows from offscreen tiles example

Tile fade transition

Debug Pages

B3DM Loading

I3DM Loading

PNTS Loading

Ellipsoid Region Bounds

Use

Installation

npm install 3d-tiles-renderer --save

Basic TilesRenderer

Setting up a basic application with a 3D Tile Set.

import { TilesRenderer } from '3d-tiles-renderer';

// ... initialize three scene ...

const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );
tilesRenderer.setCamera( camera );
tilesRenderer.setResolutionFromRenderer( camera, renderer );
scene.add( tilesRenderer.group );

renderLoop();

function renderLoop() {

	requestAnimationFrame( renderLoop );

	// The camera matrix is expected to be up to date
	// before calling tilesRenderer.update
	camera.updateMatrixWorld();
	tilesRenderer.update();
	renderer.render( scene, camera );

}

Custom Material

Setting up a 3D Tile Set using a custom material.

const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );
tilesRenderer.setCamera( camera );
tilesRenderer.setResolutionFromRenderer( camera, renderer );
tilesRenderer.onLoadModel = function ( scene ) {

	// create a custom material for the tile
	scene.traverse( c => {

		if ( c.material ) {

			c.originalMaterial = c.material;
			c.material = new MeshBasicMaterial();

		}

	} );

};

tilesRenderer.onDisposeModel = function ( scene ) {

	// dispose of any manually created materials
	scene.traverse( c => {

		if ( c.material ) {

			c.material.dispose();

		}

	} );

};
scene.add( tilesRenderer.group );

Multiple TilesRenderers with Shared Caches and Queues

Using multiple tiles renderers that share LRUCache and PriorityQueue instances to cut down on memory and correctly prioritize downloads.

// create multiple tiles renderers
const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );
tilesRenderer.setCamera( camera );
tilesRenderer.setResolutionFromRenderer( camera, renderer );

const tilesRenderer2 = new TilesRenderer( './path/to/tileset2.json' );
tilesRenderer2.setCamera( camera );
tilesRenderer2.setResolutionFromRenderer( camera, renderer );

// set the second renderer to share the cache and queues from the first
tilesRenderer2.lruCache = tilesRenderer.lruCache;
tilesRenderer2.downloadQueue = tilesRenderer.downloadQueue;
tilesRenderer2.parseQueue = tilesRenderer.parseQueue;

// add them to the scene
scene.add( tilesRenderer.group );
scene.add( tilesRenderer2.group );

Adding DRACO Decompression Support

Adding support for DRACO decompression within the GLTF files that are transported in B3DM and I3DM formats. The same approach can be used to add support for KTX2 and DDS textures.

// 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.
const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'https://unpkg.com/[email protected]/examples/js/libs/draco/gltf/' );

const loader = new GLTFLoader( tilesRenderer.manager );
loader.setDRACOLoader( dracoLoader );

tilesRenderer.manager.addHandler( /\.gltf$/, loader );

Adding support for DRACO decompression within the PNTS files.

// 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.
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'https://unpkg.com/[email protected]/examples/js/libs/draco/gltf/' );


const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );
tilesRenderer.manager.addHandler( /\.drc$/, loader );

Loading from Cesium Ion

Loading from Cesium Ion requires some extra fetching of the ion url endpoint, as well as a temporary bearer access token. A full example is found in the ionExample.js file in the examples folder.

Set the desired assetId as well as your Ion AccessToken. More reading is provided by the Cesium REST API documentation.

// fetch a temporary token for the Cesium Ion asset
const url = new URL( `https://api.cesium.com/v1/assets/${ assetId }/endpoint` );
url.searchParams.append( 'access_token', accessToken );

fetch( url, { mode: 'cors' } )
	.then( res => res.json() )
	.then( json => {

		url = new URL( json.url );

		const version = url.searchParams.get( 'v' );
		tiles = new TilesRenderer( url );
		tiles.fetchOptions.headers = {};
		tiles.fetchOptions.headers.Authorization = `Bearer ${json.accessToken}`;

		// Prefilter each model fetch by setting the cesium Ion version to the search
		// parameters of the url.
		tiles.preprocessURL = uri => {

			uri = new URL( uri );
			uri.searchParams.append( 'v', version );
			return uri.toString();

		};

	} );

Render On Change

The tile set and model load callbacks can be used to detect when the data has changed and a new render is necessary.

let needsRerender = true;
const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );
tilesRenderer.onLoadTileSet = () => needsRerender = true;
tilesRenderer.onLoadModel = () => needsRerender = true;

function renderLoop() {

	requestAnimationFrame( renderLoop );
	if ( needsRerender ) {

		needsRerender = false;
		camera.updateMatrixWorld();
		tilesRenderer.update();
		renderer.render( scene, camera );

	}

}
renderLoop();

Read Batch Id and Batch Table Data

How to find the batch id and batch table associated with a mesh and read the data.

const tilesRenderer = new TilesRenderer( './path/to/tileset.json' );

// ...checking intersections...

const intersects = raycaster.intersectObject( scene, true );
if ( intersects.length ) {

	const { face, object } = intersects[ 0 ];
	const batchidAttr = object.geometry.getAttribute( '_batchid' );

	if ( batchidAttr ) {

		// Traverse the parents to find the batch table.
		let batchTableObject = object;
		while ( ! batchTableObject.batchTable ) {

			batchTableObject = batchTableObject.parent;

		}

		// Log the batch data
		const batchTable = batchTableObject.batchTable;
		const hoveredBatchid = batchidAttr.getX( face.a );
		const batchData = batchTable.getData( 'BatchTableKey' );
		if ( batchData ) {

			console.log( batchData[ hoveredBatchid ] );

		}

	}

}

API

TilesRenderer

extends `THREE.EventDispatcher`` & TilesRendererBase, which can be used to implement a 3d tiles renderer in other engines

events

// fired when a new root or child tile set is loaded
{ type: 'load-tile-set', tileSet: Object, url: String }

// fired when a tile model is loaded
{ type: 'load-model', scene: THREE.Group, tile: Object }

// fired when a tile model is disposed
{ type: 'dispose-model', scene: THREE.Group, tile: Object }

// fired when a tiles visibility changes
{ type: 'tile-visibility-change', scene: THREE.Group, tile: Object }

.fetchOptions

fetchOptions = {} : Object

Options passed to fetch when loading tile set and model data.

.errorTarget

errorTarget = 6 : Number

The target screenspace error in pixels to target when updating the geometry. Tiles will not render if they have below this level of screenspace error. See the "geometric error" section in the 3d tiles specification for more information.

.errorThreshold

errorThreshold = Infinity : Number

Value used to compute the threshold errorTarget * errorThreshold above which tiles will not load or render. This is used to enable traversal to skip loading and rendering parent tiles far from the cameras current screenspace error requirement. If errorThreshold is set to Infinity then all parent tiles will be loaded and rendered. If it's set to 0 then no parent tiles will render and only the tiles that are being rendered will be loaded.

Note that if the camera position zooms in or out dramatically setting this to a value other than Infinity could result in tiles flickering if the renderer updates to display tiles that were previously outside the error threshold. As such this setting is best suited for when camera movement is limited smaller movement scales such as real world movement speeds.

.maxDepth

maxDepth = Infinity : Number

The max depth to which tiles will be loaded and rendered. Setting it to 1 will only render the root tile. If the tile at depth maxDepth is an empty tile then the next set of visible children will be rendered.

.loadSiblings

loadSiblings = true : Boolean

If true then all sibling tiles will be loaded, as well, to ensure coherence when moving the camera. If false then only currently viewed tiles will be loaded.

.displayActiveTiles

displayActiveTiles = false : Boolean

"Active tiles" are those that are loaded and available but not necessarily visible. If loadSiblings is true then the tiles loaded up to the extents of the tile set will be considered active even outside the camera view. These tiles are useful for raycasting off camera or for casting shadows.

Active tiles not currently visible in a camera frustum are removed from the scene as an optimization. Setting displayActiveTiles to true will keep them in the scene to be rendered from an outside camera view not accounted for by the tiles renderer.

.autoDisableRendererCulling

autoDisableRendererCulling = true : Boolean

If true then all tile meshes automatically have their frustumCulled field set to false. This is useful particularly when using one camera because the tiles renderer automatically performs it's own frustum culling on visible tiles. If displayActiveTiles is true or multiple cameras are being used then you may consider setting this to false.

.optimizeRaycast

optimizeRaycast = true : Boolean

If true then the raycast functions of the loaded tile objects are overriden to disable raycasting and the TilesRenderer.group raycast function is used to perform a raycast over all visible tiles. This enables an optimized traversal for raycasting against tiles. If raycaster.firstHitOnly = true then as well as a more optimal traversal of tiles the raycast will end early as soon as the closest intersction is found.

If you would like to manage raycasting against tiles yourself this behavior can be disabled if needed by setting optizeRaycast to false.

.preprocessURL

preprocessURL = null : ( uri : string | URL ) => string | URL;

Function to preprocess the url for each individual tile geometry or child tile set to be loaded. If null then the url is used directly.

.lruCache

lruCache = new LRUCache() : LRUCache

NOTE: This cannot be set once update is called for the first time.

.downloadQueue

downloadQueue = new PriorityQueue : PriorityQueue

NOTE: This cannot be set once update is called for the first time.

.parseQueue

parseQueue = new PriorityQueue : PriorityQueue

NOTE: This cannot be modified once update is called for the first time.

.group

group : Group

The container group for the 3d tiles. Add this to the three.js scene in order to render it.

When raycasting a higher performance traversal approach is used (see optimizeRaycast).

.manager

manager : LoadingManager

The manager used when loading tile geometry.

.constructor

constructor( url : String )

Takes the url of the tileset.json for the tile set to be rendered.

.update

update() : void

Updates the tiles to render and kicks off loads for the appropriate tiles in the 3d tile set.

Both group.matrixWorld and all cameras world matrices are expected to be up to date before this is called.

.resetFailedTiles

resetFailedTiles() : void

If any tiles failed to load due to server or network issues then they will not be retried by automatically. This function clears all failed tile states so unloaded tiles can be retried again.

.getBoundingBox

getBoundingBox( box : Box3 ) : boolean

Sets box to the axis aligned root bounding box of the tile set in the group frame. Returns false if the tile root is not loaded and the bounding box cannot be set.

.getOrientedBoundingBox

getOrientedBoundingBox( box : Box3, boxTransform : Matrix4 ) : boolean;

Sets box and boxTransform to the bounds and matrix that describe the oriented bounding box that encapsulates the root of the tile set. Returns false if the tile root is not loaded and the bounding box cannot be set.

.getBoundingSphere

getBoundingSphere( sphere : Sphere ) : boolean;

Sets sphere to the bounding sphere that encapsulates the root of the tile set. Returns false if the tile root is not loaded and the bounding sphere cannot be set.

.hasCamera

hasCamera( camera : Camera ) : boolean

Returns true if the camera has already been set on the renderer.

.setCamera

setCamera( camera : Camera ) : boolean

Adds the camera to the camera to be accounted for when traversing the tile set. Returns false if the camera is already being tracked. Returns true otherwise.

.deleteCamera

deleteCamera( camera : Camera ) : boolean

Removes the given camera from being accounted for when traversing the tile set. Returns false if the camera was not tracked.

.setResolution

setResolution( camera : Camera, resolution : Vector2 ) : boolean
setResolution( camera : Camera, x : number, y : number ) : boolean

Sets the resolution being rendered to for the given camera. Returns false if the camera is not being tracked.

.setResolutionFromRenderer

setResolutionFromRenderer( camera : Camera, renderer : WebGLRenderer ) : boolean

Sets the resolution being rendered to for the given camera via renderer which accounts for canvas size and current pixel ratio. Returns false if the camera is not being tracked.

.forEachLoadedModel

forEachLoadedModel( callback : ( scene : Object3D, tile : object ) => void ) : void

Fires the callback for every loaded scene in the hierarchy with the associatd tile as the second argument. This can be used to update the materials of all loaded meshes in the tile set.

.onLoadTileSet

onLoadTileSet = null : ( tileSet : Tileset ) => void

Callback that is called whenever a tile set is loaded.

.onLoadModel

onLoadModel = null : ( scene : Object3D, tile : Tile ) => void

Callback that is called every time a model is loaded. This can be used in conjunction with .forEachLoadedModel to set the material of all load and still yet to load meshes in the tile set.

.onDisposeModel

onDisposeModel = null : ( scene : Object3D, tile : Tile ) => void

Callback that is called every time a model is disposed of. This should be used in conjunction with .onLoadModel to dispose of any custom materials created for a tile. Note that the textures, materials, and geometries that a tile loaded in with are all automatically disposed of even if they have been removed from the tile meshes.

.onTileVisibilityChange

onTileVisibilityChange = null : ( scene : Object3D, tile : Tile, visible : boolean ) => void

Callback that is called when a tile's visibility changed. The parameter visible is true when the tile is visible

.dispose

dispose() : void

Disposes of all the tiles in the renderer. Calls dispose on all materials, textures, and geometries that were loaded by the renderer and subsequently calls onDisposeModel for any loaded tile model.

DebugTilesRenderer

extends TilesRenderer

Special variant of TilesRenderer that includes helpers for debugging and visualizing the various tiles in the tile set. Material overrides will not work as expected with this renderer. The debug renderer includes additional logic and initialization code which can cause performance loss so it's recommended to only use this when needed.

.colorMode

colorMode = NONE : ColorMode

Which color mode to use when rendering the tile set. The following exported enumerations can be used:

// No special color mode. Uses the default materials.
NONE

// Render the screenspace error from black to white with errorTarget
// being the maximum value.
SCREEN_ERROR

// Render the geometric error from black to white with maxDebugError
// being the maximum value.
GEOMETRIC_ERROR

// Render the distance from the camera to the tile as black to white
// with maxDebugDistance being the maximum value.
DISTANCE

// Render the depth of the tile relative to the root as black to white
// with maxDebugDepth being the maximum value.
DEPTH

// Render the depth of the tile relative to the nearest rendered parent
// as black to white with maxDebugDepth being the maximum value.
RELATIVE_DEPTH

// Render leaf nodes as white and parent nodes as black.
IS_LEAF

// Render the tiles with a random color to show tile edges clearly.
RANDOM_COLOR

// Render every individual mesh in the scene with a random color.
RANDOM_NODE_COLOR

// Sets a custom color using the customColorCallback call back.
CUSTOM_COLOR

.customColorCallback

customColorCallback: (tile: Tile, child: Object3D) => void

The callback used if debugColor is set to CUSTOM_COLOR. Value defaults to null and must be set explicitly.

.displayBoxBounds

displayBoxBounds = false : Boolean

Display wireframe bounding boxes from the tiles boundingVolume.box (or derived from the region bounds) for every visible tile.

.displaySphereBounds

displaySphereBounds = false : Boolean

Display wireframe bounding boxes from the tiles boundingVolume.sphere (or derived from the bounding box / region bounds) for every visible tile.

.displayRegionBounds

displayRegionBounds = false : Boolean

Display wireframe bounding rgions from the tiles boundingVolume.region for every visible tile if it exists.

.maxDebugDepth

maxDebugDepth = - 1 : Number

The depth value that represents white when rendering with DEPTH or RELATIVE_DEPTH colorMode. If maxDebugDepth is -1 then the maximum depth of the tile set is used.

.maxDebugError

maxDebugError = - 1 : Number

The error value that represents white when rendering with GEOMETRIC_ERROR colorMode. If maxDebugError is -1 then the maximum geometric error in the tile set is used.

.maxDebugDistance

maxDebugDistance = - 1 : Number

The distance value that represents white when rendering with DISTANCE colorMode. If maxDebugDistance is -1 then the radius of the tile set is used.

.getDebugColor

getDebugColor : ( val : Number, target : Color ) => void

The function used to map a [0, 1] value to a color for debug visualizations. By default the color is mapped from black to white.

PriorityQueue

Piority-sorted queue to prioritize file downloads and parsing.

.maxJobs

maxJobs = 6 : number

The maximum number of jobs to be processing at once.

.priorityCallback

priorityCallback = null : ( itemA, itemB ) => Number

Function to derive the job priority of the given item. Higher priority values get processed first.

.schedulingCallback

schedulingCallback = requestAnimationFrame : ( cb : Function ) => void

A function used for scheduling when to run jobs next so more work doesn't happen in a single frame than there is time for -- defaults to the next frame. This should be overriden in scenarios where requestAnimationFrame is not reliable, such as when running in WebXR. See the VR demo for one example on how to handle this with WebXR.

GoogleTilesRenderer

extends TilesRenderer

Variant of the TilesRenderer designed to easily support Google's Photorealistic 3D Tiles API. Handles adding api key to all requests, reading tile credits, and initializes tile set traversal options to reasonable defaults for the globe.

constructor

constructor( apiKey: String )

Takes the Google Photorealistic Tiles API Key.

.getCreditsString

getCreditsString(): String;

Returns a string of unique credits for all the tiles currently displayed.

.setLatLonToYUp

setLatLonToYUp( lat: Number, lon: Number ): void;

Rotates and positions the local transformation of the tile group object so the surface of the globe ellipsoid at the specified latitude and longitude faces Y+, X+ points north, and Z+ points east and is centered at 0, 0, 0.

CesiumIonTilesRenderer

extends TilesRenderer

Variant of TilesRenderer designed to easily support the Cesium Ion API. Handles initial url resolution, access tokens in the header, and query parameter additions.

constructor

constructor( ionAssetId: String | Number, ionAccessToken: String )

Takes the Ion asset id and access token.

LRUCache

Utility class for the TilesRenderer to keep track of currently used items so rendered items will not be unloaded.

.maxSize

maxSize = 800 : number

The maximum cached size. If that current amount of cached items is equal to this value then no more items can be cached.

.minSize

minSize = 600 : number

The minimum cache size. Above this cached data will be unloaded if it's unused.

.unloadPercent

unloadPercent = 0.05 : number

The maximum percentage of minSize to unload during a given frame.

.unloadPriorityCallback

unloadPriorityCallback = null : ( item ) => Number

Function to derive the unload priority of the given item. Higher priority values get unloaded first.

BatchTable

.getKeys

getKeys() : Array<String>

Returns the keys of all the data in the batch table.

.getData

getData(
	key : String,
	defaultComponentType = null : String | null,
	defaultType = null : String | null,
) : Array | TypedArray | null

Returns the data associated with the key passed into the function. If the component and type are specified in the batch table contents then those values are used otherwise the values in defaultComponentType and defaultType are used. Returns null if the key is not in the table.

defaultComponentType can be set to BYTE, UNSIGNED_BYTE, SHORT, UNSIGNED_SHORT, INT, UNSIGNED_INT, FLOAT, or DOUBLE. defaultType can be set to SCALAR, VEC2, VEC3, or VEC4.

LICENSE

The software is available under the Apache V2.0 license.

Copyright ยฉ 2020 California Institute of Technology. ALL RIGHTS RESERVED. United States Government Sponsorship Acknowledged. Neither the name of Caltech nor its operating division, the Jet Propulsion Laboratory, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

3dtilesrendererjs's People

Contributors

aiden-jeffrey avatar aierklk avatar alaricbaraou avatar alex-lancer avatar dependabot[bot] avatar gkjohnson avatar gmadges avatar jeffpamer avatar jesse-small avatar jo-chemla avatar jonnxie avatar junior2ran avatar justinmanley avatar liberostelios avatar lojjic avatar nickguletskii avatar patricknausha avatar peiyu7921 avatar pettergs avatar phoenixbf avatar pizza3 avatar powerocean avatar rennzie avatar robertlong avatar rvouilloz avatar segments-tobias avatar tobias74 avatar usefulthink avatar xxzefgh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

3dtilesrendererjs's Issues

ImageLoader: Verify createImageBitmap are consistent across browsers.

See

// TODO: We should verify that `flipY` is false on the resulting texture after load because it can't be modified after
// the fact. Premultiply alpha default behavior is not well defined, either.
// TODO: Determine whether or not options are supported before using this so we can force flipY false and premultiply alpha
// behavior. Fall back to regular texture loading
manager.addHandler( /(^blob:)|(\.png$)|(\.jpg$)|(\.jpeg$)/g, {
load( url, onComplete ) {
const loader = new ImageBitmapLoader();
loader.load( url, res => {
onComplete( new CanvasTexture( res ) );
} );
}
} );

Unsupported Features

Unsupported Features

Model Formats

Batch and Feature Table Features

  • Add feature / batch table tests (Issue #65)
  • Remaining i3dm feature semantic interpretation (Issue #83)
  • Remaining pnts feature semantic interpretation (Issue #84)

Tileset Features

Models : Add a method to allow for updating materials on objects

Add a method to update all meshes with a custom material so the terrain can be displayed with custom shaders.

Some ideas:

tiles = new TilesRenderer( url );
tiles.forEachLoadedModel( scene => {

  scene.traverse( c => {

    // set the material

  } );

} );

tiles.onModelLoaded = scene => {

  // set the material

};

API : Clean up ThreeTilesRenderer API

  • Each camera should be able to have it's own render target screen size
  • Remove renderer from signature and allow for providing a resolution field
  • Add API to add or remove cameras
  • Add API for updating camera resolution
  • Add caches to constructor signature
  • Add way to iterate over all loaded geometry / preprocess geometry before load so custom materials can be set.

Test performance in Firefox, Safari, Edge

Looks like we should also set the website to scale properly and work with mobile:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

Example: Add fly controls option

Easiest option is to repurpose and constrain OrbitControls to achieve the first person controls.

When fly controls is enabled:

  • Move controls target to be barely in front of the camera
  • Constrain the min and max distance to that distance
  • Repurpose zoom to move the target forward and back
  • Track WASD buttons and move the camera and target in the direction relative to the camera orientation (QE for up down?)
  • When reenabling orbit controls reset the target to the distance it was at when starting fly controls

Traversal: Loaded parent tiles jump when zooming out

There is a set of tiles with SSE above "errorThreshold" that never gets loaded (or gets unloaded) when the camera is zoomed far into the terrain. When zooming back out though those tiles start to meet the screen space error requirements causing it to jump back to a coarse level of detail.

Current Behavior

If a tile meets the SSE requirement load and render it. Once loaded load and render all children. Only render children if they're all ready.

New Behavior

If a tile meets the SSE requirement load and render it if a child tile was not rendered the previous frame. If a child was rendered previously or if this tile is loaded then load and render all children. Only render children if they're all ready.

Various Bugs

1. Raycasting does not work on the root tile.

  • Set depth to 0
  • Enable raycasting

2. Depth value is not consistent

  • Set maxDepth to 1
  • Reported displayed tile depth is 0

Add Draco Compression Support

Should this be baked into the loader? It's a big file to require people to include. Besides how does loading the draco assembly work with general build processes?

Traversal : Improve performance and memory usage of tree traversal

TODO

  • Avoid new keyword in update
  • Reuse functions as much as possible

Places to Improve

Cache: Dispose of ImageBitmap data when possible

ImageBitmap includes a close function which allows for immediately discarding of image data when it's no longer needed. We could at least call this on disposeTile if not sooner. It should not be called earlier than first render, though, because it cannot be closed before upload to GPU. If the renderer is being used in multiple WebGLRenderers then it shouldn't ever be discarded until the tile is disposed.

Multicamera: Allow each camera to have a different resolution

Right now it's expected that all cameras are rendering to the same resolution which is incorrect.

Some options:

Maintain a separate list of resolutions

const tiles = new TilesRenderer( url );
tiles.camera = camera;
tiles.resolution.set( ... );

// or

tiles.cameras = [ camera, camera, camera ];
tiles.resolutions = [ res, res, res ];

Add an API to set it:

const tiles = new TilesRenderer( url );
tiles.addCamera( camera );
tiles.setCameraResolution( camera, vec2 );
tiles.setCameraResolution( camera, renderer );
tiles.removeCamera( camera );

Screen Space Error : Reevaluate ErrorThreshold metric

The way it's being used now:

const errorRequirement = renderer.errorTarget * renderer.errorThreshold;

Means that as errorTarget approaches 0 the requirement to be rendered becomes 0. However the intent is that we allow for more tiles to be rendered as the lower level ones get loaded. Maybe it should be something more like errorTarget + errorThreshold? Or ( errorTarget + 1 ) * errorThreshold?

LRUCache: Use a shared unload function

Right now every tile gets its own function created but you should be able to set it per cache object:

const cache = new LRUCache();
cache.unloadCallback = tile => {
  
  // ...

};

Foreground child tiles are empty for a period

When prioritizing the download and parsing of tiles they're sorted by 1 / depth, meaning that coarser tiles load in first before higher detail tiles. However, when the camera is near the terrain parent tiles can have an apparent error of Infinity because the camera is inside the bounding box meaning the tile won't render (due to the skipTraversal phase) while its children will. However the children will always be deeper than the parent -- this means that distant tiles that are shallower will always be loaded and parsed before these children tiles which are actually closer to the camera leaving a gap in the foreground until the far tiles have resolved.

It would be best if at least these first renderable child tiles could be loaded with some priority so there isn't a gap in the terrain for so long.

  • Sort by 1 / distance_to_furthest_rendered_parent

  • Render with priority of 1 if no parent tiles are to be rendered

The issue is that this means that the priorities will now change based on camera movement and the priority queues will have to be resorted every frame.

Related to #31

Cache: Tune unload percentage

Unloading the LRUCache when tiles are no longer needed can wind up taking between 5 to 10ms even on a more powerful machine to unload the cache, which includes deleting data from maps and disposing of the geometry and materials.

Unload amount

The logic to calculate the amount of nodes to unload seems flawed:

let nodesToUnload = Math.max( itemList.length - targetSize, targetSize ) * unloadPercent;
nodesToUnload = Math.ceil( nodesToUnload );
nodesToUnload = Math.min( unused, nodesToUnload );

Instead we should try to unload a fixed amount every frame until the unused list is emptied:

const excess = itemList.length - targetSize;
let nodesToUnload = Math.min( excess, targetSize * unloadPercent );

In this case maybe it's best to not model the unload amount as a fixed size rather than a percentage? The amount unloaded should also be reduced so we don't unload so many at once. Or maybe it should be a ratio relative to the excess in the cache?

Material key iteration

When unloading textures / materials we iterate over all keys in the material to find textures:

for ( let i = 0, l = materials.length; i < l; i ++ ) {
const material = materials[ i ];
for ( const key in material ) {
const value = material[ key ];
if ( value && value.isTexture ) {
value.dispose();
}
}
material.dispose();
}

This is 64+ keys most of which won't have textures. It might be best to cache the textures
in a separate array on load so we don't have to iterate over so many key unnecessarily on unload.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.