@needle-tools/gltf-progressive 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +14 -3
- package/examples/react-three-fiber/src/App.tsx +1 -1
- package/examples/threejs/main.js +1 -1
- package/gltf-progressive.js +877 -740
- package/gltf-progressive.min.js +7 -7
- package/gltf-progressive.umd.cjs +7 -7
- package/lib/extension.d.ts +4 -1
- package/lib/extension.js +64 -10
- package/lib/index.d.ts +3 -17
- package/lib/index.js +29 -3
- package/lib/loaders.d.ts +8 -0
- package/lib/loaders.js +33 -22
- package/lib/lods.manager.js +20 -11
- package/lib/utils.internal.d.ts +4 -5
- package/lib/version.js +1 -1
- package/lib/worker/loader.mainthread.d.ts +45 -0
- package/lib/worker/loader.mainthread.js +193 -0
- package/lib/worker/loader.worker.js +165 -0
- package/package.json +2 -2
package/lib/extension.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { BufferGeometry, Mesh, Texture, TextureLoader } from "three";
|
|
2
2
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
3
|
import { addDracoAndKTX2Loaders } from "./loaders.js";
|
|
4
|
-
import { PromiseQueue, resolveUrl } from "./utils.internal.js";
|
|
4
|
+
import { getParam, PromiseQueue, resolveUrl } from "./utils.internal.js";
|
|
5
5
|
import { getRaycastMesh, registerRaycastMesh } from "./utils.js";
|
|
6
6
|
// All of this has to be removed
|
|
7
7
|
// import { getRaycastMesh, setRaycastMesh } from "../../engine_physics.js";
|
|
8
8
|
// import { PromiseAllWithErrors, resolveUrl } from "../../engine_utils.js";
|
|
9
9
|
import { plugins } from "./plugins/plugin.js";
|
|
10
10
|
import { debug } from "./lods.debug.js";
|
|
11
|
-
|
|
11
|
+
import { getWorker } from "./worker/loader.mainthread.js";
|
|
12
|
+
const useWorker = getParam("gltf-progressive-worker");
|
|
13
|
+
const reduceMipmaps = getParam("gltf-progressive-reduce-mipmaps");
|
|
12
14
|
const $progressiveTextureExtension = Symbol("needle-progressive-texture");
|
|
15
|
+
export const EXTENSION_NAME = "NEEDLE_progressive";
|
|
13
16
|
/**
|
|
14
17
|
* The NEEDLE_progressive extension for the GLTFLoader is responsible for loading progressive LODs for meshes and textures.
|
|
15
18
|
* This extension can be used to load different resolutions of a mesh or texture at runtime (e.g. for LODs or progressive textures).
|
|
@@ -83,6 +86,10 @@ export class NEEDLE_progressive {
|
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
}
|
|
89
|
+
else {
|
|
90
|
+
if (debug)
|
|
91
|
+
console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${material.type}`);
|
|
92
|
+
}
|
|
86
93
|
material[cacheKey] = minmax;
|
|
87
94
|
return minmax;
|
|
88
95
|
function processTexture(tex, minmax) {
|
|
@@ -310,7 +317,7 @@ export class NEEDLE_progressive {
|
|
|
310
317
|
return NEEDLE_progressive.getOrLoadLOD(current, level).then(tex => {
|
|
311
318
|
// this can currently not happen
|
|
312
319
|
if (Array.isArray(tex)) {
|
|
313
|
-
console.warn("Progressive: Got an array of textures for a texture slot, this should not happen"
|
|
320
|
+
console.warn("Progressive: Got an array of textures for a texture slot, this should not happen...");
|
|
314
321
|
return null;
|
|
315
322
|
}
|
|
316
323
|
if (tex?.isTexture === true) {
|
|
@@ -328,6 +335,15 @@ export class NEEDLE_progressive {
|
|
|
328
335
|
}
|
|
329
336
|
// assigned.dispose();
|
|
330
337
|
}
|
|
338
|
+
// Since we're switching LOD level for the texture based on distance we can avoid uploading all the mipmaps
|
|
339
|
+
if (reduceMipmaps && tex.mipmaps) {
|
|
340
|
+
const prevCount = tex.mipmaps.length;
|
|
341
|
+
tex.mipmaps.length = Math.min(tex.mipmaps.length, 3);
|
|
342
|
+
if (prevCount !== tex.mipmaps.length) {
|
|
343
|
+
if (debug)
|
|
344
|
+
console.debug(`Reduced mipmap count from ${prevCount} to ${tex.mipmaps.length} for ${tex.uuid}: ${tex.image?.width}x${tex.image?.height}.`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
331
347
|
material[slot] = tex;
|
|
332
348
|
}
|
|
333
349
|
// check if the old texture is still used by other objects
|
|
@@ -355,7 +371,8 @@ export class NEEDLE_progressive {
|
|
|
355
371
|
}
|
|
356
372
|
parser;
|
|
357
373
|
url;
|
|
358
|
-
constructor(parser
|
|
374
|
+
constructor(parser) {
|
|
375
|
+
const url = parser.options.path;
|
|
359
376
|
if (debug)
|
|
360
377
|
console.log("Progressive extension registered for", url);
|
|
361
378
|
this.parser = parser;
|
|
@@ -510,6 +527,8 @@ export class NEEDLE_progressive {
|
|
|
510
527
|
static previouslyLoaded = new Map();
|
|
511
528
|
/** this contains the geometry/textures that were originally loaded */
|
|
512
529
|
static lowresCache = new Map();
|
|
530
|
+
static workers = [];
|
|
531
|
+
static _workersIndex = 0;
|
|
513
532
|
static async getOrLoadLOD(current, level) {
|
|
514
533
|
const debugverbose = debug == "verbose";
|
|
515
534
|
/** this key is used to lookup the LOD information */
|
|
@@ -521,8 +540,9 @@ export class NEEDLE_progressive {
|
|
|
521
540
|
}
|
|
522
541
|
const LODKEY = LOD?.key;
|
|
523
542
|
let lodInfo;
|
|
543
|
+
const isTextureRequest = current.isTexture === true;
|
|
524
544
|
// See https://github.com/needle-tools/needle-engine-support/issues/133
|
|
525
|
-
if (
|
|
545
|
+
if (isTextureRequest) {
|
|
526
546
|
const tex = current;
|
|
527
547
|
if (tex.source && tex.source[$progressiveTextureExtension])
|
|
528
548
|
lodInfo = tex.source[$progressiveTextureExtension];
|
|
@@ -564,6 +584,7 @@ export class NEEDLE_progressive {
|
|
|
564
584
|
}
|
|
565
585
|
// check if the requested file has already been loaded
|
|
566
586
|
const KEY = lod_url + "_" + lodInfo.guid;
|
|
587
|
+
const slot = await this.queue.slot(lod_url);
|
|
567
588
|
// check if the requested file is currently being loaded
|
|
568
589
|
const existing = this.previouslyLoaded.get(KEY);
|
|
569
590
|
if (existing !== undefined) {
|
|
@@ -602,7 +623,7 @@ export class NEEDLE_progressive {
|
|
|
602
623
|
return res;
|
|
603
624
|
}
|
|
604
625
|
}
|
|
605
|
-
|
|
626
|
+
// #region loading
|
|
606
627
|
if (!slot.use) {
|
|
607
628
|
if (debug)
|
|
608
629
|
console.log(`LOD ${level} was aborted: ${lod_url}`);
|
|
@@ -610,6 +631,39 @@ export class NEEDLE_progressive {
|
|
|
610
631
|
}
|
|
611
632
|
const ext = lodInfo;
|
|
612
633
|
const request = new Promise(async (resolve, _) => {
|
|
634
|
+
// const useWorker = true;
|
|
635
|
+
if (useWorker) {
|
|
636
|
+
const worker = await getWorker({});
|
|
637
|
+
const res = await worker.load(lod_url);
|
|
638
|
+
if (res.textures.length > 0) {
|
|
639
|
+
// const textures = new Array<Texture>();
|
|
640
|
+
for (const entry of res.textures) {
|
|
641
|
+
let texture = entry.texture;
|
|
642
|
+
NEEDLE_progressive.assignLODInformation(LOD.url, texture, LODKEY, level, undefined);
|
|
643
|
+
if (current instanceof Texture) {
|
|
644
|
+
texture = this.copySettings(current, texture);
|
|
645
|
+
}
|
|
646
|
+
if (texture)
|
|
647
|
+
texture.guid = ext.guid;
|
|
648
|
+
// textures.push(texture);
|
|
649
|
+
return resolve(texture);
|
|
650
|
+
}
|
|
651
|
+
// if (textures.length > 0) {
|
|
652
|
+
// return resolve(textures);
|
|
653
|
+
// }
|
|
654
|
+
}
|
|
655
|
+
if (res.geometries.length > 0) {
|
|
656
|
+
const geometries = new Array();
|
|
657
|
+
for (const entry of res.geometries) {
|
|
658
|
+
const newGeo = entry.geometry;
|
|
659
|
+
NEEDLE_progressive.assignLODInformation(LOD.url, newGeo, LODKEY, level, entry.primitiveIndex);
|
|
660
|
+
geometries.push(newGeo);
|
|
661
|
+
}
|
|
662
|
+
return resolve(geometries);
|
|
663
|
+
}
|
|
664
|
+
return resolve(null);
|
|
665
|
+
}
|
|
666
|
+
// Old loading
|
|
613
667
|
const loader = new GLTFLoader();
|
|
614
668
|
addDracoAndKTX2Loaders(loader);
|
|
615
669
|
if (debug) {
|
|
@@ -686,7 +740,6 @@ export class NEEDLE_progressive {
|
|
|
686
740
|
}
|
|
687
741
|
if (found) {
|
|
688
742
|
const mesh = await parser.getDependency("mesh", index);
|
|
689
|
-
const meshExt = ext;
|
|
690
743
|
if (debugverbose)
|
|
691
744
|
console.log(`Loaded Mesh \"${mesh.name}\"`, lod_url, index, mesh, KEY);
|
|
692
745
|
if (mesh.isMesh === true) {
|
|
@@ -745,7 +798,8 @@ export class NEEDLE_progressive {
|
|
|
745
798
|
}
|
|
746
799
|
return null;
|
|
747
800
|
}
|
|
748
|
-
static
|
|
801
|
+
static maxConcurrent = 50;
|
|
802
|
+
static queue = new PromiseQueue(NEEDLE_progressive.maxConcurrent, { debug: debug != false });
|
|
749
803
|
static assignLODInformation(url, res, key, level, index) {
|
|
750
804
|
if (!res)
|
|
751
805
|
return;
|
|
@@ -782,8 +836,8 @@ export class NEEDLE_progressive {
|
|
|
782
836
|
// This should only happen once ever for every texture
|
|
783
837
|
// const original = target;
|
|
784
838
|
{
|
|
785
|
-
if (debug)
|
|
786
|
-
console.
|
|
839
|
+
if (debug === "verbose")
|
|
840
|
+
console.debug("Copy texture settings\n", source.uuid, "\n", target.uuid);
|
|
787
841
|
target = target.clone();
|
|
788
842
|
}
|
|
789
843
|
// else {
|
package/lib/index.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export { getRaycastMesh, registerRaycastMesh, useRaycastMeshes } from "./utils.j
|
|
|
7
7
|
import { WebGLRenderer } from "three";
|
|
8
8
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
9
9
|
import { SmartLoadingHints } from "./loaders.js";
|
|
10
|
-
import { LODsManager } from "./lods.manager.js";
|
|
11
10
|
declare type UseNeedleGLTFProgressiveOptions = {
|
|
12
11
|
/**
|
|
13
12
|
* When set to true the LODs manager will automatically be enabled
|
|
@@ -18,19 +17,6 @@ declare type UseNeedleGLTFProgressiveOptions = {
|
|
|
18
17
|
*/
|
|
19
18
|
hints?: Omit<SmartLoadingHints, "progressive">;
|
|
20
19
|
};
|
|
21
|
-
/** Use this
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* @param loader The gltf loader.
|
|
25
|
-
* @param opts Options.
|
|
26
|
-
* @returns The LODsManager instance.
|
|
27
|
-
* @example In react-three-fiber:
|
|
28
|
-
* ```ts
|
|
29
|
-
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
30
|
-
* const { scene } = useGLTF(url, false, false, (loader) => {
|
|
31
|
-
* useNeedleGLTFProgressive(url, gl, loader)
|
|
32
|
-
* })
|
|
33
|
-
* return <primitive object={scene} />
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export declare function useNeedleProgressive(url: string, renderer: WebGLRenderer, loader: GLTFLoader, opts?: UseNeedleGLTFProgressiveOptions): LODsManager;
|
|
20
|
+
/** @deprecated Use `useNeedleProgressive(loader, renderer)` - this method will be removed in gltf-progressive 4 */
|
|
21
|
+
export declare function useNeedleProgressive(url: string, renderer: WebGLRenderer, loader: GLTFLoader, opts?: UseNeedleGLTFProgressiveOptions): any;
|
|
22
|
+
export declare function useNeedleProgressive(loader: GLTFLoader, renderer: WebGLRenderer, opts?: UseNeedleGLTFProgressiveOptions): any;
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,15 @@ import { LODsManager } from "./lods.manager.js";
|
|
|
13
13
|
* @param loader The gltf loader.
|
|
14
14
|
* @param opts Options.
|
|
15
15
|
* @returns The LODsManager instance.
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
|
+
* @example Usage with vanilla three.js:
|
|
18
|
+
* ```ts
|
|
19
|
+
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
20
|
+
* const loader = new GLTFLoader()
|
|
21
|
+
* useNeedleProgressive(url, renderer, loader)
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Usage with react-three-fiber:
|
|
17
25
|
* ```ts
|
|
18
26
|
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
19
27
|
* const { scene } = useGLTF(url, false, false, (loader) => {
|
|
@@ -22,14 +30,32 @@ import { LODsManager } from "./lods.manager.js";
|
|
|
22
30
|
* return <primitive object={scene} />
|
|
23
31
|
* ```
|
|
24
32
|
*/
|
|
25
|
-
export function useNeedleProgressive(
|
|
33
|
+
export function useNeedleProgressive(...args) {
|
|
34
|
+
let url;
|
|
35
|
+
let renderer;
|
|
36
|
+
let loader;
|
|
37
|
+
let opts;
|
|
38
|
+
switch (args.length) {
|
|
39
|
+
case 2:
|
|
40
|
+
[loader, renderer] = args;
|
|
41
|
+
opts = {};
|
|
42
|
+
break;
|
|
43
|
+
case 3:
|
|
44
|
+
[loader, renderer, opts] = args;
|
|
45
|
+
break;
|
|
46
|
+
case 4: // legacy
|
|
47
|
+
[url, renderer, loader, opts] = args;
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error("Invalid arguments");
|
|
51
|
+
}
|
|
26
52
|
createLoaders(renderer);
|
|
27
53
|
addDracoAndKTX2Loaders(loader);
|
|
28
54
|
configureLoader(loader, {
|
|
29
55
|
progressive: true,
|
|
30
56
|
...opts?.hints,
|
|
31
57
|
});
|
|
32
|
-
loader.register(p => new NEEDLE_progressive(p
|
|
58
|
+
loader.register(p => new NEEDLE_progressive(p));
|
|
33
59
|
const lod = LODsManager.get(renderer);
|
|
34
60
|
if (opts?.enableLODsManager !== false) {
|
|
35
61
|
lod.enable();
|
package/lib/loaders.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import { WebGLRenderer } from 'three';
|
|
|
2
2
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
3
3
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
4
4
|
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
5
|
+
/**
|
|
6
|
+
* Return the current loader configuration.
|
|
7
|
+
* These paths can be changed using `setDracoDecoderLocation` and `setKTX2TranscoderLocation`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const GET_LOADER_LOCATION_CONFIG: () => {
|
|
10
|
+
dracoDecoderPath: string;
|
|
11
|
+
ktx2TranscoderPath: string;
|
|
12
|
+
};
|
|
5
13
|
/**
|
|
6
14
|
* Set the location of the Draco decoder. If a draco loader has already been created, it will be updated.
|
|
7
15
|
* @default 'https://www.gstatic.com/draco/versioned/decoders/1.5.7/'
|
package/lib/loaders.js
CHANGED
|
@@ -6,6 +6,7 @@ let DEFAULT_DRACO_DECODER_LOCATION = 'https://www.gstatic.com/draco/versioned/de
|
|
|
6
6
|
let DEFAULT_KTX2_TRANSCODER_LOCATION = 'https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/';
|
|
7
7
|
const defaultDraco = DEFAULT_DRACO_DECODER_LOCATION;
|
|
8
8
|
const defaultKTX2 = DEFAULT_KTX2_TRANSCODER_LOCATION;
|
|
9
|
+
// #region Online check
|
|
9
10
|
const _remoteDracoDecoderUrl = new URL(DEFAULT_DRACO_DECODER_LOCATION + "draco_decoder.js");
|
|
10
11
|
// if (typeof window !== "undefined") {
|
|
11
12
|
// if (!window.navigator.onLine) {
|
|
@@ -40,6 +41,15 @@ fetch(_remoteDracoDecoderUrl, {
|
|
|
40
41
|
.finally(() => {
|
|
41
42
|
prepareLoaders();
|
|
42
43
|
});
|
|
44
|
+
// #region Loader Configuration
|
|
45
|
+
/**
|
|
46
|
+
* Return the current loader configuration.
|
|
47
|
+
* These paths can be changed using `setDracoDecoderLocation` and `setKTX2TranscoderLocation`.
|
|
48
|
+
*/
|
|
49
|
+
export const GET_LOADER_LOCATION_CONFIG = () => ({
|
|
50
|
+
dracoDecoderPath: DEFAULT_DRACO_DECODER_LOCATION,
|
|
51
|
+
ktx2TranscoderPath: DEFAULT_KTX2_TRANSCODER_LOCATION
|
|
52
|
+
});
|
|
43
53
|
/**
|
|
44
54
|
* Set the location of the Draco decoder. If a draco loader has already been created, it will be updated.
|
|
45
55
|
* @default 'https://www.gstatic.com/draco/versioned/decoders/1.5.7/'
|
|
@@ -72,28 +82,6 @@ export function setKTX2TranscoderLocation(location) {
|
|
|
72
82
|
console.debug("Setting KTX2 transcoder path to " + location);
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
|
-
const $dracoDecoderPath = Symbol("dracoDecoderPath");
|
|
76
|
-
let dracoLoader;
|
|
77
|
-
let meshoptDecoder;
|
|
78
|
-
let ktx2Loader;
|
|
79
|
-
/** Used to create and load loaders */
|
|
80
|
-
function prepareLoaders() {
|
|
81
|
-
if (!dracoLoader) {
|
|
82
|
-
dracoLoader = new DRACOLoader();
|
|
83
|
-
dracoLoader[$dracoDecoderPath] = DEFAULT_DRACO_DECODER_LOCATION;
|
|
84
|
-
dracoLoader.setDecoderPath(DEFAULT_DRACO_DECODER_LOCATION);
|
|
85
|
-
dracoLoader.setDecoderConfig({ type: 'js' });
|
|
86
|
-
dracoLoader.preload();
|
|
87
|
-
}
|
|
88
|
-
if (!ktx2Loader) {
|
|
89
|
-
ktx2Loader = new KTX2Loader();
|
|
90
|
-
ktx2Loader.setTranscoderPath(DEFAULT_KTX2_TRANSCODER_LOCATION);
|
|
91
|
-
ktx2Loader.init();
|
|
92
|
-
}
|
|
93
|
-
if (!meshoptDecoder) {
|
|
94
|
-
meshoptDecoder = MeshoptDecoder;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
85
|
/**
|
|
98
86
|
* Create loaders/decoders for Draco, KTX2 and Meshopt to be used with GLTFLoader.
|
|
99
87
|
* @param renderer - Provide a renderer to detect KTX2 support.
|
|
@@ -116,6 +104,29 @@ export function addDracoAndKTX2Loaders(loader) {
|
|
|
116
104
|
if (!loader.meshoptDecoder)
|
|
117
105
|
loader.setMeshoptDecoder(meshoptDecoder);
|
|
118
106
|
}
|
|
107
|
+
// #region internal
|
|
108
|
+
const $dracoDecoderPath = Symbol("dracoDecoderPath");
|
|
109
|
+
let dracoLoader;
|
|
110
|
+
let meshoptDecoder;
|
|
111
|
+
let ktx2Loader;
|
|
112
|
+
/** Used to create and load loaders */
|
|
113
|
+
function prepareLoaders() {
|
|
114
|
+
if (!dracoLoader) {
|
|
115
|
+
dracoLoader = new DRACOLoader();
|
|
116
|
+
dracoLoader[$dracoDecoderPath] = DEFAULT_DRACO_DECODER_LOCATION;
|
|
117
|
+
dracoLoader.setDecoderPath(DEFAULT_DRACO_DECODER_LOCATION);
|
|
118
|
+
dracoLoader.setDecoderConfig({ type: 'js' });
|
|
119
|
+
dracoLoader.preload();
|
|
120
|
+
}
|
|
121
|
+
if (!ktx2Loader) {
|
|
122
|
+
ktx2Loader = new KTX2Loader();
|
|
123
|
+
ktx2Loader.setTranscoderPath(DEFAULT_KTX2_TRANSCODER_LOCATION);
|
|
124
|
+
ktx2Loader.init();
|
|
125
|
+
}
|
|
126
|
+
if (!meshoptDecoder) {
|
|
127
|
+
meshoptDecoder = MeshoptDecoder;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
119
130
|
const gltfLoaderConfigurations = new WeakMap();
|
|
120
131
|
export function configureLoader(loader, opts) {
|
|
121
132
|
let config = gltfLoaderConfigurations.get(loader);
|
package/lib/lods.manager.js
CHANGED
|
@@ -160,6 +160,14 @@ export class LODsManager {
|
|
|
160
160
|
constructor(renderer, context) {
|
|
161
161
|
this.renderer = renderer;
|
|
162
162
|
this.context = { ...context };
|
|
163
|
+
// createGLTFLoaderWorker().then(res => {
|
|
164
|
+
// res.load("https://cloud.needle.tools/-/assets/Z23hmXBZ20RjNk-Z20RjNk-optimized/file").then(res2 => {
|
|
165
|
+
// console.log("DONE", res2);
|
|
166
|
+
// })
|
|
167
|
+
// res.load("https://cloud.needle.tools/-/assets/Z23hmXBZ20RjNk-Z20RjNk-world/file").then(res2 => {
|
|
168
|
+
// console.log("DONE2", res2);
|
|
169
|
+
// })
|
|
170
|
+
// })
|
|
163
171
|
}
|
|
164
172
|
#originalRender;
|
|
165
173
|
#clock = new Clock();
|
|
@@ -360,7 +368,7 @@ export class LODsManager {
|
|
|
360
368
|
}
|
|
361
369
|
// TODO: we currently can not switch texture lods because we need better caching for the textures internally (see copySettings in progressive + NE-4431)
|
|
362
370
|
if (object.material && levels.texture_lod >= 0) {
|
|
363
|
-
this.loadProgressiveTextures(object.material, levels.texture_lod);
|
|
371
|
+
this.loadProgressiveTextures(object.material, levels.texture_lod, debugLodLevel);
|
|
364
372
|
}
|
|
365
373
|
if (debug && object.material && !object["isGizmo"]) {
|
|
366
374
|
applyDebugSettings(object.material);
|
|
@@ -376,7 +384,7 @@ export class LODsManager {
|
|
|
376
384
|
* @param level the LOD level to load. Level 0 is the best quality, higher levels are lower quality
|
|
377
385
|
* @returns Promise with true if the LOD was loaded, false if not
|
|
378
386
|
*/
|
|
379
|
-
loadProgressiveTextures(material, level) {
|
|
387
|
+
loadProgressiveTextures(material, level, overrideLodLevel) {
|
|
380
388
|
if (!material)
|
|
381
389
|
return;
|
|
382
390
|
if (Array.isArray(material)) {
|
|
@@ -394,10 +402,9 @@ export class LODsManager {
|
|
|
394
402
|
else if (level < material[$currentLOD]) {
|
|
395
403
|
update = true;
|
|
396
404
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
level = debugLevel;
|
|
405
|
+
if (overrideLodLevel !== undefined && overrideLodLevel >= 0) {
|
|
406
|
+
update = material[$currentLOD] != overrideLodLevel;
|
|
407
|
+
level = overrideLodLevel;
|
|
401
408
|
}
|
|
402
409
|
if (update) {
|
|
403
410
|
material[$currentLOD] = level;
|
|
@@ -459,6 +466,7 @@ export class LODsManager {
|
|
|
459
466
|
}
|
|
460
467
|
static skinnedMeshBoundsFrameOffsetCounter = 0;
|
|
461
468
|
static $skinnedMeshBoundsOffset = Symbol("gltf-progressive-skinnedMeshBoundsOffset");
|
|
469
|
+
// #region calculateLodLevel
|
|
462
470
|
calculateLodLevel(camera, mesh, state, desiredDensity, result) {
|
|
463
471
|
if (!mesh) {
|
|
464
472
|
result.mesh_lod = -1;
|
|
@@ -485,7 +493,7 @@ export class LODsManager {
|
|
|
485
493
|
const primitive_index = NEEDLE_progressive.getPrimitiveIndex(mesh.geometry);
|
|
486
494
|
const has_mesh_lods = mesh_lods && mesh_lods.length > 0;
|
|
487
495
|
const texture_lods_minmax = NEEDLE_progressive.getMaterialMinMaxLODsCount(mesh.material);
|
|
488
|
-
const has_texture_lods = texture_lods_minmax
|
|
496
|
+
const has_texture_lods = texture_lods_minmax.min_count !== Infinity && texture_lods_minmax.min_count >= 0 && texture_lods_minmax.max_count >= 0;
|
|
489
497
|
// We can skip all this if we dont have any LOD information
|
|
490
498
|
if (!has_mesh_lods && !has_texture_lods) {
|
|
491
499
|
result.mesh_lod = 0;
|
|
@@ -675,7 +683,7 @@ export class LODsManager {
|
|
|
675
683
|
if (changed) {
|
|
676
684
|
const level = mesh_lods?.[result.mesh_lod];
|
|
677
685
|
if (level) {
|
|
678
|
-
console.
|
|
686
|
+
console.debug(`Mesh LOD changed: ${state.lastLodLevel_Mesh} → ${result.mesh_lod} (density: ${level.densities?.[primitive_index].toFixed(0)}) | ${mesh.name}`);
|
|
679
687
|
}
|
|
680
688
|
}
|
|
681
689
|
}
|
|
@@ -710,10 +718,11 @@ export class LODsManager {
|
|
|
710
718
|
if (lod.max_height > pixelSizeOnScreen || (!foundLod && i === 0)) {
|
|
711
719
|
foundLod = true;
|
|
712
720
|
result.texture_lod = i;
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
721
|
+
if (debugProgressiveLoading) {
|
|
722
|
+
if (result.texture_lod < state.lastLodLevel_Texture) {
|
|
723
|
+
const lod_pixel_height = lod.max_height;
|
|
716
724
|
console.log(`Texture LOD changed: ${state.lastLodLevel_Texture} → ${result.texture_lod} = ${lod_pixel_height}px \nScreensize: ${pixelSizeOnScreen.toFixed(0)}px, Coverage: ${(100 * state.lastScreenCoverage).toFixed(2)}%, Volume ${volume.toFixed(1)} \n${mesh.name}`);
|
|
725
|
+
}
|
|
717
726
|
}
|
|
718
727
|
break;
|
|
719
728
|
}
|
package/lib/utils.internal.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ export declare function resolveUrl(source: string | undefined, uri: string): str
|
|
|
4
4
|
/** @returns `true` if it's a phone or tablet */
|
|
5
5
|
export declare function isMobileDevice(): boolean;
|
|
6
6
|
export declare function isDevelopmentServer(): boolean;
|
|
7
|
-
type SlotReturnValue = {
|
|
8
|
-
use?: ((promise: Promise<
|
|
7
|
+
export type SlotReturnValue<T = any> = {
|
|
8
|
+
use?: ((promise: Promise<T>) => void);
|
|
9
9
|
};
|
|
10
|
-
export declare class PromiseQueue {
|
|
10
|
+
export declare class PromiseQueue<T = any> {
|
|
11
11
|
readonly maxConcurrent: number;
|
|
12
12
|
private readonly _running;
|
|
13
13
|
private readonly _queue;
|
|
@@ -19,8 +19,7 @@ export declare class PromiseQueue {
|
|
|
19
19
|
/**
|
|
20
20
|
* Request a slot for a promise with a specific key. This function returns a promise with a `use` method that can be called to add the promise to the queue.
|
|
21
21
|
*/
|
|
22
|
-
slot(key: string): Promise<SlotReturnValue
|
|
22
|
+
slot(key: string): Promise<SlotReturnValue<T>>;
|
|
23
23
|
private add;
|
|
24
24
|
private internalUpdate;
|
|
25
25
|
}
|
|
26
|
-
export {};
|
package/lib/version.js
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { BufferGeometry, Texture, WebGLRenderer } from "three";
|
|
2
|
+
import type { KTX2LoaderWorkerConfig } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
3
|
+
type GLTFLoaderWorkerOptions = {
|
|
4
|
+
debug?: boolean;
|
|
5
|
+
};
|
|
6
|
+
type WorkerLoadResult = {
|
|
7
|
+
url: string;
|
|
8
|
+
geometries: Array<{
|
|
9
|
+
geometry: BufferGeometry;
|
|
10
|
+
meshIndex: number;
|
|
11
|
+
primitiveIndex: number;
|
|
12
|
+
extensions: Record<string, any>;
|
|
13
|
+
}>;
|
|
14
|
+
textures: Array<{
|
|
15
|
+
texture: Texture;
|
|
16
|
+
textureIndex: number;
|
|
17
|
+
extensions: Record<string, any>;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
export declare function getWorker(opts?: GLTFLoaderWorkerOptions): Promise<GLTFLoaderWorker>;
|
|
21
|
+
export type { GLTFLoaderWorker, GLTFLoaderWorkerOptions, WorkerLoadResult };
|
|
22
|
+
/** @internal */
|
|
23
|
+
export type GLTFLoaderWorker_Message = {
|
|
24
|
+
type: 'init';
|
|
25
|
+
} | {
|
|
26
|
+
type: 'load';
|
|
27
|
+
url: string;
|
|
28
|
+
dracoDecoderPath: string;
|
|
29
|
+
ktx2TranscoderPath: string;
|
|
30
|
+
ktx2LoaderConfig: KTX2LoaderWorkerConfig;
|
|
31
|
+
} | {
|
|
32
|
+
type: "loaded-gltf";
|
|
33
|
+
result: WorkerLoadResult;
|
|
34
|
+
};
|
|
35
|
+
declare class GLTFLoaderWorker {
|
|
36
|
+
private readonly worker;
|
|
37
|
+
static createWorker(opts: GLTFLoaderWorkerOptions): Promise<GLTFLoaderWorker>;
|
|
38
|
+
private _running;
|
|
39
|
+
private _webglRenderer;
|
|
40
|
+
load(url: string | URL, opts?: {
|
|
41
|
+
renderer?: WebGLRenderer;
|
|
42
|
+
}): Promise<WorkerLoadResult>;
|
|
43
|
+
private _debug;
|
|
44
|
+
private constructor();
|
|
45
|
+
}
|