Skip to content

Commit

Permalink
Merge branch 'master' of github.com:NASA-AMMOS/3DTilesRendererJS into…
Browse files Browse the repository at this point in the history
… master
  • Loading branch information
gkjohnson committed Nov 11, 2021
2 parents 4be4a8b + 379f04c commit 03bc725
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 52 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- PriorityQueue: Added `schedulingCallback` to afford flexibility in job scheduling callback for scenarios where `requestAnimationFrame` will not work, such as with WebXR.

### Fixed
- `autoDisableRendererCulling` incorrectly applying the inverse of the documented effect.
- Screen space error calculations now use the camera projectionMatrix rather than camera type to determine frustum type.

## [0.3.3] - 2021-09-08
### Added
- Support for embedded tileset / tile geometry URLs with hashes, search query parameters.
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,14 @@ priorityCallback = null : ( itemA, itemB ) => Number

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

### .schedulingCallback

```js
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.

## LRUCache

Utility class for the TilesRenderer to keep track of currently used items so rendered items will not be unloaded.
Expand Down
102 changes: 75 additions & 27 deletions example/vr.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ let box, sphere, grid;
let raycaster, fwdVector, intersectRing;
let offsetParent;
let controller, controllerGrip;
let xrSession = null;
let tasks = [];

let params = {

Expand All @@ -52,7 +54,6 @@ let params = {
};

init();
animate();

function init() {

Expand All @@ -69,6 +70,8 @@ function init() {
document.body.appendChild( renderer.domElement );
renderer.domElement.tabIndex = 1;

renderer.setAnimationLoop( animate );

// create workspace
workspace = new Group();
scene.add( workspace );
Expand Down Expand Up @@ -102,6 +105,23 @@ function init() {
tiles = new TilesRenderer( '../data/tileset.json' );
offsetParent.add( tiles.group );

// We set camera for tileset
tiles.setCamera( camera );
tiles.setResolutionFromRenderer( camera, renderer );


// We define a custom scheduling callback to handle also active WebXR sessions
const tilesSchedulingCB = func => {

tasks.push( func );

};

// We set our scheduling callback for tiles downloading and parsing
tiles.downloadQueue.schedulingCallback = tilesSchedulingCB;
tiles.parseQueue.schedulingCallback = tilesSchedulingCB;


// Raycasting init
raycaster = new Raycaster();
fwdVector = new Vector3( 0, 0, 1 );
Expand Down Expand Up @@ -206,16 +226,62 @@ function onWindowResize() {

}

function animate() {
function handleCamera() {

// get the XR camera with a combined frustum for culling
if ( renderer.xr.isPresenting ) {

if ( xrSession === null ) { // We setup XR camera once

// remove all cameras so we can use the VR camera instead
tiles.cameras.forEach( c => tiles.deleteCamera( camera ) );

const currCamera = renderer.xr.getCamera( camera );
tiles.setCamera( currCamera );

const leftCam = currCamera.cameras[ 0 ];
if ( leftCam ) {

tiles.setResolution( currCamera, leftCam.viewport.z, leftCam.viewport.w );

}

xrSession = renderer.xr.getSession();

}

} else {

// Reset default camera (exiting WebXR session)
if ( xrSession !== null ) {

tiles.cameras.forEach( c => tiles.deleteCamera( camera ) );

renderer.setAnimationLoop( render );
tiles.setCamera( camera );
tiles.setResolutionFromRenderer( camera, renderer );

camera.position.set( 0, 1, 0 );

xrSession = null;

}

}

}

function handleTasks() {

function render() {
for ( let t = 0, l = tasks.length; t < l; t ++ ) {

requestAnimationFrame( animate );
tasks[ t ]();

}
tasks.length = 0;

}

function animate() {

grid.visible = params.displayGrid;

Expand All @@ -236,32 +302,14 @@ function render() {

}

// remove all cameras so we can use the VR camera instead
tiles.cameras.forEach( c => tiles.deleteCamera( camera ) );
// We check for tiles camera setup (default and XR sessions)
handleCamera();

// get the XR camera with a combined frustum for culling
if ( renderer.xr.isPresenting ) {

const currCamera = renderer.xr.getCamera( camera );
tiles.setCamera( currCamera );

const leftCam = currCamera.cameras[ 0 ];
if ( leftCam ) {

tiles.setResolution( currCamera, leftCam.viewport.z, leftCam.viewport.w );

}

} else {

tiles.setCamera( camera );
tiles.setResolutionFromRenderer( camera, renderer );

}
// We handle pending tasks
handleTasks();

tiles.update();


if ( controller.controllerActive ) {

const { ray } = raycaster;
Expand Down
42 changes: 22 additions & 20 deletions src/three/TilesRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import {
Sphere,
Vector3,
Vector2,
Math as MathUtils,
Frustum,
LoadingManager
} from 'three';
import { raycastTraverse, raycastTraverseFirstHit } from './raycastTraverse.js';

const INITIAL_FRUSTUM_CULLED = Symbol( 'INITIAL_FRUSTUM_CULLED' );
const DEG2RAD = MathUtils.DEG2RAD;
const tempMat = new Matrix4();
const tempMat2 = new Matrix4();
const tempVector = new Vector3();
Expand Down Expand Up @@ -51,13 +49,9 @@ export class TilesRenderer extends TilesRendererBase {
if ( this._autoDisableRendererCulling !== value ) {

super._autoDisableRendererCulling = value;
this.traverse( tile => {
this.forEachLoadedModel( ( scene ) => {

if ( tile.scene ) {

updateFrustumCulled( tile.scene, value );

}
updateFrustumCulled( scene, ! value );

} );

Expand Down Expand Up @@ -355,10 +349,11 @@ export class TilesRenderer extends TilesRendererBase {
cameraInfo.push( {

frustum: new Frustum(),
sseDenominator: - 1,
isOrthographic: false,
sseDenominator: - 1, // used if isOrthographic:false
position: new Vector3(),
invScale: - 1,
pixelSize: 0,
pixelSize: 0, // used if isOrthographic:true

} );

Expand Down Expand Up @@ -392,18 +387,26 @@ export class TilesRenderer extends TilesRendererBase {

}

if ( camera.isPerspectiveCamera ) {

info.sseDenominator = 2 * Math.tan( 0.5 * camera.fov * DEG2RAD ) / resolution.height;
// Read the calculated projection matrix directly to support custom Camera implementations
const projection = camera.projectionMatrix.elements;

}
// The last element of the projection matrix is 1 for orthographic, 0 for perspective
info.isOrthographic = projection[ 15 ] === 1;

if ( camera.isOrthographicCamera ) {
if ( info.isOrthographic ) {

const w = camera.right - camera.left;
const h = camera.top - camera.bottom;
// See OrthographicCamera.updateProjectionMatrix and Matrix4.makeOrthographic:
// the view width and height are used to populate matrix elements 0 and 5.
const w = 2 / projection[ 0 ];
const h = 2 / projection[ 5 ];
info.pixelSize = Math.max( h / resolution.height, w / resolution.width );

} else {

// See PerspectiveCamera.updateProjectionMatrix and Matrix4.makePerspective:
// the vertical FOV is used to populate matrix element 5.
info.sseDenominator = ( 2 / projection[ 5 ] ) / resolution.height;

}

info.invScale = invScale;
Expand Down Expand Up @@ -673,7 +676,7 @@ export class TilesRenderer extends TilesRendererBase {
c[ INITIAL_FRUSTUM_CULLED ] = c.frustumCulled;

} );
updateFrustumCulled( scene, this.autoDisableRendererCulling );
updateFrustumCulled( scene, ! this.autoDisableRendererCulling );

cached.scene = scene;

Expand Down Expand Up @@ -842,12 +845,11 @@ export class TilesRenderer extends TilesRendererBase {
}

// transform camera position into local frame of the tile bounding box
const camera = cameras[ i ];
const info = cameraInfo[ i ];
const invScale = info.invScale;

let error;
if ( camera.isOrthographicCamera ) {
if ( info.isOrthographic ) {

const pixelSize = info.pixelSize;
error = tile.geometricError / ( pixelSize * invScale );
Expand Down
2 changes: 2 additions & 0 deletions src/utilities/PriorityQueue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export class PriorityQueue {
maxJobs : Number;
autoUpdate : Boolean;
priorityCallback : ( itemA : any , itemB : any ) => Number;

schedulingCallback : ( func : Function ) => void;

sort() : void;
add( item : any, callback : ( item : any ) => any ) : Promise< any >;
Expand Down
20 changes: 15 additions & 5 deletions src/utilities/PriorityQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ class PriorityQueue {

};

// Customizable scheduling callback. Default using requestAnimationFrame()
this.schedulingCallback = func => {

requestAnimationFrame( func );

};

this._runjobs = () => {

this.tryRunJobs();
this.scheduled = false;

};

}

sort() {
Expand Down Expand Up @@ -110,12 +124,8 @@ class PriorityQueue {

if ( ! this.scheduled ) {

requestAnimationFrame( () => {

this.tryRunJobs();
this.scheduled = false;
this.schedulingCallback( this._runjobs );

} );
this.scheduled = true;

}
Expand Down

0 comments on commit 03bc725

Please sign in to comment.