Просмотр исходного кода

Merge branch 'master' into account-for-children-visibility

# Conflicts:
#	src/base/traverseFunctions.js
Garrett Johnson 5 лет назад
Родитель
Сommit
2d59d48287

+ 6 - 7
example/index.js

@@ -182,11 +182,8 @@ function init() {
 		GEOMETRIC_ERROR: 2,
 		DISTANCE: 3,
 		DEPTH: 4,
-		IS_LEAF: 5.
-
-	} ).onChange( function ( v ) {
-
-		tiles.colorMode = parseFloat( v );
+		IS_LEAF: 5,
+		RANDOM_COLOR: 6,
 
 	} );
 	debug.open();
@@ -309,6 +306,8 @@ function updateOrthoCamera() {
 
 function animate() {
 
+	requestAnimationFrame( animate );
+
 	// update options
 	tiles.errorTarget = params.errorTarget;
 	tiles.errorThreshold = params.errorThreshold;
@@ -316,6 +315,8 @@ function animate() {
 	tiles.maxDepth = params.maxDepth;
 	tiles.camera = params.orthographic ? orthoCamera : camera;
 	tiles.displayBoxBounds = params.displayBoxBounds;
+	tiles.colorMode = parseFloat( params.colorMode );
+
 
 	tiles.setResolutionFromRenderer( renderer );
 
@@ -394,8 +395,6 @@ function animate() {
 	render();
 	stats.end();
 
-	requestAnimationFrame( animate );
-
 }
 
 function render() {

+ 127 - 0
package-lock.json

@@ -3378,6 +3378,45 @@
         "typedarray": "^0.0.6"
       }
     },
+    "concurrently": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.1.0.tgz",
+      "integrity": "sha512-9ViZMu3OOCID3rBgU31mjBftro2chOop0G2u1olq1OuwRBVRw/GxHTg80TVJBUTJfoswMmEUeuOg1g1yu1X2dA==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2",
+        "date-fns": "^2.0.1",
+        "lodash": "^4.17.15",
+        "read-pkg": "^4.0.1",
+        "rxjs": "^6.5.2",
+        "spawn-command": "^0.0.2-1",
+        "supports-color": "^6.1.0",
+        "tree-kill": "^1.2.2",
+        "yargs": "^13.3.0"
+      },
+      "dependencies": {
+        "read-pkg": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
+          "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
+          "dev": true,
+          "requires": {
+            "normalize-package-data": "^2.3.2",
+            "parse-json": "^4.0.0",
+            "pify": "^3.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
     "console-browserify": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
@@ -3901,6 +3940,12 @@
         "whatwg-url": "^7.0.0"
       }
     },
+    "date-fns": {
+      "version": "2.11.1",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.11.1.tgz",
+      "integrity": "sha512-3RdUoinZ43URd2MJcquzBbDQo+J87cSzB8NkXdZiN5ia1UNyep0oCyitfiL88+R7clGTeq/RniXAc16gWyAu1w==",
+      "dev": true
+    },
     "date-now": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@@ -4793,6 +4838,12 @@
         "flat-cache": "^2.0.1"
       }
     },
+    "file-size": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/file-size/-/file-size-0.0.5.tgz",
+      "integrity": "sha1-BX1Dw6Ptc12j+Q1gUqs4Dx5tXjs=",
+      "dev": true
+    },
     "filesize": {
       "version": "3.6.1",
       "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@@ -9414,6 +9465,12 @@
       "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
       "dev": true
     },
+    "spawn-command": {
+      "version": "0.0.2-1",
+      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
+      "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
+      "dev": true
+    },
     "spdx-correct": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
@@ -9542,6 +9599,70 @@
         "through2": "~2.0.3"
       }
     },
+    "static-server": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/static-server/-/static-server-2.2.1.tgz",
+      "integrity": "sha512-j5eeW6higxYNmXMIT8iHjsdiViTpQDthg7o+SHsRtqdbxscdHqBHXwrXjHC8hL3F0Tsu34ApUpDkwzMBPBsrLw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^0.5.1",
+        "commander": "^2.3.0",
+        "file-size": "0.0.5",
+        "mime": "^1.2.11",
+        "opn": "^5.2.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+          "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+          "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+          "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^1.1.0",
+            "escape-string-regexp": "^1.0.0",
+            "has-ansi": "^0.1.0",
+            "strip-ansi": "^0.3.0",
+            "supports-color": "^0.2.0"
+          }
+        },
+        "has-ansi": {
+          "version": "0.1.0",
+          "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+          "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^0.2.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+          "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^0.2.1"
+          }
+        },
+        "supports-color": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+          "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
+          "dev": true
+        }
+      }
+    },
     "statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -9925,6 +10046,12 @@
         "punycode": "^2.1.0"
       }
     },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true
