Selaa lähdekoodia

Merge branch 'master' of github.com:NASA-AMMOS/3DTilesRendererJS into master

# Conflicts:
#	src/index.js
#	src/three/DebugTilesRenderer.js
Garrett Johnson 4 vuotta sitten
vanhempi
commit
cceedd71d7

+ 2 - 4
.github/workflows/examples-build.yml

@@ -25,13 +25,11 @@ jobs:
         node-version: ${{ matrix.node-version }}
         cache: 'npm'
     - run: npm ci
-    - run: npm run build-examples --if-present
+    - run: npm run build-examples
 
     - name: Commit Examples
       uses: EndBug/add-and-commit@v7
       with:
         add: 'example/bundle'
-        branch: examples
         message: 'update builds'
-        push: 'origin examples --force --set-upstream'
-#         pull_strategy: 'NO-PULL'
+        push: 'origin HEAD:examples --force'

+ 7 - 0
CHANGELOG.md

@@ -8,12 +8,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 ### Changed
 - DebugTilesRenderer: "RANDOM_COLOR" debug colors are now consistent between flag toggles.
 - DebugTilesRenderer: "MeshStandardMaterial" is now used instead of "MeshBasicMaterial" for debugging.
+- TilesRenderer: add `getBoundingSphere` function.
 
 ### Added
 - DebugTilesRenderer: "RANDOM_NODE_COLOR" visualization setting.
 - Names for various tile objects.
 - DebugTilesRenderer: Added `getDebugColor` function for adjusing the debug visualization colors.
 - Support for computing screen space error for tiles that had sphere bounds but no box bounds.
+- Support for embedded tileset / tile geometry URLs with hashes, search query parameters.
+- DebugTilesRenderer: Added `customColorCallback` and `CUSTOM_COLOR` mode for custom debug coloring.
+
+### Fixed
+- I3DMLoader: Fixed embedded absolute URLs not working correctly.
+- TilesRenderer: "getBounds" function throwing an error if no bounding box is present on the tileset.
 
 ## [0.3.1] - 2021-07-28
 ### Fixed

+ 10 - 0
README.md

