@needle-tools/gltf-progressive 3.4.0-next.dbcee85 → 4.0.0-alpha
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 +6 -0
- package/README.md +9 -0
- package/package.json +21 -8
- package/gltf-progressive.js +0 -1490
- package/gltf-progressive.min.js +0 -9
- package/gltf-progressive.umd.cjs +0 -9
- package/lib/extension.d.ts +0 -124
- package/lib/extension.js +0 -1044
- package/lib/extension.model.d.ts +0 -33
- package/lib/extension.model.js +0 -1
- package/lib/index.d.ts +0 -22
- package/lib/index.js +0 -87
- package/lib/loaders.d.ts +0 -48
- package/lib/loaders.js +0 -161
- package/lib/lods.debug.d.ts +0 -4
- package/lib/lods.debug.js +0 -43
- package/lib/lods.manager.d.ts +0 -165
- package/lib/lods.manager.js +0 -747
- package/lib/lods.promise.d.ts +0 -68
- package/lib/lods.promise.js +0 -108
- package/lib/plugins/index.d.ts +0 -2
- package/lib/plugins/index.js +0 -1
- package/lib/plugins/modelviewer.d.ts +0 -1
- package/lib/plugins/modelviewer.js +0 -223
- package/lib/plugins/plugin.d.ts +0 -23
- package/lib/plugins/plugin.js +0 -5
- package/lib/utils.d.ts +0 -30
- package/lib/utils.internal.d.ts +0 -35
- package/lib/utils.internal.js +0 -117
- package/lib/utils.js +0 -82
- package/lib/version.d.ts +0 -1
- package/lib/version.js +0 -4
- package/lib/worker/loader.mainthread.d.ts +0 -45
- package/lib/worker/loader.mainthread.js +0 -193
- package/lib/worker/loader.worker.js +0 -165
package/lib/lods.promise.d.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
type PromiseType = "texture" | "mesh";
|
|
2
|
-
export type PromiseGroupOptions = {
|
|
3
|
-
/** Name for debugging purposes */
|
|
4
|
-
name?: string;
|
|
5
|
-
/** Define how many frames new LOD promises will at least be captured and awaited. The group will resolve after all promises captured during this time have resolved (or when the abort signal is triggered).
|
|
6
|
-
* @default 2 frames, which means the group will capture promises for 2 frames before resolving.
|
|
7
|
-
*/
|
|
8
|
-
frames?: number;
|
|
9
|
-
/** If set to true at least one LOD loading promise must be captured before this promise will resolve.
|
|
10
|
-
* After the first promise has been captured the group will wait for the amount of frames specified in the `frames` option.
|
|
11
|
-
*/
|
|
12
|
-
waitForFirstCapture?: boolean;
|
|
13
|
-
/** An optional signal to abort the promise */
|
|
14
|
-
signal?: AbortSignal;
|
|
15
|
-
/**
|
|
16
|
-
* If set to true, the group will only await one promise per object.
|
|
17
|
-
* @default 1
|
|
18
|
-
*/
|
|
19
|
-
maxPromisesPerObject?: number;
|
|
20
|
-
};
|
|
21
|
-
type PromiseGroupResolveResult = {
|
|
22
|
-
/**
|
|
23
|
-
* `true` if the group was cancelled, `false` if it was resolved normally.
|
|
24
|
-
*/
|
|
25
|
-
cancelled: boolean;
|
|
26
|
-
/**
|
|
27
|
-
* The number of promises that started to being awaited
|
|
28
|
-
*/
|
|
29
|
-
awaited_count: number;
|
|
30
|
-
/**
|
|
31
|
-
* The number of promises that were resolved
|
|
32
|
-
*/
|
|
33
|
-
resolved_count: number;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* A group of promises that can be awaited together.
|
|
37
|
-
* This is used for awaiting LOD
|
|
38
|
-
*/
|
|
39
|
-
export declare class PromiseGroup {
|
|
40
|
-
static readonly addPromise: (type: PromiseType, object: object, promise: Promise<any>, groups: PromiseGroup[]) => void;
|
|
41
|
-
readonly ready: Promise<PromiseGroupResolveResult>;
|
|
42
|
-
/** The number of promises that have been added to this group so far */
|
|
43
|
-
get awaitedCount(): number;
|
|
44
|
-
/** The number of promises that have been resolved */
|
|
45
|
-
get resolvedCount(): number;
|
|
46
|
-
/** The number of promises that are in-flight */
|
|
47
|
-
get currentlyAwaiting(): number;
|
|
48
|
-
private _resolve;
|
|
49
|
-
private readonly _signal?;
|
|
50
|
-
/** start frame can be undefined if the user configured this group to wait for the first promise.
|
|
51
|
-
* Then the start frame will be set when the first promise has been added to the group */
|
|
52
|
-
private _frame_start;
|
|
53
|
-
/** How many frames to capture since the start frame */
|
|
54
|
-
private _frames_to_capture;
|
|
55
|
-
private _resolved;
|
|
56
|
-
private _addedCount;
|
|
57
|
-
private _resolvedCount;
|
|
58
|
-
/** These promises are currently being awaited */
|
|
59
|
-
private readonly _awaiting;
|
|
60
|
-
private _maxPromisesPerObject;
|
|
61
|
-
constructor(frame: number, options: PromiseGroupOptions);
|
|
62
|
-
private _currentFrame;
|
|
63
|
-
update(frame: number): void;
|
|
64
|
-
private readonly _seen;
|
|
65
|
-
private add;
|
|
66
|
-
private resolveNow;
|
|
67
|
-
}
|
|
68
|
-
export {};
|
package/lib/lods.promise.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { debug } from "./lods.debug.js";
|
|
2
|
-
/**
|
|
3
|
-
* A group of promises that can be awaited together.
|
|
4
|
-
* This is used for awaiting LOD
|
|
5
|
-
*/
|
|
6
|
-
export class PromiseGroup {
|
|
7
|
-
static addPromise = (type, object, promise, groups) => {
|
|
8
|
-
groups.forEach(group => {
|
|
9
|
-
group.add(type, object, promise);
|
|
10
|
-
});
|
|
11
|
-
};
|
|
12
|
-
ready;
|
|
13
|
-
/** The number of promises that have been added to this group so far */
|
|
14
|
-
get awaitedCount() {
|
|
15
|
-
return this._addedCount;
|
|
16
|
-
}
|
|
17
|
-
/** The number of promises that have been resolved */
|
|
18
|
-
get resolvedCount() {
|
|
19
|
-
return this._resolvedCount;
|
|
20
|
-
}
|
|
21
|
-
/** The number of promises that are in-flight */
|
|
22
|
-
get currentlyAwaiting() {
|
|
23
|
-
return this._awaiting.length;
|
|
24
|
-
}
|
|
25
|
-
_resolve;
|
|
26
|
-
_signal;
|
|
27
|
-
/** start frame can be undefined if the user configured this group to wait for the first promise.
|
|
28
|
-
* Then the start frame will be set when the first promise has been added to the group */
|
|
29
|
-
_frame_start;
|
|
30
|
-
/** How many frames to capture since the start frame */
|
|
31
|
-
_frames_to_capture;
|
|
32
|
-
_resolved = false;
|
|
33
|
-
_addedCount = 0;
|
|
34
|
-
_resolvedCount = 0;
|
|
35
|
-
/** These promises are currently being awaited */
|
|
36
|
-
_awaiting = [];
|
|
37
|
-
_maxPromisesPerObject = 1;
|
|
38
|
-
constructor(frame, options) {
|
|
39
|
-
const minFrames = 2; // wait at least 2 frames to capture promises
|
|
40
|
-
const framesToCapture = Math.max(options.frames ?? minFrames, minFrames); // default to 2 frames and make sure it's at least 2 frames
|
|
41
|
-
this._frame_start = options.waitForFirstCapture ? undefined : frame;
|
|
42
|
-
this._frames_to_capture = framesToCapture;
|
|
43
|
-
this.ready = new Promise((resolve) => {
|
|
44
|
-
this._resolve = resolve;
|
|
45
|
-
});
|
|
46
|
-
this.ready.finally(() => {
|
|
47
|
-
this._resolved = true;
|
|
48
|
-
this._awaiting.length = 0;
|
|
49
|
-
});
|
|
50
|
-
this._signal = options.signal;
|
|
51
|
-
this._signal?.addEventListener("abort", () => {
|
|
52
|
-
this.resolveNow();
|
|
53
|
-
});
|
|
54
|
-
this._maxPromisesPerObject = Math.max(1, options.maxPromisesPerObject ?? 1);
|
|
55
|
-
}
|
|
56
|
-
_currentFrame = 0;
|
|
57
|
-
update(frame) {
|
|
58
|
-
this._currentFrame = frame;
|
|
59
|
-
// Check if start frame is not defined yet but we have added objects since the last update
|
|
60
|
-
if (this._frame_start === undefined && this._addedCount > 0) {
|
|
61
|
-
this._frame_start = frame;
|
|
62
|
-
}
|
|
63
|
-
// If we've passes the frame capture end frame and didn't add any promises, we resolve immediately
|
|
64
|
-
if (this._signal?.aborted || (this._awaiting.length === 0 && (this._frame_start !== undefined && (frame > this._frame_start + this._frames_to_capture)))) {
|
|
65
|
-
this.resolveNow();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
_seen = new WeakMap();
|
|
69
|
-
add(_type, object, promise) {
|
|
70
|
-
if (this._resolved) {
|
|
71
|
-
if (debug)
|
|
72
|
-
console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (this._frame_start !== undefined && this._currentFrame > this._frame_start + this._frames_to_capture) {
|
|
76
|
-
return; // we are not capturing any more promises
|
|
77
|
-
}
|
|
78
|
-
if (this._maxPromisesPerObject >= 1) {
|
|
79
|
-
if (this._seen.has(object)) {
|
|
80
|
-
let count = this._seen.get(object);
|
|
81
|
-
if (count >= this._maxPromisesPerObject) {
|
|
82
|
-
if (debug)
|
|
83
|
-
console.warn(`PromiseGroup: Already awaiting object ignoring new promise for it.`);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this._seen.set(object, count + 1);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
this._seen.set(object, 1);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
this._awaiting.push(promise);
|
|
93
|
-
this._addedCount++;
|
|
94
|
-
promise.finally(() => {
|
|
95
|
-
this._resolvedCount++;
|
|
96
|
-
this._awaiting.splice(this._awaiting.indexOf(promise), 1);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
resolveNow() {
|
|
100
|
-
if (this._resolved)
|
|
101
|
-
return;
|
|
102
|
-
this._resolve?.({
|
|
103
|
-
awaited_count: this._addedCount,
|
|
104
|
-
resolved_count: this._resolvedCount,
|
|
105
|
-
cancelled: this._signal?.aborted ?? false,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
package/lib/plugins/index.d.ts
DELETED
package/lib/plugins/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { patchModelViewer } from "./modelviewer.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function patchModelViewer(): void;
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { LODsManager } from "../lods.manager.js";
|
|
2
|
-
import { EXTENSION_NAME, NEEDLE_progressive, } from "../extension.js";
|
|
3
|
-
const $meshLODSymbol = Symbol("NEEDLE_mesh_lod");
|
|
4
|
-
const $textureLODSymbol = Symbol("NEEDLE_texture_lod");
|
|
5
|
-
let documentObserver = null;
|
|
6
|
-
export function patchModelViewer() {
|
|
7
|
-
const ModelViewerElement = tryGetModelViewerConstructor();
|
|
8
|
-
if (!ModelViewerElement) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
ModelViewerElement.mapURLs(function (url) {
|
|
12
|
-
searchModelViewers();
|
|
13
|
-
return url;
|
|
14
|
-
});
|
|
15
|
-
searchModelViewers();
|
|
16
|
-
// observe the document for new model-viewers
|
|
17
|
-
documentObserver?.disconnect();
|
|
18
|
-
documentObserver = new MutationObserver((mutations) => {
|
|
19
|
-
mutations.forEach((mutation) => {
|
|
20
|
-
mutation.addedNodes.forEach((node) => {
|
|
21
|
-
if (node instanceof HTMLElement && node.tagName.toLowerCase() === "model-viewer") {
|
|
22
|
-
_patchModelViewer(node);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
documentObserver.observe(document, { childList: true, subtree: true });
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Tries to get the mode-viewer constructor from the custom element registry. If it doesnt exist yet we will wait for it to be loaded in case it's added to the document at a later point
|
|
31
|
-
*/
|
|
32
|
-
function tryGetModelViewerConstructor() {
|
|
33
|
-
if (typeof customElements === 'undefined')
|
|
34
|
-
return null;
|
|
35
|
-
// If model-viewer is already registered we can ignore this
|
|
36
|
-
const ModelViewerElement = customElements.get('model-viewer');
|
|
37
|
-
if (ModelViewerElement)
|
|
38
|
-
return ModelViewerElement;
|
|
39
|
-
// wait for model-viewer to be defined
|
|
40
|
-
customElements.whenDefined('model-viewer').then(() => {
|
|
41
|
-
console.debug("[gltf-progressive] model-viewer defined");
|
|
42
|
-
patchModelViewer();
|
|
43
|
-
});
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
function searchModelViewers() {
|
|
47
|
-
if (typeof document === 'undefined')
|
|
48
|
-
return;
|
|
49
|
-
// Query once for model viewer. If a user does not have model-viewer in their page, this will return null.
|
|
50
|
-
const modelviewers = document.querySelectorAll("model-viewer");
|
|
51
|
-
modelviewers.forEach((modelviewer) => {
|
|
52
|
-
_patchModelViewer(modelviewer);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
const foundModelViewers = new WeakSet();
|
|
56
|
-
let modelViewerCount = 0;
|
|
57
|
-
/** Patch modelviewer to support NEEDLE progressive system
|
|
58
|
-
* @returns a function to remove the patch
|
|
59
|
-
*/
|
|
60
|
-
function _patchModelViewer(modelviewer) {
|
|
61
|
-
if (!modelviewer)
|
|
62
|
-
return null;
|
|
63
|
-
if (foundModelViewers.has(modelviewer))
|
|
64
|
-
return null;
|
|
65
|
-
foundModelViewers.add(modelviewer);
|
|
66
|
-
console.debug("[gltf-progressive] found new model-viewer..." + (++modelViewerCount) + "\n", modelviewer.getAttribute("src"));
|
|
67
|
-
// Find the necessary internal methods and properties. We need access to the scene, renderer
|
|
68
|
-
let renderer = null;
|
|
69
|
-
let scene = null;
|
|
70
|
-
let needsRender = null; // < used to force render updates for a few frames
|
|
71
|
-
for (let p = modelviewer; p != null; p = Object.getPrototypeOf(p)) {
|
|
72
|
-
const privateAPI = Object.getOwnPropertySymbols(p);
|
|
73
|
-
const rendererSymbol = privateAPI.find((value) => value.toString() == 'Symbol(renderer)');
|
|
74
|
-
const sceneSymbol = privateAPI.find((value) => value.toString() == 'Symbol(scene)');
|
|
75
|
-
const needsRenderSymbol = privateAPI.find((value) => value.toString() == 'Symbol(needsRender)');
|
|
76
|
-
if (!renderer && rendererSymbol != null) {
|
|
77
|
-
renderer = modelviewer[rendererSymbol].threeRenderer;
|
|
78
|
-
}
|
|
79
|
-
if (!scene && sceneSymbol != null) {
|
|
80
|
-
scene = modelviewer[sceneSymbol];
|
|
81
|
-
}
|
|
82
|
-
if (!needsRender && needsRenderSymbol != null) {
|
|
83
|
-
needsRender = modelviewer[needsRenderSymbol];
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (renderer && scene) {
|
|
87
|
-
console.debug("[gltf-progressive] setup model-viewer");
|
|
88
|
-
const lod = LODsManager.get(renderer, { engine: "model-viewer" });
|
|
89
|
-
LODsManager.addPlugin(new RegisterModelviewerDataPlugin());
|
|
90
|
-
lod.enable();
|
|
91
|
-
// Trigger a render when a LOD has changed
|
|
92
|
-
lod.addEventListener("changed", () => {
|
|
93
|
-
needsRender?.call(modelviewer);
|
|
94
|
-
});
|
|
95
|
-
// Trigger a render when the model viewer visibility changes
|
|
96
|
-
modelviewer.addEventListener("model-visibility", (evt) => {
|
|
97
|
-
const visible = evt.detail.visible;
|
|
98
|
-
if (visible)
|
|
99
|
-
needsRender?.call(modelviewer);
|
|
100
|
-
});
|
|
101
|
-
modelviewer.addEventListener("load", () => {
|
|
102
|
-
renderFrames();
|
|
103
|
-
});
|
|
104
|
-
/**
|
|
105
|
-
* For model viewer to immediately update without interaction we need to trigger a few renders
|
|
106
|
-
* We do this so that the LODs are loaded
|
|
107
|
-
*/
|
|
108
|
-
function renderFrames() {
|
|
109
|
-
if (needsRender) {
|
|
110
|
-
let forcedFrames = 0;
|
|
111
|
-
let interval = setInterval(() => {
|
|
112
|
-
if (forcedFrames++ > 5) {
|
|
113
|
-
clearInterval(interval);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
needsRender?.call(modelviewer);
|
|
117
|
-
}, 300);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return () => {
|
|
121
|
-
lod.disable();
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* LODs manager plugin that registers LOD data to the NEEDLE progressive system
|
|
128
|
-
*/
|
|
129
|
-
class RegisterModelviewerDataPlugin {
|
|
130
|
-
_didWarnAboutMissingUrl = false;
|
|
131
|
-
onBeforeUpdateLOD(_renderer, scene, _camera, object) {
|
|
132
|
-
this.tryParseMeshLOD(scene, object);
|
|
133
|
-
this.tryParseTextureLOD(scene, object);
|
|
134
|
-
}
|
|
135
|
-
getUrl(element) {
|
|
136
|
-
if (!element) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
let url = element.getAttribute("src");
|
|
140
|
-
// fallback in case the attribute is not set but the src property is
|
|
141
|
-
if (!url) {
|
|
142
|
-
url = element["src"];
|
|
143
|
-
}
|
|
144
|
-
if (!url) {
|
|
145
|
-
if (!this._didWarnAboutMissingUrl)
|
|
146
|
-
console.warn("No url found in modelviewer", element);
|
|
147
|
-
this._didWarnAboutMissingUrl = true;
|
|
148
|
-
}
|
|
149
|
-
return url;
|
|
150
|
-
}
|
|
151
|
-
tryGetCurrentGLTF(scene) {
|
|
152
|
-
return scene._currentGLTF;
|
|
153
|
-
}
|
|
154
|
-
tryGetCurrentModelViewer(scene) {
|
|
155
|
-
return scene.element;
|
|
156
|
-
}
|
|
157
|
-
tryParseTextureLOD(scene, object) {
|
|
158
|
-
if (object[$textureLODSymbol] == true)
|
|
159
|
-
return;
|
|
160
|
-
object[$textureLODSymbol] = true;
|
|
161
|
-
const currentGLTF = this.tryGetCurrentGLTF(scene);
|
|
162
|
-
const element = this.tryGetCurrentModelViewer(scene);
|
|
163
|
-
const url = this.getUrl(element);
|
|
164
|
-
if (!url) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (currentGLTF) {
|
|
168
|
-
if (object.material) {
|
|
169
|
-
const mat = object.material;
|
|
170
|
-
if (Array.isArray(mat))
|
|
171
|
-
for (const m of mat)
|
|
172
|
-
handleMaterial(m);
|
|
173
|
-
else
|
|
174
|
-
handleMaterial(mat);
|
|
175
|
-
function handleMaterial(mat) {
|
|
176
|
-
if (mat[$textureLODSymbol] == true)
|
|
177
|
-
return;
|
|
178
|
-
mat[$textureLODSymbol] = true;
|
|
179
|
-
// make sure to force the material to be updated
|
|
180
|
-
if (mat.userData)
|
|
181
|
-
mat.userData.LOD = -1;
|
|
182
|
-
const keys = Object.keys(mat);
|
|
183
|
-
for (let i = 0; i < keys.length; i++) {
|
|
184
|
-
const key = keys[i];
|
|
185
|
-
const value = mat[key];
|
|
186
|
-
if (value?.isTexture === true) {
|
|
187
|
-
const textureIndex = value.userData?.associations?.textures;
|
|
188
|
-
if (textureIndex == null)
|
|
189
|
-
continue;
|
|
190
|
-
const textureData = currentGLTF.parser.json.textures[textureIndex];
|
|
191
|
-
if (!textureData) {
|
|
192
|
-
console.warn("Texture data not found for texture index " + textureIndex);
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
if (textureData?.extensions?.[EXTENSION_NAME]) {
|
|
196
|
-
const ext = textureData.extensions[EXTENSION_NAME];
|
|
197
|
-
if (ext && url) {
|
|
198
|
-
NEEDLE_progressive.registerTexture(url, value, ext.lods.length, textureIndex, ext);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
tryParseMeshLOD(scene, object) {
|
|
208
|
-
if (object[$meshLODSymbol] == true)
|
|
209
|
-
return;
|
|
210
|
-
object[$meshLODSymbol] = true;
|
|
211
|
-
const element = this.tryGetCurrentModelViewer(scene);
|
|
212
|
-
const url = this.getUrl(element);
|
|
213
|
-
if (!url) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
// modelviewer has all the information we need in the userData (associations + gltfExtensions)
|
|
217
|
-
const ext = object.userData?.["gltfExtensions"]?.[EXTENSION_NAME];
|
|
218
|
-
if (ext && url) {
|
|
219
|
-
const lodKey = object.uuid;
|
|
220
|
-
NEEDLE_progressive.registerMesh(url, lodKey, object, 0, ext.lods.length, ext);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
package/lib/plugins/plugin.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { WebGLRenderer, Scene, Camera, Mesh } from 'three';
|
|
2
|
-
import { NEEDLE_ext_progressive_mesh } from '../extension.model.js';
|
|
3
|
-
/**
|
|
4
|
-
* This interface is used to define a plugin for the progressive extension. It can be registered using the `registerPlugin` function.
|
|
5
|
-
*/
|
|
6
|
-
export interface NEEDLE_progressive_plugin {
|
|
7
|
-
/** Called before the LOD level will be requested/updated for a object */
|
|
8
|
-
onBeforeUpdateLOD?(renderer: WebGLRenderer, scene: Scene, camera: Camera, object: Mesh): void;
|
|
9
|
-
/** Called after the LOD level has been requested */
|
|
10
|
-
onAfterUpdatedLOD?(renderer: WebGLRenderer, scene: Scene, camera: Camera, object: Mesh, level: {
|
|
11
|
-
mesh_lod: number;
|
|
12
|
-
texture_lod: number;
|
|
13
|
-
}): void;
|
|
14
|
-
/** Called when a new mesh is registered */
|
|
15
|
-
onRegisteredNewMesh?(mesh: Mesh, ext: NEEDLE_ext_progressive_mesh): void;
|
|
16
|
-
/** Called before the LOD mesh is fetched */
|
|
17
|
-
onBeforeGetLODMesh?(mesh: Mesh, level: number): void;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* List of registered plugins for the progressive extension. Please use the `registerPlugin` function to add a plugin.
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
export declare const plugins: NEEDLE_progressive_plugin[];
|
package/lib/plugins/plugin.js
DELETED
package/lib/utils.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { BufferGeometry, Object3D } from "three";
|
|
2
|
-
export declare const isSSR: boolean;
|
|
3
|
-
/**
|
|
4
|
-
* The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
|
|
5
|
-
* @param obj the object to get the raycast mesh from
|
|
6
|
-
* @returns the raycast mesh or null if not set
|
|
7
|
-
*/
|
|
8
|
-
export declare function getRaycastMesh(obj: Object3D): BufferGeometry<any> | null;
|
|
9
|
-
/**
|
|
10
|
-
* Set the raycast mesh for an object.
|
|
11
|
-
* The raycast mesh is a low poly version of the mesh used for raycasting. It is set when a mesh that has LOD level with more vertices is discovered for the first time
|
|
12
|
-
* @param obj the object to set the raycast mesh for
|
|
13
|
-
* @param geom the raycast mesh
|
|
14
|
-
*/
|
|
15
|
-
export declare function registerRaycastMesh(obj: Object3D, geom: BufferGeometry): void;
|
|
16
|
-
/**
|
|
17
|
-
* Call this method to enable raycasting with the lowpoly raycast meshes (if available) for all meshes in the scene.
|
|
18
|
-
* This is useful for performance optimization when the scene contains high poly meshes that are not visible to the camera.
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* // call to enable raycasting with low poly raycast meshes
|
|
22
|
-
* useRaycastMeshes();
|
|
23
|
-
*
|
|
24
|
-
* // then use the raycaster as usual
|
|
25
|
-
* const raycaster = new Raycaster();
|
|
26
|
-
* raycaster.setFromCamera(mouse, camera);
|
|
27
|
-
* const intersects = raycaster.intersectObjects(scene.children, true);
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare function useRaycastMeshes(enabled?: boolean): void;
|
package/lib/utils.internal.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export declare function isDebugMode(): string | boolean;
|
|
2
|
-
export declare function getParam(name: string): boolean | string;
|
|
3
|
-
export declare function resolveUrl(source: string | undefined, uri: string): string;
|
|
4
|
-
/** Check if the current device is a mobile device.
|
|
5
|
-
* @returns `true` if it's a phone or tablet
|
|
6
|
-
*/
|
|
7
|
-
export declare function isMobileDevice(): boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Check if we are running in a development server (localhost or ip address).
|
|
10
|
-
* @returns `true` if we are running in a development server (localhost or ip address).
|
|
11
|
-
*/
|
|
12
|
-
export declare function isDevelopmentServer(): boolean;
|
|
13
|
-
export type SlotReturnValue<T = any> = {
|
|
14
|
-
use?: ((promise: Promise<T>) => void);
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* A promise queue that limits the number of concurrent promises.
|
|
18
|
-
* Use the `slot` method to request a slot for a promise with a specific key. The returned promise resolves to an object with a `use` method that can be called to add the promise to the queue.
|
|
19
|
-
*/
|
|
20
|
-
export declare class PromiseQueue<T = any> {
|
|
21
|
-
readonly maxConcurrent: number;
|
|
22
|
-
private readonly _running;
|
|
23
|
-
private readonly _queue;
|
|
24
|
-
debug: boolean;
|
|
25
|
-
constructor(maxConcurrent?: number, opts?: {
|
|
26
|
-
debug?: boolean;
|
|
27
|
-
});
|
|
28
|
-
private tick;
|
|
29
|
-
/**
|
|
30
|
-
* 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.
|
|
31
|
-
*/
|
|
32
|
-
slot(key: string): Promise<SlotReturnValue<T>>;
|
|
33
|
-
private add;
|
|
34
|
-
private internalUpdate;
|
|
35
|
-
}
|
package/lib/utils.internal.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
const debug = getParam("debugprogressive");
|
|
2
|
-
export function isDebugMode() {
|
|
3
|
-
return debug;
|
|
4
|
-
}
|
|
5
|
-
export function getParam(name) {
|
|
6
|
-
if (typeof window === "undefined")
|
|
7
|
-
return false;
|
|
8
|
-
const url = new URL(window.location.href);
|
|
9
|
-
const param = url.searchParams.get(name);
|
|
10
|
-
if (param == null || param === "0" || param === "false")
|
|
11
|
-
return false;
|
|
12
|
-
if (param === "")
|
|
13
|
-
return true;
|
|
14
|
-
return param;
|
|
15
|
-
}
|
|
16
|
-
export function resolveUrl(source, uri) {
|
|
17
|
-
if (uri === undefined || source === undefined) {
|
|
18
|
-
return uri;
|
|
19
|
-
}
|
|
20
|
-
if (uri.startsWith("./") ||
|
|
21
|
-
uri.startsWith("http") ||
|
|
22
|
-
uri.startsWith("data:") ||
|
|
23
|
-
uri.startsWith("blob:")) {
|
|
24
|
-
return uri;
|
|
25
|
-
}
|
|
26
|
-
// TODO: why not just use new URL(uri, source).href ?
|
|
27
|
-
const pathIndex = source.lastIndexOf("/");
|
|
28
|
-
if (pathIndex >= 0) {
|
|
29
|
-
// Take the source uri as the base path
|
|
30
|
-
const basePath = source.substring(0, pathIndex + 1);
|
|
31
|
-
// make sure we don't have double slashes
|
|
32
|
-
while (basePath.endsWith("/") && uri.startsWith("/"))
|
|
33
|
-
uri = uri.substring(1);
|
|
34
|
-
// Append the relative uri
|
|
35
|
-
const newUri = basePath + uri;
|
|
36
|
-
// newUri = new URL(newUri, globalThis.location.href).href;
|
|
37
|
-
return newUri;
|
|
38
|
-
}
|
|
39
|
-
return uri;
|
|
40
|
-
}
|
|
41
|
-
/** Check if the current device is a mobile device.
|
|
42
|
-
* @returns `true` if it's a phone or tablet
|
|
43
|
-
*/
|
|
44
|
-
export function isMobileDevice() {
|
|
45
|
-
if (_ismobile !== undefined)
|
|
46
|
-
return _ismobile;
|
|
47
|
-
_ismobile = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent);
|
|
48
|
-
if (getParam("debugprogressive"))
|
|
49
|
-
console.log("[glTF Progressive]: isMobileDevice", _ismobile);
|
|
50
|
-
return _ismobile;
|
|
51
|
-
}
|
|
52
|
-
let _ismobile;
|
|
53
|
-
/**
|
|
54
|
-
* Check if we are running in a development server (localhost or ip address).
|
|
55
|
-
* @returns `true` if we are running in a development server (localhost or ip address).
|
|
56
|
-
*/
|
|
57
|
-
export function isDevelopmentServer() {
|
|
58
|
-
if (typeof window === "undefined")
|
|
59
|
-
return false;
|
|
60
|
-
const url = new URL(window.location.href);
|
|
61
|
-
const isLocalhostOrIpAddress = url.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(url.hostname);
|
|
62
|
-
const isDevelopment = url.hostname === "127.0.0.1" || isLocalhostOrIpAddress;
|
|
63
|
-
return isDevelopment;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* A promise queue that limits the number of concurrent promises.
|
|
67
|
-
* Use the `slot` method to request a slot for a promise with a specific key. The returned promise resolves to an object with a `use` method that can be called to add the promise to the queue.
|
|
68
|
-
*/
|
|
69
|
-
export class PromiseQueue {
|
|
70
|
-
maxConcurrent;
|
|
71
|
-
_running = new Map();
|
|
72
|
-
_queue = [];
|
|
73
|
-
debug = false;
|
|
74
|
-
constructor(maxConcurrent = 100, opts = {}) {
|
|
75
|
-
this.maxConcurrent = maxConcurrent;
|
|
76
|
-
this.debug = opts.debug ?? false;
|
|
77
|
-
window.requestAnimationFrame(this.tick);
|
|
78
|
-
}
|
|
79
|
-
tick = () => {
|
|
80
|
-
this.internalUpdate();
|
|
81
|
-
setTimeout(this.tick, 10);
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* 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.
|
|
85
|
-
*/
|
|
86
|
-
slot(key) {
|
|
87
|
-
if (this.debug)
|
|
88
|
-
console.debug(`[PromiseQueue]: Requesting slot for key ${key}, running: ${this._running.size}, waiting: ${this._queue.length}`);
|
|
89
|
-
return new Promise((resolve) => {
|
|
90
|
-
this._queue.push({ key, resolve });
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
add(key, promise) {
|
|
94
|
-
if (this._running.has(key))
|
|
95
|
-
return;
|
|
96
|
-
this._running.set(key, promise);
|
|
97
|
-
promise.finally(() => {
|
|
98
|
-
this._running.delete(key);
|
|
99
|
-
if (this.debug)
|
|
100
|
-
console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${key})`);
|
|
101
|
-
});
|
|
102
|
-
if (this.debug)
|
|
103
|
-
console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${key})`);
|
|
104
|
-
}
|
|
105
|
-
internalUpdate() {
|
|
106
|
-
// Run for as many free slots as we can
|
|
107
|
-
const diff = this.maxConcurrent - this._running.size;
|
|
108
|
-
for (let i = 0; i < diff && this._queue.length > 0; i++) {
|
|
109
|
-
if (this.debug)
|
|
110
|
-
console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);
|
|
111
|
-
const { key, resolve } = this._queue.shift();
|
|
112
|
-
resolve({
|
|
113
|
-
use: (promise) => this.add(key, promise)
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|