+    },
     "tslib": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",

+ 3 - 1
package.json

@@ -16,7 +16,7 @@
   "type": "module",
   "main": "src/index.js",
   "scripts": {
-    "start": "parcel watch example/index.html --out-dir ./example/bundle/ --public-url . --no-cache",
+    "start": "concurrently \"parcel watch example/index.html --out-dir ./example/bundle/ --public-url . --no-cache\" \"static-server\"",
     "lint": "eslint src/**",
     "test": "jest"
   },
@@ -33,12 +33,14 @@
     "@babel/core": "^7.7.2",
     "@babel/preset-env": "^7.7.1",
     "babel-jest": "^24.9.0",
+    "concurrently": "^5.1.0",
     "eslint": "^6.5.1",
     "eslint-config-mdcs": "^4.2.3",
     "eslint-plugin-jest": "^23.8.2",
     "jest": "^24.9.0",
     "jest-cli": "^24.9.0",
     "parcel-bundler": "^1.12.4",
+    "static-server": "^2.2.1",
     "three": ">=0.114.0"
   },
   "peerDependencies": {

+ 0 - 3
src/base/traverseFunctions.js

@@ -249,9 +249,6 @@ export function skipTraversal( tile, renderer ) {
 	// all children have loaded yet, and if no children were visible last frame. We want to keep children visible
 	// that _were_ visible to avoid a pop in level of detail as the camera moves around and parent / sibling tiles
 	// load in.
-
-	// TODO: this condition is skipped over if data hasn't loaded yet meaning that between when this tile is first
-	// used trigger and when it loads the children are iterated over and triggered to load, which is unnecessary
 	if ( meetsSSE && ! allChildrenHaveContent && ! childrenWereVisible ) {
 
 		if ( loadedContent ) {

+ 23 - 2
src/three/DebugTilesRenderer.js

@@ -10,6 +10,7 @@ const GEOMETRIC_ERROR = 2;
 const DISTANCE = 3;
 const DEPTH = 4;
 const IS_LEAF = 5;
+const RANDOM_COLOR = 6;
 
 function emptyRaycast() {}
 
@@ -188,6 +189,12 @@ export class DebugTilesRenderer extends TilesRenderer {
 
 					}
 
+					if ( colorMode !== RANDOM_COLOR ) {
+
+						delete c.material.__randomColor;
+
+					}
+
 					switch ( colorMode ) {
 
 						case DEPTH: {
@@ -240,6 +247,20 @@ export class DebugTilesRenderer extends TilesRenderer {
 							break;
 
 						}
+						case RANDOM_COLOR: {
+
+							if ( ! c.material.__randomColor ) {
+
+								const h = Math.random();
+								const s = 0.5 + Math.random() * 0.5;
+								const l = 0.375 + Math.random() * 0.25;
+								c.material.color.setHSL( h, s, l );
+								c.material.__randomColor = true;
+
+							}
+							break;
+
+						}
 
 					}
 
@@ -301,7 +322,7 @@ export class DebugTilesRenderer extends TilesRenderer {
 
 					cached.boxHelperGroup = boxHelperGroup;
 
-					if ( this.displayBoxBounds ) {
+					if ( this.visibleTiles.has( tile ) && this.displayBoxBounds ) {
 
 						this.boxGroup.add( boxHelperGroup );
 						boxHelperGroup.updateMatrixWorld( true );
@@ -313,7 +334,7 @@ export class DebugTilesRenderer extends TilesRenderer {
 					sphereHelper.raycast = emptyRaycast;
 					cached.sphereHelper = sphereHelper;
 
-					if ( this.displaySphereBounds ) {
+					if ( this.visibleTiles.has( tile ) && this.displaySphereBounds ) {
 
 						this.sphereGroup.add( sphereHelper );
 						sphereHelper.updateMatrixWorld( true );

+ 30 - 17
src/three/TilesRenderer.js

@@ -25,6 +25,7 @@ const vecZ = new Vector3();
 
 const X_AXIS = new Vector3( 1, 0, 0 );
 const Y_AXIS = new Vector3( 0, 1, 0 );
+const useImageBitmap = typeof createImageBitmap !== 'undefined';
 
 function emptyRaycast() {}
 
@@ -314,8 +315,12 @@ export class TilesRenderer extends TilesRendererBase {
 		const loadIndex = tile._loadIndex;
 		const manager = new LoadingManager();
 
-		if ( typeof createImageBitmap !== 'undefined' ) {
+		if ( useImageBitmap ) {
 
+			// 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 ) {
@@ -376,6 +381,7 @@ export class TilesRenderer extends TilesRendererBase {
 
 			const materials = [];
 			const geometry = [];
+			const textures = [];
 			scene.traverse( c => {
 
 				if ( c.geometry ) {
@@ -386,14 +392,27 @@ export class TilesRenderer extends TilesRendererBase {
 
 				if ( c.material ) {
 
+					const material = c.material;
 					materials.push( c.material );
 
+					for ( const key in material ) {
+
+						const value = material[ key ];
+						if ( value && value.isTexture ) {
+
+							textures.push( value );
+
+						}
+
+					}
+
 				}
 
 			} );
 
 			cached.materials = materials;
 			cached.geometry = geometry;
+			cached.textures = textures;
 
 		} );
 
@@ -405,17 +424,9 @@ export class TilesRenderer extends TilesRendererBase {
 		const cached = tile.cached;
 		if ( cached.scene ) {
 
-			// TODO: This should never get called if the scene hasn't been removed from the scene yet, right?
-			// TODO: this can possibly be slow because so many discard can happen at once -- maybe iteratively do it? Or lower the eviction ratio / put a cap
-			// on the amount disposed per frame.
-			const scene = cached.scene;
 			const materials = cached.materials;
 			const geometry = cached.geometry;
-			if ( scene.parent ) {
-
-				scene.parent.remove( scene );
-
-			}
+			const textures = cached.textures;
 
 			for ( let i = 0, l = geometry.length; i < l; i ++ ) {
 
@@ -425,23 +436,25 @@ export class TilesRenderer extends TilesRendererBase {
 
 			for ( let i = 0, l = materials.length; i < l; i ++ ) {
 
-				const material = materials[ i ];
-				for ( const key in material ) {
+				materials[ i ].dispose();
 
-					const value = material[ key ];
-					if ( value && value.isTexture ) {
+			}
 
-						value.dispose();
+			for ( let i = 0, l = textures.length; i < l; i ++ ) {
 
-					}
+				const texture = textures[ i ];
+				texture.dispose();
+				if ( useImageBitmap && 'close' in texture.image ) {
+
+					texture.image.close();
 
 				}
-				material.dispose();
 
 			}
 
 			cached.scene = null;
 			cached.materials = null;
+			cached.textures = null;
 			cached.geometry = null;
 
 		}

+ 12 - 6
src/utilities/LRUCache.js

@@ -1,6 +1,13 @@
 // TODO: can we remove the use of `indexOf` here because it's potentially slow? Possibly use time and sort as needed?
 // Keep a used list that we can sort as needed when it's dirty, a map of item to last used time, and a binary search
 // of the array to find an item that needs to be removed
+
+// Fires at the end of the frame and before the next one
+function enqueueMicrotask( callback ) {
+
+	Promise.resolve().then( callback );
+
+}
 class LRUCache {
 
 	constructor() {
@@ -8,7 +15,7 @@ class LRUCache {
 		// options
 		this.maxSize = 800;
 		this.minSize = 600;
-		this.unloadPercent = 0.2;
+		this.unloadPercent = 0.05;
 
 		this.usedSet = new Set();
 		this.itemSet = new Set();
@@ -109,14 +116,14 @@ class LRUCache {
 		const usedSet = this.usedSet;
 		const callbacks = this.callbacks;
 		const unused = itemList.length - usedSet.size;
+		const excess = itemList.length - targetSize;
 
-		if ( itemList.length > targetSize && unused > 0 ) {
+		if ( excess > 0 && unused > 0 ) {
 
 			// TODO: sort by priority
 
-			let nodesToUnload = Math.max( itemList.length - targetSize, targetSize ) * unloadPercent;
+			let nodesToUnload = Math.min( targetSize * unloadPercent, unused );
 			nodesToUnload = Math.ceil( nodesToUnload );
-			nodesToUnload = Math.min( unused, nodesToUnload );
 
 			const removedItems = itemList.splice( 0, nodesToUnload );
 			for ( let i = 0, l = removedItems.length; i < l; i ++ ) {
@@ -128,7 +135,6 @@ class LRUCache {
 
 			}
 
-
 		}
 
 	}
@@ -138,7 +144,7 @@ class LRUCache {
 		if ( ! this.scheduled ) {
 
 			this.scheduled = true;
-			Promise.resolve().then( () => {
+			enqueueMicrotask( () => {
 
 				this.scheduled = false;
 				this.unloadUnusedContent( prioritySortCb );