@@ -532,7 +532,17 @@ 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_MODE
 ```
+### .customColorCallback
+
+```js
+customColorCallback: (tile: Tile, child: Object3D) => void
+```
+
+The callback used if `debugColor` is set to `CUSTOM_COLOR_MODE`. Value defaults to `null` and must be set explicitly.
 
 ### .displayBoxBounds
 

+ 8 - 1
example/customMaterial.js

@@ -12,13 +12,14 @@ import {
 	ShaderMaterial,
 	MeshStandardMaterial,
 	PCFSoftShadowMap,
+	Sphere,
 } from 'three';
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
 import * as dat from 'three/examples/jsm/libs/dat.gui.module.js';
 import Stats from 'three/examples/jsm/libs/stats.module.js';
 
 let camera, controls, scene, renderer, tiles, orthoCamera;
-let offsetParent, box, dirLight, statsContainer;
+let offsetParent, box, sphere, dirLight, statsContainer;
 let stats;
 
 const DEFAULT = 0;
@@ -258,6 +259,7 @@ function init() {
 	scene.add( ambLight );
 
 	box = new Box3();
+	sphere = new Sphere();
 
 	offsetParent = new Group();
 	scene.add( offsetParent );
@@ -359,6 +361,11 @@ function animate() {
 		box.getCenter( tiles.group.position );
 		tiles.group.position.multiplyScalar( - 1 );
 
+	} else if ( tiles.getBoundingSphere( sphere ) ) {
+
+		tiles.group.position.copy( sphere.center );
+		tiles.group.position.multiplyScalar( - 1 );
+
 	}
 
 	// update tiles

+ 27 - 1
example/index.js

@@ -9,6 +9,7 @@ import {
 	IS_LEAF,
 	RANDOM_COLOR,
 	RANDOM_NODE_COLOR,
+	CUSTOM_COLOR_MODE
 } from '../src/index.js';
 import {
 	Scene,
@@ -27,6 +28,7 @@ import {
 	TorusBufferGeometry,
 	OrthographicCamera,
 	sRGBEncoding,
+	Sphere,
 } from 'three';
 import { FlyOrbitControls } from './FlyOrbitControls.js';
 import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
@@ -43,7 +45,7 @@ let camera, controls, scene, renderer, tiles, cameraHelper;
 let thirdPersonCamera, thirdPersonRenderer, thirdPersonControls;
 let secondRenderer, secondCameraHelper, secondControls, secondCamera;
 let orthoCamera, orthoCameraHelper;
-let box;
+let box, sphere;
 let raycaster, mouse, rayIntersect, lastHoveredElement;
 let offsetParent;
 let statsContainer, stats;
@@ -102,6 +104,23 @@ function reinstantiateTiles() {
 	tiles.manager.addHandler( /\.gltf$/, loader );
 	offsetParent.add( tiles.group );
 
+	// Used with CUSTOM_COLOR_MODE
+	tiles.customColorCallback = ( tile, object ) => {
+
+		const depthIsEven = tile.__depth % 2 === 0;
+		const hex = depthIsEven ? 0xff0000 : 0xffffff;
+		object.traverse( c => {
+
+			if ( c.isMesh ) {
+
+				c.material.color.set( hex );
+
+			}
+
+		} );
+
+	};
+
 }
 
 function init() {
@@ -190,6 +209,7 @@ function init() {
 	scene.add( ambLight );
 
 	box = new Box3();
+	sphere = new Sphere();
 
 	offsetParent = new Group();
 	scene.add( offsetParent );
@@ -253,6 +273,7 @@ function init() {
 		IS_LEAF,
 		RANDOM_COLOR,
 		RANDOM_NODE_COLOR,
+		CUSTOM_COLOR_MODE
 
 	} );
 	debug.open();
@@ -497,6 +518,11 @@ function animate() {
 		box.getCenter( tiles.group.position );
 		tiles.group.position.multiplyScalar( - 1 );
 
+	} else if ( tiles.getBoundingSphere( sphere ) ) {
+
+		tiles.group.position.copy( sphere.center );
+		tiles.group.position.multiplyScalar( - 1 );
+
 	}
 
 	if ( parseFloat( params.raycast ) !== NONE && lastHoveredElement !== null ) {

+ 17 - 3
example/ionExample.js

@@ -29,6 +29,7 @@ import {
 	sRGBEncoding,
 	Matrix4,
 	Box3,
+	Sphere,
 } from 'three';
 import { FlyOrbitControls } from './FlyOrbitControls.js';
 import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
@@ -176,10 +177,23 @@ function reinstantiateTiles() {
 				tiles.onLoadTileSet = () => {
 
 					const box = new Box3();
+					const sphere = new Sphere();
 					const matrix = new Matrix4();
-					tiles.getOrientedBounds( box, matrix );
-					const position = new Vector3().setFromMatrixPosition( matrix );
-					const distanceToEllipsoidCenter = position.length();
+
+					let position;
+					let distanceToEllipsoidCenter;
+
+					if ( tiles.getOrientedBounds( box, matrix ) ) {
+
+						position = new Vector3().setFromMatrixPosition( matrix );
+						distanceToEllipsoidCenter = position.length();
+
+					} else if ( tiles.getBoundingSphere( sphere ) ) {
+
+						position = sphere.center.clone();
+						distanceToEllipsoidCenter = position.length();
+
+					}
 
 					const surfaceDirection = position.normalize();
 					const up = new Vector3( 0, 1, 0 );

+ 8 - 1
example/offscreenShadows.js

@@ -11,13 +11,14 @@ import {
 	Group,
 	MeshStandardMaterial,
 	PCFSoftShadowMap,
+	Sphere,
 } from 'three';
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
 import * as dat from 'three/examples/jsm/libs/dat.gui.module.js';
 import Stats from 'three/examples/jsm/libs/stats.module.js';
 
 let camera, controls, scene, renderer, tiles, orthoCamera;
-let offsetParent, box, dirLight;
+let offsetParent, box, sphere, dirLight;
 let stats;
 
 const NONE = 0;
@@ -110,6 +111,7 @@ function init() {
 	scene.add( ambLight );
 
 	box = new Box3();
+	sphere = new Sphere();
 
 	offsetParent = new Group();
 	scene.add( offsetParent );
@@ -224,6 +226,11 @@ function animate() {
 		box.getCenter( tiles.group.position );
 		tiles.group.position.multiplyScalar( - 1 );
 
+	} else if ( tiles.getBoundingSphere( sphere ) ) {
+
+		tiles.group.position.copy( sphere.center );
+		tiles.group.position.multiplyScalar( - 1 );
+
 	}
 
 	// update tiles

+ 8 - 1
example/vr.js

@@ -30,6 +30,7 @@ import {
 	Line,
 	Vector3,
 	RingBufferGeometry,
+	Sphere,
 } from 'three';
 import * as dat from 'three/examples/jsm/libs/dat.gui.module.js';
 import { VRButton } from 'three/examples/jsm/webxr/VRButton.js';
@@ -37,7 +38,7 @@ import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerM
 
 let camera, scene, renderer, tiles;
 let workspace;
-let box, grid;
+let box, sphere, grid;
 let raycaster, fwdVector, intersectRing;
 let offsetParent;
 let controller, controllerGrip;
@@ -92,6 +93,7 @@ function init() {
 
 	// tile set
 	box = new Box3();
+	sphere = new Sphere();
 
 	// parent for centering the tileset
 	offsetParent = new Group();
@@ -227,6 +229,11 @@ function render() {
 		box.getCenter( tiles.group.position );
 		tiles.group.position.multiplyScalar( - 1 );
 
+	} else if ( tiles.getBoundingSphere( sphere ) ) {
+
+		tiles.group.position.copy( sphere.center );
+		tiles.group.position.multiplyScalar( - 1 );
+
 	}
 
 	// remove all cameras so we can use the VR camera instead

+ 12 - 7
src/base/TilesRendererBase.js

@@ -14,26 +14,31 @@ import { UNLOADED, LOADING, PARSING, LOADED, FAILED } from './constants.js';
  */
 const priorityCallback = ( a, b ) => {
 
-	if ( a.__inFrustum !== b.__inFrustum ) {
+	if ( a.__depth !== b.__depth ) {
 
-		// prioritize loading whatever is in the frame
+		// load shallower tiles first
+		return a.__depth > b.__depth ? - 1 : 1;
+
+	} else if ( a.__inFrustum !== b.__inFrustum ) {
+
+		// load tiles that are in the frustum at the current depth
 		return a.__inFrustum ? 1 : - 1;
 
 	} else if ( a.__used !== b.__used ) {
 
-		// prioritize tiles that were used most recently
+		// load tiles that have been used
 		return a.__used ? 1 : - 1;
 
-	} if ( a.__error !== b.__error ) {
+	} else if ( a.__error !== b.__error ) {
 
-		// tiles which have greater error next
-		return a.__error - b.__error;
+		// load the tile with the higher error
+		return a.__error > b.__error ? 1 : - 1;
 
 	} else if ( a.__distanceFromCamera !== b.__distanceFromCamera ) {
 
 		// and finally visible tiles which have equal error (ex: if geometricError === 0)
 		// should prioritize based on distance.
-		return a.__distanceFromCamera - b.__distanceFromCamera;
+		return a.__distanceFromCamera > b.__distanceFromCamera ? - 1 : 1;
 
 	}
 

+ 2 - 0
src/index.js

@@ -9,6 +9,7 @@ import {
 	IS_LEAF,
 	RANDOM_COLOR,
 	RANDOM_NODE_COLOR,
+	CUSTOM_COLOR_MODE,
 } from './three/DebugTilesRenderer.js';
 import { TilesRenderer } from './three/TilesRenderer.js';
 import { B3DMLoader } from './three/B3DMLoader.js';
@@ -51,4 +52,5 @@ export {
 	IS_LEAF,
 	RANDOM_COLOR,
 	RANDOM_NODE_COLOR,
+	CUSTOM_COLOR_MODE,
 };

+ 77 - 32
src/three/DebugTilesRenderer.js

@@ -18,6 +18,7 @@ export const RELATIVE_DEPTH = 5;
 export const IS_LEAF = 6;
 export const RANDOM_COLOR = 7;
 export const RANDOM_NODE_COLOR = 8;
+export const CUSTOM_COLOR_MODE = 9;
 
 export class DebugTilesRenderer extends TilesRenderer {
 
@@ -37,6 +38,7 @@ export class DebugTilesRenderer extends TilesRenderer {
 		this.displayBoxBounds = false;
 		this.displaySphereBounds = false;
 		this.colorMode = NONE;
+		this.customColorCallback = null;
 		this.boxGroup = boxGroup;
 		this.sphereGroup = sphereGroup;
 		this.maxDebugDepth = - 1;
@@ -351,6 +353,20 @@ export class DebugTilesRenderer extends TilesRenderer {
 							break;
 
 						}
+						case CUSTOM_COLOR_MODE: {
+
+							if ( this.customColorCallback ) {
+
+								this.customColorCallback( tile, c );
+
+							} else {
+
+								console.warn( 'DebugTilesRenderer: customColorCallback not defined' );
+
+							}
+							break;
+
+						}
 
 					}
 
@@ -374,16 +390,33 @@ export class DebugTilesRenderer extends TilesRenderer {
 
 		if ( ! visible ) {
 
-			boxGroup.remove( boxHelperGroup );
-			sphereGroup.remove( sphereHelper );
+			if ( boxHelperGroup ) {
+
+				boxGroup.remove( boxHelperGroup );
+
+			}
+
+			if ( sphereHelper ) {
+
+				sphereGroup.remove( sphereHelper );
+
+			}
 
 		} else {
 
-			boxGroup.add( boxHelperGroup );
-			boxHelperGroup.updateMatrixWorld( true );
+			if ( boxHelperGroup ) {
+
+				boxGroup.add( boxHelperGroup );
+				boxHelperGroup.updateMatrixWorld( true );
 
-			sphereGroup.add( sphereHelper );
-			sphereHelper.updateMatrixWorld( true );
+			}
+
+			if ( sphereHelper ) {
+
+				sphereGroup.add( sphereHelper );
+				sphereHelper.updateMatrixWorld( true );
+
+			}
 
 		}
 
@@ -399,40 +432,48 @@ export class DebugTilesRenderer extends TilesRenderer {
 				const scene = cached.scene;
 				if ( scene ) {
 
-					const cachedBox = cached.box;
-					const cachedBoxMat = cached.boxTransform;
+					if ( cached.box && cached.boxTransform ) {
 
-					// Create debug bounding box
-					// In some cases the bounding box may have a scale of 0 in one dimension resulting
-					// in the NaNs in an extracted rotation so we disable matrix updates instead.
-					const boxHelperGroup = new Group();
-					boxHelperGroup.name = 'DebugTilesRenderer.boxHelperGroup';
-					boxHelperGroup.matrix.copy( cachedBoxMat );
-					boxHelperGroup.matrixAutoUpdate = false;
+						const cachedBox = cached.box;
+						const cachedBoxMat = cached.boxTransform;
 
-					const boxHelper = new Box3Helper( cachedBox, getIndexedRandomColor( tile.__depth ) );
-					boxHelper.raycast = emptyRaycast;
-					boxHelperGroup.add( boxHelper );
+						// Create debug bounding box
+						// In some cases the bounding box may have a scale of 0 in one dimension resulting
+						// in the NaNs in an extracted rotation so we disable matrix updates instead.
+						const boxHelperGroup = new Group();
+						boxHelperGroup.name = 'DebugTilesRenderer.boxHelperGroup';
+						boxHelperGroup.matrix.copy( cachedBoxMat );
+						boxHelperGroup.matrixAutoUpdate = false;
 
-					cached.boxHelperGroup = boxHelperGroup;
+						const boxHelper = new Box3Helper( cachedBox, getIndexedRandomColor( tile.__depth ) );
+						boxHelper.raycast = emptyRaycast;
+						boxHelperGroup.add( boxHelper );
 
-					if ( this.visibleTiles.has( tile ) && this.displayBoxBounds ) {
+						cached.boxHelperGroup = boxHelperGroup;
 
-						this.boxGroup.add( boxHelperGroup );
-						boxHelperGroup.updateMatrixWorld( true );
+						if ( this.visibleTiles.has( tile ) && this.displayBoxBounds ) {
+
+							this.boxGroup.add( boxHelperGroup );
+							boxHelperGroup.updateMatrixWorld( true );
+
+						}
 
 					}
 
-					// Create debugbounding sphere
-					const cachedSphere = cached.sphere;
-					const sphereHelper = new SphereHelper( cachedSphere );
-					sphereHelper.raycast = emptyRaycast;
-					cached.sphereHelper = sphereHelper;
+					if ( cached.sphere ) {
+
+						// Create debugbounding sphere
+						const cachedSphere = cached.sphere;
+						const sphereHelper = new SphereHelper( cachedSphere );
+						sphereHelper.raycast = emptyRaycast;
+						cached.sphereHelper = sphereHelper;
 
-					if ( this.visibleTiles.has( tile ) && this.displaySphereBounds ) {
+						if ( this.visibleTiles.has( tile ) && this.displaySphereBounds ) {
 
-						this.sphereGroup.add( sphereHelper );
-						sphereHelper.updateMatrixWorld( true );
+							this.sphereGroup.add( sphereHelper );
+							sphereHelper.updateMatrixWorld( true );
+
+						}
 
 					}
 
@@ -462,9 +503,13 @@ export class DebugTilesRenderer extends TilesRenderer {
 		if ( cached.boxHelperGroup ) {
 
 			cached.boxHelperGroup.children[ 0 ].geometry.dispose();
-			cached.sphereHelper.geometry.dispose();
-
 			delete cached.boxHelperGroup;
+
+		}
+
+		if ( cached.sphereHelper ) {
+
+			cached.sphereHelper.geometry.dispose();
 			delete cached.sphereHelper;
 
 		}

+ 28 - 2
src/three/TilesRenderer.js

@@ -152,7 +152,7 @@ export class TilesRenderer extends TilesRendererBase {
 		const boundingBox = cached.box;
 		const obbMat = cached.boxTransform;
 
-		if ( box ) {
+		if ( boundingBox ) {
 
 			box.copy( boundingBox );
 			matrix.copy( obbMat );
@@ -167,6 +167,29 @@ export class TilesRenderer extends TilesRendererBase {
 
 	}
 
+	getBoundingSphere( sphere ) {
+
+		if ( ! this.root ) {
+
+			return false;
+
+		}
+
+		const boundingSphere = this.root.cached.sphere;
+
+		if ( boundingSphere ) {
+
+			sphere.copy( boundingSphere );
+			return true;
+
+		} else {
+
+			return false;
+
+		}
+
+	}
+
 	forEachLoadedModel( callback ) {
 
 		this.traverse( tile => {
@@ -842,7 +865,10 @@ export class TilesRenderer extends TilesRendererBase {
 					} else {
 
 						tempVector.applyMatrix4( transformInverse );
-						distance = boundingSphere.distanceToPoint( tempVector );
+						// Sphere#distanceToPoint is negative inside the sphere, whereas Box3#distanceToPoint is
+						// zero inside the box. Clipping the distance to a minimum of zero ensures that both
+						// types of bounding volume behave the same way.
+						distance = Math.max( boundingSphere.distanceToPoint( tempVector ), 0 );
 
 					}