@needle-tools/engine 6.0.0-alpha.5 → 6.0.0-alpha.7
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 +9 -0
- package/dist/gltf-progressive-CRdzVlSY.umd.cjs +10 -0
- package/dist/gltf-progressive-OBdIERAY.min.js +10 -0
- package/dist/{gltf-progressive-B7lOWdLe.js → gltf-progressive-ZuAsp3FE.js} +237 -231
- package/dist/{materialx-CTPhpPxI.min.js → materialx-Bd0NThHa.min.js} +1 -1
- package/dist/{materialx-BwYvA-4x.umd.cjs → materialx-Clep-Dc8.umd.cjs} +2 -2
- package/dist/{materialx-B3qY8vPU.js → materialx-m9EM4lhZ.js} +1 -1
- package/dist/{needle-engine.bundle-_kAszB-k.min.js → needle-engine.bundle-B2_-yVCX.min.js} +148 -148
- package/dist/{needle-engine.bundle-D3nBMgfk.umd.cjs → needle-engine.bundle-BOs3iHUC.umd.cjs} +147 -147
- package/dist/{needle-engine.bundle-4xlWxOTF.js → needle-engine.bundle-hBHg3HuX.js} +7092 -7023
- package/dist/needle-engine.d.ts +49 -28
- package/dist/needle-engine.js +604 -603
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{vendor-BJviq-Oq.umd.cjs → vendor-Bb_SUKxZ.umd.cjs} +92 -92
- package/dist/{vendor-CdNldo8c.min.js → vendor-D1vc0sOe.min.js} +100 -100
- package/dist/{vendor-BQUQwut5.js → vendor-ipoLrKAE.js} +3466 -3320
- package/lib/engine/api.d.ts +2 -1
- package/lib/engine/api.js +2 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +14 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +7 -7
- package/lib/engine/engine_license.js +73 -73
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_loaders.gltf.d.ts +3 -1
- package/lib/engine/engine_loaders.gltf.js +6 -1
- package/lib/engine/engine_loaders.gltf.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics.d.ts +2 -0
- package/lib/engine/engine_physics.js +23 -1
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_qrcode.js.map +1 -1
- package/lib/engine/project-document/engine_document_extensions.d.ts +16 -0
- package/lib/engine/project-document/engine_document_extensions.js +29 -0
- package/lib/engine/project-document/engine_document_extensions.js.map +1 -0
- package/lib/engine/project-document/engine_document_runtime.js +1 -1
- package/lib/engine/project-document/engine_document_runtime.js.map +1 -1
- package/lib/engine/project-document/engine_project_document.js +39 -3
- package/lib/engine/project-document/engine_project_document.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js +5 -5
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +2 -2
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +1 -1
- package/lib/engine-components/Renderer.js +2 -11
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/package.json +4 -4
- package/plugins/common/license.js +4 -4
- package/plugins/license/cli.js +7 -4
- package/plugins/vite/build-pipeline.js +6 -1
- package/plugins/vite/license.js +4 -4
- package/plugins/vite/logger.client.js +21 -8
- package/src/engine/api.ts +2 -1
- package/src/engine/engine_context.ts +11 -0
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_license.ts +70 -70
- package/src/engine/engine_loaders.gltf.ts +8 -2
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_physics.ts +22 -1
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/project-document/engine_document_extensions.ts +39 -0
- package/src/engine/project-document/engine_document_runtime.ts +1 -1
- package/src/engine/project-document/engine_project_document.ts +46 -3
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +5 -5
- package/src/engine/webcomponents/needle-engine.loading.ts +2 -2
- package/src/engine/webcomponents/needle-engine.ts +2 -2
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/Renderer.ts +4 -13
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/dist/gltf-progressive-BpT9M5Mt.umd.cjs +0 -10
- package/dist/gltf-progressive-D0rzvhNG.min.js +0 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FileLoader } from "three";
|
|
2
2
|
|
|
3
3
|
import { showBalloonWarning } from "./debug/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { _yActo } from "./engine_license.js";
|
|
5
5
|
import { delay } from "./engine_utils.js";
|
|
6
6
|
import { md5AsBytes, md5Hex, sha256Base64 } from "./engine_utils_hash.js";
|
|
7
7
|
|
|
@@ -45,7 +45,7 @@ export namespace BlobStorage {
|
|
|
45
45
|
*/
|
|
46
46
|
export function canUpload(info: { filesize: number }) {
|
|
47
47
|
const sizeInMB = info.filesize / 1024 / 1024;
|
|
48
|
-
if (
|
|
48
|
+
if (_yActo()) {
|
|
49
49
|
return sizeInMB < maxSizeInMB;
|
|
50
50
|
}
|
|
51
51
|
return sizeInMB < maxFreeSizeInMB;
|
|
@@ -100,7 +100,7 @@ export namespace BlobStorage {
|
|
|
100
100
|
console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max allowed size is ${maxSizeInMB}MB`);
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
|
-
else if (!
|
|
103
|
+
else if (!_yActo() && filesizeInMB > maxFreeSizeInMB) {
|
|
104
104
|
if (opts?.silent !== true) showBalloonWarning(`File is too large for uploading. Please get a <a href=\"https://needle.tools/pricing\" target=\"_blank\">commercial license</a> to upload files larger than 5MB`);
|
|
105
105
|
console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max size is 5MB for non-commercial users. Please get a commercial license at https://needle.tools/pricing for larger files (up to 50MB)`);
|
|
106
106
|
return null;
|
|
@@ -183,6 +183,11 @@ export function setPhysicsEngineFactory(factory: ((context: Context) => IPhysics
|
|
|
183
183
|
|
|
184
184
|
export class Physics {
|
|
185
185
|
|
|
186
|
+
/** Release the shared BVH worker pool after the last engine context is destroyed. */
|
|
187
|
+
static disposeMeshBVHWorkers() {
|
|
188
|
+
NEMeshBVH.disposeWorkers();
|
|
189
|
+
}
|
|
190
|
+
|
|
186
191
|
private static _raycasting: number = 0;
|
|
187
192
|
/**
|
|
188
193
|
* Returns true if raycasting is currently in progress
|
|
@@ -805,7 +810,13 @@ namespace NEMeshBVH {
|
|
|
805
810
|
})
|
|
806
811
|
.finally(() => {
|
|
807
812
|
if (debugPhysics) console.log(">>>>> worker done", name, { hasBoundsTre: geom.boundsTree != undefined });
|
|
808
|
-
|
|
813
|
+
if (retiredWorkers.has(workerInstance)) {
|
|
814
|
+
retiredWorkers.delete(workerInstance);
|
|
815
|
+
workerInstance.dispose();
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
availableWorkers.push(workerInstance);
|
|
819
|
+
}
|
|
809
820
|
copy.dispose();
|
|
810
821
|
performance.mark("bvh.create.end");
|
|
811
822
|
performance.measure("bvh.create (worker)", "bvh.create.start", "bvh.create.end");
|
|
@@ -926,6 +937,16 @@ namespace NEMeshBVH {
|
|
|
926
937
|
const canUseWorkerSymbol = Symbol("Needle:MeshBVH-CanUseWorker");
|
|
927
938
|
const workerInstances: GenerateMeshBVHWorker[] = [];
|
|
928
939
|
const availableWorkers: GenerateMeshBVHWorker[] = [];
|
|
940
|
+
const retiredWorkers = new Set<GenerateMeshBVHWorker>();
|
|
941
|
+
|
|
942
|
+
export function disposeWorkers() {
|
|
943
|
+
for (const worker of workerInstances) {
|
|
944
|
+
if (worker.running) retiredWorkers.add(worker);
|
|
945
|
+
else worker.dispose();
|
|
946
|
+
}
|
|
947
|
+
workerInstances.length = 0;
|
|
948
|
+
availableWorkers.length = 0;
|
|
949
|
+
}
|
|
929
950
|
|
|
930
951
|
function internalSetupWorker() {
|
|
931
952
|
didSetupWorker = true;
|
|
@@ -4,7 +4,7 @@ import { Quaternion, Vector2, Vector3, Vector4 } from "three";
|
|
|
4
4
|
|
|
5
5
|
import { needleLogoOnlySVG } from "./assets/index.js";
|
|
6
6
|
import { isDevEnvironment } from "./debug/debug.js";
|
|
7
|
-
import {
|
|
7
|
+
import { _yActo } from "./engine_license.js";
|
|
8
8
|
import { InternalAttributeUtils } from "./engine_utils_attributes.js";
|
|
9
9
|
import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
|
|
10
10
|
|
|
@@ -135,7 +135,7 @@ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { s
|
|
|
135
135
|
console.debug("[QR Code] No web component found")
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
const canUseCustomLogo =
|
|
138
|
+
const canUseCustomLogo = _yActo();
|
|
139
139
|
|
|
140
140
|
// Query logo src from needle-engine attribute.
|
|
141
141
|
// For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DocumentDefContext, DocumentExtensionProvider } from '@needle-tools/addressing/document';
|
|
2
|
+
|
|
3
|
+
/** Creates extension readers bound to each document's Engine context. */
|
|
4
|
+
export interface DocumentExtensionRegistration {
|
|
5
|
+
readonly version: number;
|
|
6
|
+
readonly migrations?: DocumentExtensionProvider['migrations'];
|
|
7
|
+
readonly valueCodecs?: DocumentExtensionProvider['valueCodecs'];
|
|
8
|
+
readonly components?: {
|
|
9
|
+
createReader(context: DocumentDefContext): NonNullable<DocumentExtensionProvider['components']>['apply'] | undefined;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const extensions = new Map<string, DocumentExtensionRegistration>();
|
|
14
|
+
|
|
15
|
+
/** Register before loading documents. Disposing affects subsequent evaluations. */
|
|
16
|
+
export function registerDocumentExtensionProvider(id: string, provider: DocumentExtensionRegistration): { dispose(): void } {
|
|
17
|
+
if (!id.trim() || extensions.has(id)) throw new Error(`Invalid or duplicate document extension '${id}'.`);
|
|
18
|
+
if (!Number.isSafeInteger(provider.version) || provider.version < 1) throw new Error('Document extension version must be a positive integer.');
|
|
19
|
+
extensions.set(id, { ...provider });
|
|
20
|
+
let disposed = false;
|
|
21
|
+
return { dispose() {
|
|
22
|
+
if (disposed) return;
|
|
23
|
+
disposed = true;
|
|
24
|
+
extensions.delete(id);
|
|
25
|
+
} };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Bind readers at composition time, after asset loading and before activation. */
|
|
29
|
+
export function resolveDocumentExtensions(context: DocumentDefContext): ReadonlyMap<string, DocumentExtensionProvider> {
|
|
30
|
+
const result = new Map<string, DocumentExtensionProvider>();
|
|
31
|
+
for (const [id, provider] of extensions) {
|
|
32
|
+
const apply = provider.components?.createReader(context);
|
|
33
|
+
result.set(id, { version: provider.version, migrations: provider.migrations,
|
|
34
|
+
valueCodecs: provider.valueCodecs,
|
|
35
|
+
...(apply ? { components: { apply } } : {}),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
@@ -57,7 +57,7 @@ export function createNeedleDocumentRuntime(context: Context): DocumentRuntime {
|
|
|
57
57
|
createNode: () => new Object3D() as unknown as DocumentNode,
|
|
58
58
|
createColor: value => new Color().fromArray(value),
|
|
59
59
|
parse: async (data, src, options) => (await parseSync(context, data, src, null, options)) as unknown as DocumentModel | null,
|
|
60
|
-
|
|
60
|
+
clone(template, referenceId) {
|
|
61
61
|
const provider: UIDProvider = {
|
|
62
62
|
seed: 0,
|
|
63
63
|
generateUUID: () => deriveStableId(referenceId, `seq:${provider.seed++}`),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { joinPath, type NeedleAddress } from "@needle-tools/addressing";
|
|
1
2
|
import {
|
|
2
3
|
applyDef as applySharedDef,
|
|
3
4
|
applyOver as applySharedOver,
|
|
@@ -16,6 +17,7 @@ import type { Object3D } from "three";
|
|
|
16
17
|
import type { SerializationContext } from "../engine_serialization_core.js";
|
|
17
18
|
import type { Context } from "../engine_setup.js";
|
|
18
19
|
import type { Model } from "../engine_types.js";
|
|
20
|
+
import { resolveDocumentExtensions } from './engine_document_extensions.js';
|
|
19
21
|
import { createNeedleDocumentRuntime, documentDefContext } from "./engine_document_runtime.js";
|
|
20
22
|
|
|
21
23
|
export { createDefSerializationContext } from "./engine_document_runtime.js";
|
|
@@ -43,17 +45,58 @@ export type ProjectEvaluationResult = Omit<SharedResult, "root" | "references" |
|
|
|
43
45
|
};
|
|
44
46
|
export type EvaluateProjectDocumentOptions = Omit<SharedOptions, "parent"> & { parent?: Object3D };
|
|
45
47
|
|
|
48
|
+
type DocumentTarget = NonNullable<ProjectDocument["defs"]>[number]["target"];
|
|
49
|
+
type DocumentDef = NonNullable<ProjectDocument["defs"]>[number];
|
|
50
|
+
type DocumentOver = NonNullable<ProjectDocument["overs"]>[number];
|
|
51
|
+
|
|
52
|
+
/** Documents saved before addressing 0.4 used namePath instead of the escaped path string. */
|
|
53
|
+
function withLegacyPath<T extends { address?: NeedleAddress }>(target: T): T {
|
|
54
|
+
const address = target.address as (NeedleAddress & { namePath?: unknown }) | undefined;
|
|
55
|
+
if (address?.path !== undefined || !Array.isArray(address?.namePath) || !address.namePath.length
|
|
56
|
+
|| !address.namePath.every((name): name is string => typeof name === "string")) return target;
|
|
57
|
+
return { ...target, address: { ...address, path: joinPath(address.namePath) } };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function withLegacyDef(def: DocumentDef): DocumentDef {
|
|
61
|
+
const target = withLegacyPath(def.target);
|
|
62
|
+
const copy = def.create?.copy;
|
|
63
|
+
const source = copy ? withLegacyPath(copy.source) : undefined;
|
|
64
|
+
if (target === def.target && source === copy?.source) return def;
|
|
65
|
+
return { ...def, target, create: copy && source ? { ...def.create, copy: { ...copy, source } } : def.create };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function withLegacyOver(over: DocumentOver): DocumentOver {
|
|
69
|
+
const target = withLegacyPath(over.target);
|
|
70
|
+
const value = over.value && typeof over.value === "object" && "address" in over.value
|
|
71
|
+
? withLegacyPath(over.value as DocumentTarget) : over.value;
|
|
72
|
+
return target === over.target && value === over.value ? over : { ...over, target, value };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function withLegacyPaths(document: ProjectDocument): ProjectDocument {
|
|
76
|
+
return {
|
|
77
|
+
...document,
|
|
78
|
+
references: document.references?.map(ref => {
|
|
79
|
+
const normalized = withLegacyPath(ref);
|
|
80
|
+
const parent = ref.parent ? withLegacyPath(ref.parent) : undefined;
|
|
81
|
+
return parent === ref.parent ? normalized : { ...normalized, parent };
|
|
82
|
+
}),
|
|
83
|
+
defs: document.defs?.map(withLegacyDef),
|
|
84
|
+
overs: document.overs?.map(withLegacyOver),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
46
88
|
/** Engine entry point: preserve pre-awake scheduling through the runtime adapter. */
|
|
47
89
|
export async function evaluateProjectDocument(context: Context, document: ProjectDocument, options?: EvaluateProjectDocumentOptions): Promise<ProjectEvaluationResult> {
|
|
48
|
-
return await evaluateShared(createNeedleDocumentRuntime(context), document,
|
|
90
|
+
return await evaluateShared(createNeedleDocumentRuntime(context), withLegacyPaths(document),
|
|
91
|
+
{ ...options, extensions: options?.extensions ?? resolveDocumentExtensions } as unknown as SharedOptions) as unknown as ProjectEvaluationResult;
|
|
49
92
|
}
|
|
50
93
|
|
|
51
94
|
export function applyDef(def: NonNullable<ProjectDocument["defs"]>[number], root: Object3D, references: Map<string, Object3D>, issue: (arc: "def", message: string) => void, serializationContext: SerializationContext): void {
|
|
52
|
-
applySharedDef(def, root as unknown as DocumentNode, references as unknown as Map<string, DocumentNode>, issue, documentDefContext(serializationContext));
|
|
95
|
+
applySharedDef(withLegacyDef(def), root as unknown as DocumentNode, references as unknown as Map<string, DocumentNode>, issue, documentDefContext(serializationContext));
|
|
53
96
|
}
|
|
54
97
|
|
|
55
98
|
export function applyOver(context: Context, over: NonNullable<ProjectDocument["overs"]>[number], root: Object3D, references: Map<string, Object3D>, state: OverApplyState, issue: (arc: "over", message: string) => void): void {
|
|
56
|
-
applySharedOver(createNeedleDocumentRuntime(context), over, root as unknown as DocumentNode, references as unknown as Map<string, DocumentNode>, state, issue);
|
|
99
|
+
applySharedOver(createNeedleDocumentRuntime(context), withLegacyOver(over), root as unknown as DocumentNode, references as unknown as Map<string, DocumentNode>, state, issue);
|
|
57
100
|
}
|
|
58
101
|
|
|
59
102
|
export function resolveMaterialEntity(model: Model, address: import("@needle-tools/addressing").NeedleAddress): Promise<object | null> {
|
|
@@ -2,7 +2,7 @@ import { Mesh, Object3D, TextureLoader, Vector4 } from "three";
|
|
|
2
2
|
import ThreeMeshUI from "three-mesh-ui";
|
|
3
3
|
|
|
4
4
|
import { addNewComponent } from "../../engine_components.js";
|
|
5
|
-
import {
|
|
5
|
+
import { _yActo } from "../../engine_license.js";
|
|
6
6
|
import { OneEuroFilterXYZ } from "../../engine_math.js";
|
|
7
7
|
import type { Context } from "../../engine_setup.js";
|
|
8
8
|
import { lookAtObject } from "../../engine_three_utils.js";
|
|
@@ -321,7 +321,7 @@ export class NeedleSpatialMenu {
|
|
|
321
321
|
}
|
|
322
322
|
if (this.menu) {
|
|
323
323
|
const index = this.menu.children.indexOf(this._poweredByNeedleElement as any);
|
|
324
|
-
if (!this._showNeedleLogo &&
|
|
324
|
+
if (!this._showNeedleLogo && _yActo()) {
|
|
325
325
|
if (index >= 0) {
|
|
326
326
|
this._poweredByNeedleElement.removeFromParent();
|
|
327
327
|
this.markDirty();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { showBalloonMessage } from "../../debug/debug.js";
|
|
2
2
|
import type { Context } from "../../engine_context.js";
|
|
3
|
-
import {
|
|
3
|
+
import { _yActo, _PvsqpIB, Telemetry } from "../../engine_license.js";
|
|
4
4
|
import { isLocalNetwork } from "../../engine_networking_utils.js";
|
|
5
5
|
import { HTMLElementBase } from "../../engine_ssr.js";
|
|
6
6
|
import { DeviceUtilities, getParam } from "../../engine_utils.js";
|
|
@@ -801,8 +801,8 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
801
801
|
// calling this method immediately will cause an issue with vite bundling tho
|
|
802
802
|
this._licenseFrame = window.requestAnimationFrame(() => {
|
|
803
803
|
this._licenseFrame = undefined;
|
|
804
|
-
this._unsubscribeLicense =
|
|
805
|
-
if (res == true &&
|
|
804
|
+
this._unsubscribeLicense = _PvsqpIB(res => {
|
|
805
|
+
if (res == true && _yActo() && !debugNonCommercial) {
|
|
806
806
|
let visible = this._userRequestedLogoVisible;
|
|
807
807
|
if (visible === undefined) visible = false;
|
|
808
808
|
this.___onSetLogoVisible(visible);
|
|
@@ -854,7 +854,7 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
854
854
|
// ensure the menu is not hidden or removed
|
|
855
855
|
const requiredParent = this?.parentNode;
|
|
856
856
|
if (this.style.display != "flex" || this.style.visibility != "visible" || this.style.opacity != "1" || requiredParent != this._domElement?.shadowRoot) {
|
|
857
|
-
if (!
|
|
857
|
+
if (!_yActo()) {
|
|
858
858
|
const change = changeEventCounter++;
|
|
859
859
|
// if a user doesn't have a local pro license *but* for development the menu is hidden then we show a warning
|
|
860
860
|
if (isLocalNetwork() && this._userRequestedMenuVisible === false) {
|
|
@@ -950,7 +950,7 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
950
950
|
showNeedleLogo(visible: boolean) {
|
|
951
951
|
this._userRequestedLogoVisible = visible;
|
|
952
952
|
if (!visible) {
|
|
953
|
-
if (!
|
|
953
|
+
if (!_yActo() || debugNonCommercial) {
|
|
954
954
|
console.warn("[Needle Engine] You need a commercial license to hide the Needle Engine logo in production.");
|
|
955
955
|
const localNetwork = isLocalNetwork()
|
|
956
956
|
if (!localNetwork) return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { needleLogoOnlySVG } from "../assets/index.js"
|
|
2
2
|
import { showBalloonWarning } from "../debug/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { __mor } from "../engine_license.js";
|
|
4
4
|
import { Mathf } from "../engine_math.js";
|
|
5
5
|
import { LoadingProgressArgs } from "../engine_setup.js";
|
|
6
6
|
import { getParam } from "../engine_utils.js";
|
|
@@ -402,7 +402,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
402
402
|
const logoToggle = InternalAttributeUtils.getAttributeAndCheckFalsey(this._element, "loading-logo");
|
|
403
403
|
const showLogo = logoToggle !== null && logoToggle !== false;
|
|
404
404
|
|
|
405
|
-
const hasLicense =
|
|
405
|
+
const hasLicense = __mor();
|
|
406
406
|
|
|
407
407
|
const className = this._loadingElementOptions?.className ?? EngineLoadingView.LoadingContainerClassName;
|
|
408
408
|
el.classList.add(className);
|
|
@@ -3,7 +3,7 @@ import type { Texture } from "three";
|
|
|
3
3
|
import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
|
|
4
4
|
import { PUBLIC_KEY, VERSION } from "../engine_constants.js";
|
|
5
5
|
import { ContextEvent, ContextRegistry } from "../engine_context_registry.js";
|
|
6
|
-
import {
|
|
6
|
+
import { _yActo, Telemetry } from "../engine_license.js";
|
|
7
7
|
import { onStart } from "../engine_lifecycle_api.js";
|
|
8
8
|
import { setDracoDecoderPath, setDracoDecoderType, setKtx2TranscoderPath } from "../engine_loaders.gltf.js";
|
|
9
9
|
import { Context, ContextCreateArgs } from "../engine_setup.js";
|
|
@@ -617,7 +617,7 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
|
|
|
617
617
|
|
|
618
618
|
|
|
619
619
|
// Loading start events
|
|
620
|
-
const allowOverridingDefaultLoading =
|
|
620
|
+
const allowOverridingDefaultLoading = _yActo();
|
|
621
621
|
// default loading can be overriden by calling preventDefault in the onload start event
|
|
622
622
|
this.ensureLoadStartIsRegistered();
|
|
623
623
|
let useDefaultLoading = this.dispatchEvent(new CustomEvent("loadstart", {
|
|
@@ -3,7 +3,7 @@ import { ArrayCamera, AxesHelper, Camera, Color, DirectionalLight, Fog, GridHelp
|
|
|
3
3
|
import { needleLogoOnlySVG } from "../assets/index.js";
|
|
4
4
|
import { isDevEnvironment } from "../debug/index.js";
|
|
5
5
|
import { ObjectUtils, PrimitiveType } from "../engine_create_objects.js";
|
|
6
|
-
import {
|
|
6
|
+
import { _yActo } from "../engine_license.js";
|
|
7
7
|
import { Mathf } from "../engine_math.js";
|
|
8
8
|
import { delay, DeviceUtilities } from "../engine_utils.js";
|
|
9
9
|
|
|
@@ -215,7 +215,7 @@ export class TemporaryXRContext {
|
|
|
215
215
|
this._scene.background = new Color(0x000000);
|
|
216
216
|
|
|
217
217
|
let logoSrc = needleLogoOnlySVG;
|
|
218
|
-
if (
|
|
218
|
+
if (_yActo()) {
|
|
219
219
|
const htmlComponent = document.querySelector("needle-engine");
|
|
220
220
|
if (htmlComponent) {
|
|
221
221
|
const licenseLogo = htmlComponent.getAttribute("logo-src");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRaycastMesh } from "@needle-tools/gltf-progressive";
|
|
2
|
-
import { AxesHelper, Material, Mesh,
|
|
2
|
+
import { AxesHelper, Material, Mesh, MeshPhysicalMaterial, MeshStandardMaterial, Object3D, RawShaderMaterial, ShaderMaterial, SkinnedMesh, Texture, Vector4 } from "three";
|
|
3
3
|
|
|
4
4
|
import { showBalloonWarning } from "../engine/debug/index.js";
|
|
5
5
|
import { getComponent, getOrAddComponent } from "../engine/engine_components.js";
|
|
@@ -305,21 +305,12 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
305
305
|
// private _materialProperties: Array<MaterialProperties> | undefined = undefined;
|
|
306
306
|
private _lightmaps?: RendererLightmap[];
|
|
307
307
|
|
|
308
|
-
/** Get the mesh Object3D for this renderer
|
|
309
|
-
* Warn: if this is a multimaterial object it will return the first
|
|
308
|
+
/** Get the mesh Object3D for this renderer
|
|
309
|
+
* Warn: if this is a multimaterial object it will return the first submesh only (same as `sharedMeshes[0]`), use {@link sharedMeshes} to get all of them
|
|
310
310
|
* @returns a mesh object3D.
|
|
311
311
|
* */
|
|
312
312
|
get sharedMesh(): Mesh | SkinnedMesh | undefined {
|
|
313
|
-
|
|
314
|
-
return this.gameObject as unknown as Mesh
|
|
315
|
-
}
|
|
316
|
-
else if (this.gameObject.type === "SkinnesMesh") {
|
|
317
|
-
return this.gameObject as unknown as SkinnedMesh;
|
|
318
|
-
}
|
|
319
|
-
else if (this.gameObject.type === "Group") {
|
|
320
|
-
return this.gameObject.children[0] as unknown as Mesh;
|
|
321
|
-
}
|
|
322
|
-
return undefined;
|
|
313
|
+
return this.sharedMeshes[0];
|
|
323
314
|
}
|
|
324
315
|
|
|
325
316
|
private readonly _sharedMeshes: Mesh[] = [];
|
|
@@ -3,7 +3,7 @@ import { Euler, Material, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "t
|
|
|
3
3
|
|
|
4
4
|
import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/index.js";
|
|
5
5
|
import { findObjectOfType } from "../../../engine/engine_components.js";
|
|
6
|
-
import {
|
|
6
|
+
import { __mor } from "../../../engine/engine_license.js";
|
|
7
7
|
import { serializable } from "../../../engine/engine_serialization.js";
|
|
8
8
|
import { getFormattedDate, Progress } from "../../../engine/engine_time_utils.js";
|
|
9
9
|
import { DeviceUtilities, getParam } from "../../../engine/engine_utils.js";
|
|
@@ -277,7 +277,7 @@ export class USDZExporter extends Behaviour {
|
|
|
277
277
|
let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
|
|
278
278
|
name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
|
|
279
279
|
|
|
280
|
-
if (!
|
|
280
|
+
if (!__mor()) {
|
|
281
281
|
if (name !== "") name += "-";
|
|
282
282
|
name += "MadeWithNeedle";
|
|
283
283
|
}
|
|
@@ -682,7 +682,7 @@ export class USDZExporter extends Behaviour {
|
|
|
682
682
|
if (debug)
|
|
683
683
|
showBalloonMessage("Quicklook url: " + callToActionURL);
|
|
684
684
|
if (callToActionURL) {
|
|
685
|
-
if (!
|
|
685
|
+
if (!__mor()) {
|
|
686
686
|
console.warn("Quicklook closed: custom redirects require a Needle Engine Pro license: https://needle.tools/pricing", callToActionURL)
|
|
687
687
|
}
|
|
688
688
|
else {
|
|
@@ -697,7 +697,7 @@ export class USDZExporter extends Behaviour {
|
|
|
697
697
|
private buildQuicklookOverlay(): CustomBranding {
|
|
698
698
|
const obj: CustomBranding = {};
|
|
699
699
|
if (this.customBranding) Object.assign(obj, this.customBranding);
|
|
700
|
-
if (!
|
|
700
|
+
if (!__mor()) {
|
|
701
701
|
console.log("Custom Quicklook banner text requires pro license: https://needle.tools/pricing");
|
|
702
702
|
obj.callToAction = "Close";
|
|
703
703
|
obj.checkoutTitle = "🌵 Made with Needle";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";const f=require("./three-core.umd.cjs"),Z=require("./three-examples.umd.cjs");var fe=typeof document<"u"?document.currentScript:null;const Ee="";globalThis.GLTF_PROGRESSIVE_VERSION=Ee;console.debug("[gltf-progressive] version -");let $="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",K="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ne=$,Xe=K;if(typeof window<"u"){const o=new URL($+"draco_decoder.js");o.searchParams.append("range","true"),fetch(o,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(t=>{console.debug(`Failed to fetch remote Draco decoder from ${$} (offline: ${navigator.onLine})`),$===Ne&&Ie("./include/draco/"),K===Xe&&Ge("./include/ktx2/")}).finally(()=>{$e()})}const je=()=>({dracoDecoderPath:$,ktx2TranscoderPath:K});function Ie(o){$=o,A&&A[xe]!=$?(console.debug("Updating Draco decoder path to "+o),A[xe]=$,A.setDecoderPath($),A.preload()):console.debug("Setting Draco decoder path to "+o)}function Ge(o){K=o,B&&B.transcoderPath!=K?(console.debug("Updating KTX2 transcoder path to "+o),B.setTranscoderPath(K),B.init()):console.debug("Setting KTX2 transcoder path to "+o)}function ce(o){return $e(),o?B.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:B,meshoptDecoder:ue}}function Me(o){o.dracoLoader||o.setDRACOLoader(A),o.ktx2Loader||o.setKTX2Loader(B),o.meshoptDecoder||o.setMeshoptDecoder(ue)}const xe=Symbol("dracoDecoderPath");let A,ue,B;function $e(){A||(A=new Z.DRACOLoader,A[xe]=$,A.setDecoderPath($),A.preload()),B||(B=new Z.KTX2Loader,B.setTranscoderPath(K),B.init()),ue||(ue=Z.MeshoptDecoder)}const Le=new WeakMap;function Te(o,t){let e=Le.get(o);e?e=Object.assign(e,t):e=t,Le.set(o,e)}const Ke=Z.GLTFLoader.prototype.load;function He(...o){const t=Le.get(this);let e=o[0];const s=Ye(e);if(s?.hostname.endsWith("needle.tools")){const n=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";n?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=s.toString()}return o[0]=e,Ke?.call(this,...o)}Z.GLTFLoader.prototype.load=He;function Ye(o){try{if(o instanceof URL)return o;const t=globalThis.location?.href;return t?new URL(o,t):new URL(o)}catch{return null}}function Oe(o){return o!=null&&o.data!=null}function we(o){const t=o.source?.data;return t!=null&&typeof t=="object"?t:null}function Qe(o){const t=o.image;return t!=null&&typeof t=="object"?t:null}function De(o){const t=Qe(o),e=we(o);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}E("debugprogressive");function E(o){const t=globalThis.location?.href;if(!t)return!1;const s=new URL(t).searchParams.get(o);return s==null||s==="0"||s==="false"?!1:s===""?!0:s}function Je(o,t){if(t===void 0||o===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=o.lastIndexOf("/");if(e>=0){const s=o.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function _e(){if(te!==void 0)return te;const o=globalThis.navigator?.userAgent||"";return te=/iPhone|iPad|iPod|Android|IEMobile/i.test(o),E("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",te),te}let te;function Be(){const o=globalThis.location?.href;if(!o)return!1;const t=new URL(o),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class Ze{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}maxConcurrent;_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:s,resolve:r}=this._queue.shift();r({use:n=>this.add(s,n)})}}}function et(o){const t=o.image,e=t?.width??0,s=t?.height??0,r=t?.depth??1,n=Math.floor(Math.log2(Math.max(e,s,r)))+1,i=tt(o);return e*s*r*i*(1-Math.pow(.25,n))/(1-.25)}function tt(o){let t=4;const e=o.format;e===f.RedFormat||e===f.RedIntegerFormat?t=1:e===f.RGFormat||e===f.RGIntegerFormat?t=2:e===f.RGBFormat||e===1029?t=3:(e===f.RGBAFormat||e===f.RGBAIntegerFormat)&&(t=4);let s=1;const r=o.type;return r===1009||r===1010?s=1:r===1011||r===1012?s=2:r===1013||r===1014||r===1015?s=4:r===1016&&(s=2),t*s}const st=typeof window>"u"&&typeof document>"u",ve=Symbol("needle:raycast-mesh");function ee(o){return o?.[ve]instanceof f.BufferGeometry?o[ve]:null}function rt(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!ee(o)){const s=ot(t);s.userData={isRaycastMesh:!0},o[ve]=s}}function nt(o=!0){if(o){if(se)return;const t=se=f.Mesh.prototype.raycast;f.Mesh.prototype.raycast=function(e,s){const r=this,n=ee(r);let i;n&&r.isMesh&&(i=r.geometry,r.geometry=n),t.call(this,e,s),i&&(r.geometry=i)}}else{if(!se)return;f.Mesh.prototype.raycast=se,se=null}}let se=null;function ot(o){const t=new f.BufferGeometry;for(const e in o.attributes)t.setAttribute(e,o.getAttribute(e));return t.setIndex(o.getIndex()),t}const F=new Array,p=E("debugprogressive");let oe,X=-1;if(p&&typeof window<"u"){let o=function(){X+=1,X>=t&&(X=-1),console.log(`Toggle LOD level [${X}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(oe=!oe,console.log(`Toggle wireframe [${oe}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(X=s,console.log(`Set LOD level to [${X}]`))})}function qe(o){if(p&&oe!==void 0)if(Array.isArray(o))for(const t of o)qe(t);else o&&"wireframe"in o&&(o.wireframe=oe===!0)}const re=new Array;let it=0;const at=_e()?2:10;function lt(o){if(re.length<at){const s=re.length;p&&console.warn(`[Worker] Creating new worker #${s}`);const r=be.createWorker(o||{});return re.push(r),r}const t=it++%re.length;return re[t]}class be{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=s=>{const r=s.data;switch(this._debug&&console.log("[Worker] EVENT",r),r.type){case"loaded-gltf":for(const n of this._running)if(n.url===r.result.url){ut(r.result),n.resolve(r.result);const i=n.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}worker;static async createWorker(t){const e=new Worker(new URL("/gltf-progressive.worker-DvoMaVdx.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:fe&&fe.tagName.toUpperCase()==="SCRIPT"&&fe.src||new URL("gltf-progressive-BpT9M5Mt.umd.cjs",document.baseURI).href),{type:"module"});return new be(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=je();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await Promise.resolve().then(()=>require("./three-core.umd.cjs")).then(h=>h.THREE);return new u})(),r=await this._webglRenderer);const l=ce(r).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const u=globalThis.location?.href;u&&(t=new URL(t,u).toString())}const a={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function ut(o){for(const t of o.geometries){const e=t.geometry,s=new f.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(de(r))}for(const r in e.attributes){const n=e.attributes[r],i=de(n);s.setAttribute(r,i)}if(e.morphAttributes)for(const r in e.morphAttributes){const i=e.morphAttributes[r].map(l=>de(l));s.morphAttributes[r]=i}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new f.Box3,s.boundingBox.min=new f.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new f.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new f.Sphere(new f.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const r of e.groups)s.addGroup(r.start,r.count,r.materialIndex);e.userData&&(s.userData=e.userData),t.geometry=s}for(const t of o.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,{width:n,height:i}=De(e);s=new f.CompressedTexture(r,n,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new f.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),s.mipmaps=e.mipmaps,s.channel=e.channel,s.source.data=e.source.data,s.flipY=e.flipY,s.premultiplyAlpha=e.premultiplyAlpha,s.unpackAlignment=e.unpackAlignment,s.matrix=new f.Matrix3(...e.matrix.elements);if(!s){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=s}return o}function de(o){let t=o;if("isInterleavedBufferAttribute"in o&&o.isInterleavedBufferAttribute){const e=o.data,s=e.array,r=new f.InterleavedBuffer(s,e.stride);t=new f.InterleavedBufferAttribute(r,o.itemSize,s.byteOffset,o.normalized),t.offset=o.offset}else"isBufferAttribute"in o&&o.isBufferAttribute&&(t=new f.BufferAttribute(o.array,o.itemSize,o.normalized),t.usage=o.usage,t.gpuType=o.gpuType,t.updateRanges=o.updateRanges);return t}const ct=E("gltf-progressive-worker");E("gltf-progressive-reduce-mipmaps");const ne=E("gltf-progressive-gc"),he=Symbol("needle-progressive-texture"),W="NEEDLE_progressive";class m{get name(){return W}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-1}static getMaterialMinMaxLODsCount(t,e){const s=this,r="LODS:minmax",n=t[r];if(n!=null)return n;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[r]=e,e}if(p==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function i(l,a){const u=s.getAssignedLODInformation(l);if(u){const h=s.lodInfos.get(u.key);if(h&&h.lods){a.min_count=Math.min(a.min_count,h.lods.length),a.max_count=Math.max(a.max_count,h.lods.length);for(let c=0;c<h.lods.length;c++){const d=h.lods[c];d.width&&(a.lods[c]=a.lods[c]||{min_height:1/0,max_height:0},a.lods[c].min_height=Math.min(a.lods[c].min_height,d.height),a.lods[c].max_height=Math.max(a.lods[c].max_height,d.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const n of t)if(this.hasLODLevelAvailable(n,e))return!0;return!1}if(t.isMaterial===!0){for(const n of Object.keys(t)){const i=t[n];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const n of t.children)if(n.isMesh===!0&&this.hasLODLevelAvailable(n,e))return!0}let s,r;if(t.isMesh?s=t.geometry:(t.isBufferGeometry||t.isTexture)&&(s=t),s&&s?.userData?.LODS){const n=s.userData.LODS;if(r=this.lodInfos.get(n.key),e===void 0)return r!=null;if(r)return Array.isArray(r.lods)?e<r.lods.length:e===0}return!1}static assignMeshLOD(t,e,s){if(!t)return Promise.resolve(null);if(t instanceof f.Mesh||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const l of F)l.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return m.getOrLoadLOD(r,e,{isCurrent:i}).then(l=>{if(Array.isArray(l)){const u=n.index||0;l=l[u]}const a=t["LOD:requested level"]===e;return(a||this.shouldApplyStaleMeshLOD(t,e))&&(a&&delete t["LOD:requested level"],l&&r!=l&&(l?.isBufferGeometry?typeof s?.apply=="function"?s.apply(l,e,t):s?.apply!==!1&&(t.geometry=l):p&&console.error("Invalid LOD geometry",l))),l}).catch(l=>(console.error("Error loading mesh LOD",t,l),null))}else p&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0,s){if(!t)return Promise.resolve(null);const r=s?.force===!0;if(t.isMesh===!0){const n=t;if(Array.isArray(n.material)){const i=new Array;for(const l of n.material){const a=this.assignTextureLOD(l,e,s);i.push(a)}return Promise.all(i).then(l=>{const a=new Array;for(const u of l)Array.isArray(u)&&a.push(...u);return a})}else return this.assignTextureLOD(n.material,e,s)}if(t.isMaterial===!0){const n=t,i=[],l=new Array;if(this.trackCurrentMaterialTextureSlots(n),n.uniforms&&(n.isRawShaderMaterial||n.isShaderMaterial===!0)){const a=n;for(const u of Object.keys(a.uniforms)){const h=a.uniforms[u].value;if(h?.isTexture===!0){const c=this.assignTextureLODForSlot(h,e,n,u,r).then(d=>(d&&a.uniforms[u].value!=d&&(a.uniforms[u].value=d,a.uniformsNeedUpdate=!0),d));i.push(c),l.push(u)}}}else for(const a of Object.keys(n)){const u=n[a];if(u?.isTexture===!0){const h=this.assignTextureLODForSlot(u,e,n,a,r);i.push(h),l.push(a)}}return Promise.all(i).then(a=>{const u=new Array;for(let h=0;h<a.length;h++){const c=a[h],d=l[h];c&&c.isTexture===!0?u.push({material:n,slot:d,texture:c,level:e}):u.push({material:n,slot:d,texture:null,level:e})}return u})}if(t instanceof f.Texture||t.isTexture===!0){const n=t;return this.assignTextureLODForSlot(n,e,null,null,r)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){m.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return m.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r,n){if(t?.isTexture!==!0)return Promise.resolve(null);if(r==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!n&&i.level<e))return Promise.resolve(t);if(s&&r){const c=this.getPendingTextureSlotRequest(s,r);if(c&&c.level===e&&c.force===n)return c.promise}const l=s&&r?this.nextTextureSlotRequestId(s,r,e,n):0,a=()=>!s||!r||this.getLatestTextureSlotRequest(s,r)?.id===l,u=()=>a()||this.shouldApplyStaleTextureSlotLOD(s,r,e,n),h=m.getOrLoadLOD(t,e,{isCurrent:u}).then(c=>{if(!a()&&!this.shouldApplyStaleTextureSlotLOD(s,r,e,n))return null;if(Array.isArray(c))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(c?.isTexture===!0){if(c!=t&&s&&r){const d=this.getMaterialTextureSlot(s,r)??t;if(d&&!n){const g=this.getAssignedLODInformation(d);if(g&&g?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",g.level,e,s,d,c),null}this.assignTrackedTextureSlot(s,r,c)}return c}else p=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(c=>(console.error("Error loading LOD",t,c),null));return s&&r&&this.setPendingTextureSlotRequest(s,r,e,n,l,h),h}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const s of Object.keys(e.uniforms)){const r=e.uniforms[s].value;r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,s,r)}return}for(const e of Object.keys(t)){const s=t[e];s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,s)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,s,r){let n=this.latestTextureSlotRequests.get(t);n||(n=new Map,this.latestTextureSlotRequests.set(t,n));const i=++this.textureSlotRequestId;return n.set(e,{id:i,level:s,force:r}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,s,r){if(!t||!e)return!1;const n=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),l=this.getAssignedLODInformation(i)?.level??1/0;return s>=l?!1:r?n?s>=n.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const s=t["LOD:requested level"];if(typeof s!="number")return!1;const r=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<r&&e>=s}static setPendingTextureSlotRequest(t,e,s,r,n,i){let l=this.pendingTextureSlotRequests.get(t);l||(l=new Map,this.pendingTextureSlotRequests.set(t,l));const a={level:s,force:r,id:n,promise:i};l.set(e,a),i.finally(()=>{l.get(e)?.id===n&&l.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const n=t[e];return n?.isTexture===!0?n:null}static setMaterialTextureSlot(t,e,s){const n=t.uniforms?.[e];if(n?.value?.isTexture===!0){n.value=s,t.uniformsNeedUpdate=!0;return}t[e]=s}static assignTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const n=this.getMaterialTextureSlot(t,e);let i=r.get(e);!i&&n?i=this.ensureTrackedTextureSlot(t,e,n):i&&n&&i!==n&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,n)),!(i===s&&n===s)&&(i&&i!==s&&this.releaseTrackedTextureSlot(t,e,i),i!==s&&(this.trackTextureUsage(s),r.set(e,s)),n!==s&&this.setMaterialTextureSlot(t,e,s))}static ensureTrackedTextureSlot(t,e,s){let r=this.trackedTextureSlots.get(t);r||(r=new Map,this.trackedTextureSlots.set(t,r));const n=r.get(e);return n===s?n:(n&&this.releaseTrackedTextureSlot(t,e,n),this.trackTextureUsage(s),r.set(e,s),s)}static releaseTrackedTextureSlot(t,e,s){const r=this.trackedTextureSlots.get(t);if(r?.get(e)===s&&r.delete(e),this.untrackTextureUsage(s)&&(p||ne)){const i=this.getAssignedLODInformation(s);console.log(`[gltf-progressive] Disposed old texture LOD ${i?.level??"?"} for ${t.name||t.type}.${e}`,s.uuid)}}parser;url;constructor(t){const e=t.options.path;p&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[W];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&m.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return p&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[W];if(r){if(!r.lods){p&&console.warn("Texture has no LODs",r);return}let n=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===s&&(n=!0,m.registerTexture(this.url,i,r.lods?.length,s,r));n||this.parser.getDependency("texture",s).then(i=>{i&&m.registerTexture(this.url,i,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[W];if(r&&r.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const i=this.parser.associations.get(n);i?.meshes===s&&m.registerMesh(this.url,r.guid,n,r.lods.length,i.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,n)=>{if(!e){p&&console.error("!! gltf-progressive: Called register texture without texture");return}if(p){const{width:l,height:a}=De(e);console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`,n,e)}e.source&&(e.source[he]=n);const i=n.guid;m.assignLODInformation(t,e,i,s,r),m.lodInfos.set(i,n),m.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,s,r,n,i)=>{const l=s.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+s.name,{index:n,uuid:s.uuid},i,s),m.assignLODInformation(t,l,e,r,n),m.lodInfos.set(e,i);let u=m.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],m.lowresCache.set(e,new WeakRef(u)),r>0&&!ee(s)&&rt(s,l);for(const h of F)h.onRegisteredNewMesh?.(s,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}this.lowresCache.delete(t)}for(const[s,r]of this.cache)s.includes(t)&&(this._disposeCacheEntry(r),this.cache.delete(s))}else{this.cacheGeneration++,this.lodInfos.clear();for(const[,e]of this.lowresCache){const s=e.deref();if(s){if(s.isTexture){const r=s;this.textureRefCounts.delete(r.uuid),r.dispose()}else if(Array.isArray(s))for(const r of s)r.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else this.disposedRequests.add(t),t.then(e=>{if(e)if(Array.isArray(e))for(const s of e)s.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=m.cache.get(t);(p||ne)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(p||ne)&&console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,s+1),p==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${s} → ${s+1}`)}static untrackTextureUsage(t){const e=t.uuid,s=this.textureRefCounts.get(e);if(!s)return(p==="verbose"||ne)&&n("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const r=s-1;if(r<=0)return this.textureRefCounts.delete(e),(p||ne)&&n("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),p==="verbose"&&n("[gltf-progressive] Memory: Untrack texture",r),!1;function n(i,l){let{width:a,height:u}=De(t);const h=a&&u?`${a}x${u}`:"N/A";let c="N/A";a&&u&&(c=`~${(et(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} — ${t.name} ${h} (${c}), refCount: ${s} → ${l}
|
|
3
|
-
${e}`)}}static workers=[];static cacheGeneration=0;static disposedRequests=new WeakSet;static _workersIndex=0;static async getOrLoadLOD(t,e,s){const r=this.cacheGeneration,n=p=="verbose",i=this.getAssignedLODInformation(t);if(!i)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const l=i?.key;let a;if(t.isTexture===!0){const h=t;h.source&&h.source[he]&&(a=h.source[he])}if(a||(a=m.lodInfos.get(l)),!a)p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${l}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let d=!1;const g=Array.isArray(a.lods);if(g&&e>=a.lods.length?d=!0:g||(d=!0),d){const M=this.lowresCache.get(l);if(M){const w=M.deref();if(w)return w;this.lowresCache.delete(l),p&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${l}`)}return null}}const h=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!h)return p&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const c=Je(i.url,h);if(c.endsWith(".glb")||c.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const d=c+"_"+a.guid,g=await this.tryResolveLODCacheEntry(this.cache.get(d),d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(g.found)return g.value;if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale LOD ${e} request before queue: ${c}`),null;const M=await this.queue.slot(c);if(r!==this.cacheGeneration)return null;if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale LOD ${e} request after queue: ${c}`),null;const w=await this.tryResolveLODCacheEntry(this.cache.get(d),d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(w.found)return w.value;const L=this.cache.get(d);if(L!==void 0){const x=await this.tryResolveLODCacheEntry(L,d,c,t,e,n);if(r!==this.cacheGeneration)return null;if(x.found)return x.value}if(!M.use)return p&&console.log(`LOD ${e} was aborted: ${c}`),null;const D=a,T=new Promise(async(x,S)=>{if(ct){const y=await(await lt({})).load(c);if(y.textures.length>0)for(const v of y.textures){let R=v.texture;return m.assignLODInformation(i.url,R,l,e,void 0),t instanceof f.Texture&&(R=this.copySettings(t,R)),R&&(R.guid=D.guid),x(R)}if(y.geometries.length>0){const v=new Array;for(const R of y.geometries){const N=R.geometry;m.assignLODInformation(i.url,N,l,e,R.primitiveIndex),v.push(N)}return x(v)}return x(null)}const I=new Z.GLTFLoader;Me(I),p&&(await new Promise(b=>setTimeout(b,1e3)),n&&console.warn("Start loading (delayed) "+c,D.guid));let P=c;if(D&&Array.isArray(D.lods)){const b=D.lods[e];b.hash&&(P+="?v="+b.hash)}const _=await I.loadAsync(P).catch(b=>(console.error(`Error loading LOD ${e} from ${c}
|
|
4
|
-
`,b),x(null)));if(!_)return x(null);const U=_.parser;n&&console.log("Loading finished "+c,D.guid);let k=0;if(_.parser.json.textures){let b=!1;for(const y of _.parser.json.textures){if(y?.extensions){const v=y?.extensions[W];if(v?.guid&&v.guid===D.guid){b=!0;break}}k++}if(b){let y=await U.getDependency("texture",k);return y&&m.assignLODInformation(i.url,y,l,e,void 0),n&&console.log('change "'+t.name+'" → "'+y.name+'"',c,k,y,d),t instanceof f.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=D.guid),x(y)}else p&&console.warn("Could not find texture with guid",D.guid,_.parser.json)}if(k=0,_.parser.json.meshes){let b=!1;for(const y of _.parser.json.meshes){if(y?.extensions){const v=y?.extensions[W];if(v?.guid&&v.guid===D.guid){b=!0;break}}k++}if(b){const y=await U.getDependency("mesh",k);if(n&&console.log(`Loaded Mesh "${y.name}"`,c,k,y,d),y.isMesh===!0){const v=y.geometry;return m.assignLODInformation(i.url,v,l,e,0),x(v)}else{const v=new Array;for(let R=0;R<y.children.length;R++){const N=y.children[R];if(N.isMesh===!0){const H=N.geometry;m.assignLODInformation(i.url,H,l,e,R),v.push(H)}}return x(v)}}else p&&console.warn("Could not find mesh with guid",D.guid,_.parser.json)}return x(null)});this.cache.set(d,T),M.use(T);const O=await T;return r!==this.cacheGeneration||this.cache.get(d)!==T?null:(O!=null?O instanceof f.Texture?(this.cache.set(d,new WeakRef(O)),m._resourceRegistry.register(O,d)):Array.isArray(O)?this.cache.set(d,Promise.resolve(O)):this.cache.set(d,Promise.resolve(O)):this.cache.set(d,Promise.resolve(null)),O)}else if(t instanceof f.Texture){if(s?.isCurrent?.()===!1)return n&&console.log(`Skipping stale texture LOD ${e} request: ${c}`),null;n&&console.log("Load texture from uri: "+c);const g=await new f.TextureLoader().loadAsync(c);return r!==this.cacheGeneration||s?.isCurrent?.()===!1?(g?.dispose(),null):(g?(g.guid=a.guid,g.flipY=!1,g.needsUpdate=!0,g.colorSpace=t.colorSpace,n&&console.log(a,g)):p&&console.warn("failed loading",c),g)}}return null}static async tryResolveLODCacheEntry(t,e,s,r,n,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${n} was already loading/loaded: ${e}`),t instanceof WeakRef){const u=t.deref();if(u){let h=u,c=!1;if(h instanceof f.Texture&&r instanceof f.Texture?Oe(h.image)||we(h)?h=this.copySettings(r,h):c=!0:h instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(h.attributes.position?.array||(c=!0)),!c)return{found:!0,value:h}}return this.cache.delete(e),p&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let l=await t.catch(u=>(console.error(`Error loading LOD ${n} from ${s}
|
|
5
|
-
`,u),null));if(this.disposedRequests.has(t))return{found:!0,value:null};let a=!1;return l==null||(l instanceof f.Texture&&r instanceof f.Texture?Oe(l.image)||we(l)?l=this.copySettings(r,l):(a=!0,this.cache.delete(e)):l instanceof f.BufferGeometry&&r instanceof f.BufferGeometry&&(l.attributes.position?.array||(a=!0,this.cache.delete(e)))),a?{found:!1}:{found:!0,value:l}}static _queue;static get queue(){return this._queue??=new Ze(_e()?20:50,{debug:p!=!1})}static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const i=new ft(t,s,r,n);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(p==="verbose"&&console.debug(`Copy texture settings
|
|
6
|
-
`,t.uuid,`
|
|
7
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class ft{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class ae{static addPromise=(t,e,s,r)=>{r.forEach(n=>{n.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(n=>{this._resolve=n}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){p&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const r=this._seen.get(e);if(r>=this._maxPromisesPerObject){p&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const G=E("debugprogressive"),dt=G==="colors",ht=E("noprogressive"),ge=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),V=Symbol("Needle:CurrentLOD"),Se=f.THREE,C={mesh_lod:-1,texture_lod:-1},gt=new f.Color,Re=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function pt(){const o=Se.Timer||Se.Clock;return new o}const me=new f.Box3,z=new f.Box3,Ce=new f.Box3,mt=new f.Vector3,yt=new f.Vector3,xt=new f.Matrix4,q=new f.Vector3,Y=new f.Vector3,Q=new f.Vector3,J=new f.Vector3;function Lt(o,t){const e=o.min,s=o.max,r=(e.x+s.x)*.5,n=(e.y+s.y)*.5;return q.set(r,n,e.z).applyMatrix4(t).z<0}function Fe(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:l=-1,xrEnabled:a=!1,debugDrawLine:u,warnMissingPrimitiveDensities:h=!1}=o,c=m.getMeshLODExtension(t)?.lods,d=m.getPrimitiveIndex(t),g=o.target??{level:l,primitiveIndex:d,screenCoverage:0,screenspaceVolume:new f.Vector3,centrality:1};g.level=l,g.primitiveIndex=d,g.screenCoverage=0,g.screenspaceVolume.set(0,0,0),g.centrality=1;let M=o.boundingBox??t.boundingBox;if(M||(t.computeBoundingBox(),M=t.boundingBox),!M)return g;if(me.copy(M).applyMatrix4(e),s.isPerspectiveCamera&&Lt(me,r))return g.level=0,g.screenCoverage=1/0,g.screenspaceVolume.set(1/0,1/0,1/0),g;if(z.copy(me).applyMatrix4(r),a&&s.isPerspectiveCamera&&s.fov>70){const x=z.min,S=z.max;let I=x.x,P=x.y,_=S.x,U=S.y;const k=2,b=1.5,y=(x.x+S.x)*.5,v=(x.y+S.y)*.5;I=(I-y)*k+y,P=(P-v)*k+v,_=(_-y)*k+y,U=(U-v)*k+v;const R=I<0&&_>0?0:Math.min(Math.abs(x.x),Math.abs(S.x)),N=P<0&&U>0?0:Math.min(Math.abs(x.y),Math.abs(S.y)),H=Math.max(R,N);g.centrality=(b-H)*(b-H)*(b-H)}const w=z.getSize(mt);w.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&w.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(w.x*=s.aspect),Ce.copy(M).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Ce.getSize(yt),D=Math.max(w.x,w.y),T=Math.max(L.x,L.y);D!==0&&T!==0&&(w.z=L.z/T*D);const O=Math.max(w.x,w.y,w.z)*g.centrality;if(g.screenCoverage=O,g.screenspaceVolume.copy(w),O<=0)return g;if(u){const x=xt.copy(r);x.invert(),q.copy(z.min),Y.copy(z.max),Y.x=q.x,Q.copy(z.max),Q.y=q.y,J.copy(z.max);const S=(q.z+J.z)*.5;q.z=Y.z=Q.z=J.z=S,q.applyMatrix4(x),Y.applyMatrix4(x),Q.applyMatrix4(x),J.applyMatrix4(x),u(q,Y,255),u(q,Q,255),u(Y,J,255),u(Q,J,255)}if(c?.length)for(let x=0;x<c.length;x++){const S=c[x],I=S.densities?.[d]||S.density||1e-5;if(d>0&&h&&Be()&&!S.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),I/O<n){g.level=x;break}}return g}let ie=class j{static debugDrawLine;static getObjectLODState(t){return t[pe]}static addPlugin(t){F.push(t)}static removePlugin(t){const e=F.indexOf(t);e>=0&&F.splice(e,1)}static getPlugins(){return F}static get(t,e){if(t[ge])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ge];const s=new j(t,{engine:"unknown",...e});return t[ge]=s,s}renderer;context;projectionScreenMatrix=new f.Matrix4;get plugins(){return F}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new ae(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const n=this._newPromiseGroups.indexOf(s);n>=0&&(this._newPromiseGroups.splice(n,1),Be()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}trackLoadingPromise(t,e,s){return ae.addPromise(t,e,s,this._newPromiseGroups),s}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let s=!1;if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&(this._lodchangedlisteners.splice(r,1),s=!0)}return s}constructor(t,e){this.renderer=t,this.context={...e}}#t;#r=0;#n=0;#i=0;#s=0;#o=pt();_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ce(this.renderer),this.renderer.render=function(s,r){const n=e.renderer.getRenderTarget();(n==null||"isXRRenderTarget"in n&&n.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o.update?.(),e.#n=Math.max(e.#o.getDelta(),1/1e3),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,G&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,s){if(this.pause)return;const r=this.getRenderList(t,e,s);if(!r)return;const n=r.opaque;let i=!0;if(n.length===1){const l=n[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(ht||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,G&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,G&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.getRenderList(t,e,0);if(!s)return;const r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const a of r){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){G&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const i=s.transparent;for(const a of i){const u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const l=s.transmissive;for(const a of l){const u=a.object;(u instanceof f.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}}getRenderList(t,e,s){const r=this.renderer;let n=null;if(r.isWebGPURenderer===!0){const i=r._renderLists;if(!i)return null;n=i.get(t,e)}else if(r.isWebGLRenderer===!0){const i=r.renderLists;if(!i)return null;n=i.get(t,s)}return n?{opaque:n.opaque||[],transparent:n.transparent||[],transmissive:n.transmissive||n.transparentDoublePass||[],transparentDoublePass:n.transparentDoublePass||[]}:null}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[pe];if(n||(n=new wt,s[pe]=n),n.frames++<2)return;for(const l of F)l.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:X;i>=0?(C.mesh_lod=i,C.texture_lod=i):(this.calculateLodLevel(e,s,n,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,i),p&&s.material&&!s.isGizmo&&qe(s.material),dt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&We(s.material,C.mesh_lod);for(const l of F)l.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);n.lastLodLevel_Mesh=C.mesh_lod,n.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,s);return}let r=!1;(t[V]===void 0||e<t[V])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[V]!=s,e=s),r){t[V]=e;const i=n?{force:!0}:void 0,l=m.assignTextureLOD(t,e,i).then(a=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});ae.addPromise("texture",t,l,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[V]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[V]!=r,e=r),s){t[V]=e;const n=t.geometry,i=m.assignMeshLOD(t,e).then(l=>(l&&t[V]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return ae.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new f.Sphere;_tempWorldPosition=new f.Vector3;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,n){if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let l=10+1,a=!1;if(G&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,h=m.getPrimitiveIndex(e.geometry),c=u&&u.length>0,d=m.getMaterialMinMaxLODsCount(e.material),g=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!g){n.mesh_lod=0,n.texture_lod=0;return}c||(a=!0,l=0);const M=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!L[j.$skinnedMeshBoundsOffset]){const T=j.skinnedMeshBoundsFrameOffsetCounter++;L[j.$skinnedMeshBoundsOffset]=T}const D=L[j.$skinnedMeshBoundsOffset];if((s.frames+D)%this.skinnedMeshAutoUpdateBoundsInterval===0){const T=ee(L),O=L.geometry;T&&(L.geometry=T),L.computeBoundingBox(),L.geometry=O}}w=L.boundingBox}if(w){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const T=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(T)){n.mesh_lod=0,n.texture_lod=0;return}}const L=Fe({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:M,currentLevel:s.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:G?j.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=L.centrality,s.lastScreenCoverage=L.screenCoverage,s.lastScreenspaceVolume.copy(L.screenspaceVolume),L.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}L.level>=0&&L.level<l&&(l=L.level,a=!0)}if(a?n.mesh_lod=l:n.mesh_lod=s.lastLodLevel_Mesh,G&&n.mesh_lod!=s.lastLodLevel_Mesh){const D=u?.[n.mesh_lod];D&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${D.densities?.[h].toFixed(0)}) | ${e.name}`)}if(g){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=d.max_count-1,G){const D=d.lods[d.max_count-1];G&&console.log(`First Texture LOD ${n.texture_lod} (${D.max_height}px) - ${e.name}`)}}else{const D=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let T=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(T*=1.5);const O=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,S=M/O*T;let I=!1;for(let P=d.lods.length-1;P>=0;P--){const _=d.lods[P];if(!(L&&_.max_height>=2048)&&!(_e()&&_.max_height>4096)&&(_.max_height>S||!I&&P===0)){if(I=!0,n.texture_lod=P,G&&n.texture_lod<s.lastLodLevel_Texture){const U=_.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${U}px
|
|
8
|
-
Screensize: ${S.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${D.toFixed(1)}
|
|
9
|
-
${e.name}`)}break}}}}else n.texture_lod=0}};class wt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new f.Vector3;lastCentrality=0}function We(o,t){if(!(t<0)){if(Array.isArray(o)){for(const e of o)We(e,t);return}"color"in o&&o.color instanceof f.Color&&(o.color.copy(Ue(t,gt)),o.needsUpdate=!0)}}function Ue(o,t){const e=Math.max(0,Math.min(Re.length-1,Math.floor(o)));return t.setHex(Re[e])}const Pe=Symbol("NEEDLE_mesh_lod"),le=Symbol("NEEDLE_texture_lod");let ye=null;function Ve(){const o=Dt();o&&(o.mapURLs(function(t){return ke(),t}),ke(),ye?.disconnect(),ye=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&ze(s)})})}),ye.observe(document,{childList:!0,subtree:!0}))}function Dt(){if(typeof customElements>"u")return null;const o=customElements.get("model-viewer");return o||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ve()}),null)}function ke(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{ze(t)})}const Ae=new WeakSet;let vt=0;function ze(o){if(!o||Ae.has(o))return null;Ae.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++vt+`
|
|
10
|
-
`,o.getAttribute("src"));let t=null,e=null,s=null;for(let r=o;r!=null;r=Object.getPrototypeOf(r)){const n=Object.getOwnPropertySymbols(r),i=n.find(u=>u.toString()=="Symbol(renderer)"),l=n.find(u=>u.toString()=="Symbol(scene)"),a=n.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&l!=null&&(e=o[l]),!s&&a!=null&&(s=o[a])}if(t&&e){let r=function(){if(s){let i=0;const l=setInterval(()=>{if(i++>5){clearInterval(l);return}s?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=ie.get(t,{engine:"model-viewer"});return ie.addPlugin(new Mt),n.enable(),n.addEventListener("changed",()=>{s?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&s?.call(o)}),o.addEventListener("load",()=>{r()}),()=>{n.disable()}}return null}class Mt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[le]==!0)return;e[le]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(n&&s&&e.material){let i=function(a){if(a[le]==!0)return;a[le]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let h=0;h<u.length;h++){const c=u[h],d=a[c];if(d?.isTexture===!0){const g=d.userData?.associations?.textures;if(g==null)continue;const M=s.parser.json.textures[g];if(!M){console.warn("Texture data not found for texture index "+g);continue}if(M?.extensions?.[W]){const w=M.extensions[W];w&&n&&m.registerTexture(n,d,w.lods.length,g,w)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)i(a);else i(l)}}tryParseMeshLOD(t,e){if(e[Pe]==!0)return;e[Pe]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const n=e.userData?.gltfExtensions?.[W];if(n&&r){const i=e.uuid;m.registerMesh(r,i,e,0,n.lods.length,n)}}}function Tt(...o){let t,e,s,r;switch(o.length){case 2:[s,e]=o,r={};break;case 3:[s,e,r]=o;break;case 4:[t,e,s,r]=o;break;default:throw new Error("Invalid arguments")}ce(e),Me(s),Te(s,{progressive:!0,...r?.hints}),s.register(i=>new m(i));const n=ie.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}Ve();if(!st){const o={gltfProgressive:{useNeedleProgressive:Tt,LODsManager:ie,configureLoader:Te,getRaycastMesh:ee,useRaycastMeshes:nt}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.LODsManager=ie;exports.NEEDLE_progressive=m;exports.addDracoAndKTX2Loaders=Me;exports.calculateMeshLODLevel=Fe;exports.configureLoader=Te;exports.createLoaders=ce;exports.getLODColor=Ue;exports.getRaycastMesh=ee;exports.setDracoDecoderLocation=Ie;exports.setKTX2TranscoderLocation=Ge;
|