@needle-tools/engine 4.16.11 → 4.16.12
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 +5 -0
- package/SKILL.md +64 -4
- package/dist/gltf-progressive-DLDvZ8Xm.umd.cjs +10 -0
- package/dist/{gltf-progressive-CzxjNmG6.js → gltf-progressive-DMK8kKVB.js} +352 -346
- package/dist/gltf-progressive-RPgYR6rv.min.js +10 -0
- package/dist/{needle-engine.bundle-Du3vf5L1.js → needle-engine.bundle-BfsnYTeP.js} +6960 -6823
- package/dist/{needle-engine.bundle-DOChN3At.umd.cjs → needle-engine.bundle-COr8D1vc.umd.cjs} +130 -130
- package/dist/{needle-engine.bundle-fP5tOMI1.min.js → needle-engine.bundle-DG1sGkxn.min.js} +131 -131
- package/dist/needle-engine.d.ts +11 -1
- package/dist/needle-engine.js +3 -3
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/three-examples.js +52 -52
- package/dist/three-examples.min.js +4 -4
- package/dist/three-examples.umd.cjs +2 -2
- package/lib/engine/api.d.ts +2 -1
- package/lib/engine/api.js +1 -6
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +32 -1
- package/lib/engine/engine_addressables.js +71 -9
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_application.d.ts +2 -0
- package/lib/engine/engine_application.js +7 -3
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_context.js +25 -1
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_input.js +1 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +1 -1
- package/lib/engine/engine_license.js +31 -5
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +20 -2
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_networking_streams.d.ts +5 -1
- package/lib/engine/engine_networking_streams.js +64 -17
- package/lib/engine/engine_networking_streams.js.map +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +5 -0
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/fonts.d.ts +1 -1
- package/lib/engine/webcomponents/fonts.js +7 -0
- package/lib/engine/webcomponents/fonts.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +8 -1
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +31 -13
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
- package/lib/engine/webcomponents/needle-engine.js +16 -4
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.d.ts +2 -0
- package/lib/engine/webcomponents/needle-engine.loading.js +10 -0
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/package.json +2 -2
- package/src/engine/api.ts +3 -1
- package/src/engine/engine_addressables.ts +75 -9
- package/src/engine/engine_application.ts +7 -2
- package/src/engine/engine_context.ts +25 -1
- package/src/engine/engine_input.ts +2 -2
- package/src/engine/engine_license.ts +28 -5
- package/src/engine/engine_lifecycle_functions_internal.ts +21 -2
- package/src/engine/engine_networking_streams.ts +64 -19
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +8 -1
- package/src/engine/webcomponents/fonts.ts +6 -0
- package/src/engine/webcomponents/icons.ts +9 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +30 -12
- package/src/engine/webcomponents/needle-engine.loading.ts +13 -1
- package/src/engine/webcomponents/needle-engine.ts +15 -5
- package/dist/gltf-progressive-BacJPTD6.umd.cjs +0 -10
- package/dist/gltf-progressive-BmXoz_HR.min.js +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ All notable changes to this package will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [4.16.12] - 2026-09-09
|
|
8
|
+
- Fix: Release scene resources, asset caches, event listeners, and loading UI when disposing a context or removing a needle-engine element.
|
|
9
|
+
- Fix: Prevent pending progressive LOD loads from restoring disposed resources.
|
|
10
|
+
- Fix: Release geometry retained by BVH worker callbacks after completed or failed jobs.
|
|
11
|
+
|
|
7
12
|
## [4.16.11] - 2026-06-04
|
|
8
13
|
- Fix: iOS audio — `AudioSource` playback now survives a device lock / audio-session interruption and resumes automatically on unlock
|
|
9
14
|
- Fix: `typeof window`/`globalThis !== undefined` guards were always true and could throw outside a browser (SSR / Node)
|
package/SKILL.md
CHANGED
|
@@ -13,7 +13,10 @@ description: >
|
|
|
13
13
|
or error without mentioning Needle Engine — check if @needle-tools/engine is in
|
|
14
14
|
package.json or imports. If the project uses Needle Engine, always load this skill.
|
|
15
15
|
compatibility:
|
|
16
|
-
- optional:
|
|
16
|
+
- optional: >
|
|
17
|
+
needle_search MCP tool (search Needle Engine docs, forum posts, and community answers).
|
|
18
|
+
Without it, the same corpus is reachable over HTTP at
|
|
19
|
+
https://search.needle.tools/api/semantic-search?q=... (public, no key) — see references/mcp.md
|
|
17
20
|
---
|
|
18
21
|
|
|
19
22
|
# Needle Engine
|
|
@@ -47,6 +50,33 @@ export class HelloWorld extends Behaviour {
|
|
|
47
50
|
|
|
48
51
|
> ⚠️ **TypeScript config required:** `tsconfig.json` must have `"experimentalDecorators": true` and `"useDefineForClassFields": false` for decorators to work. Without `useDefineForClassFields: false`, TypeScript overwrites `@serializable()` properties with their default values *after* the decorator runs, silently breaking deserialization.
|
|
49
52
|
|
|
53
|
+
On **5.1+**, scenes expose auto-generated typed bindings, and `needle` gives you the context from
|
|
54
|
+
anywhere — often shorter than `getComponent` lookups:
|
|
55
|
+
```ts
|
|
56
|
+
import { needle, onStart } from "@needle-tools/engine";
|
|
57
|
+
|
|
58
|
+
// `needle` resolves to the current context when the handler runs — no wrapper needed
|
|
59
|
+
button.onclick = () => {
|
|
60
|
+
needle.sceneData.MyScene.MainCamera.$components.OrbitControls.autoRotate = false;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Without it, you'd wrap the wiring in onStart purely to capture a ctx:
|
|
64
|
+
onStart(ctx => {
|
|
65
|
+
button.onclick = () => {
|
|
66
|
+
ctx.sceneData.MyScene.MainCamera.$components.OrbitControls.autoRotate = false;
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
`onStart(ctx => …)` is still the right tool for setup that must run once the scene is ready —
|
|
71
|
+
`needle` is for reaching the context from code that runs later.
|
|
72
|
+
> ⚠️ **Don't touch `needle` at module top-level.** It's a lazy Proxy resolving to the *current*
|
|
73
|
+
> context — module bodies evaluate before any scene exists, so it logs an error and returns a
|
|
74
|
+
> no-op proxy. Use it inside callbacks and event handlers; use `onStart(ctx => …)` for setup.
|
|
75
|
+
> (Importing it at top level is fine, including under SSR — only *access* is the problem.)
|
|
76
|
+
|
|
77
|
+
Components need `$components`; bare names are child nodes. Both APIs are experimental — see
|
|
78
|
+
[What's New](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/whats-new.md).
|
|
79
|
+
|
|
50
80
|
---
|
|
51
81
|
|
|
52
82
|
## Key Concepts
|
|
@@ -137,7 +167,9 @@ Boolean attributes can be disabled with `="0"` (e.g. `camera-controls="0"`).
|
|
|
137
167
|
| `background-color` | Hex or RGB background color (e.g. `#ff0000`) |
|
|
138
168
|
| `background-image` | Skybox URL or preset: `studio`, `blurred-skybox`, `quicklook`, `quicklook-ar` |
|
|
139
169
|
| `background-blurriness` | Blur intensity for background (0–1) |
|
|
170
|
+
| `background-rotation` | Rotate the background skybox (5.1+) |
|
|
140
171
|
| `environment-image` | Environment lighting image URL or preset (same presets as `background-image`) |
|
|
172
|
+
| `environment-rotation` | Rotate the environment lighting (5.1+) |
|
|
141
173
|
| `contactshadows` | Enable contact shadows |
|
|
142
174
|
| `tone-mapping` | `none`, `linear`, `neutral`, `agx` |
|
|
143
175
|
| `poster` | Placeholder image URL shown while loading |
|
|
@@ -245,6 +277,13 @@ export default defineConfig(async ({ command }) => ({
|
|
|
245
277
|
}));
|
|
246
278
|
```
|
|
247
279
|
|
|
280
|
+
As of 5.1, `needlePlugins()` resolves the Vite command itself, so it can be called with no
|
|
281
|
+
arguments and without `await`/spread — handy when composing with other framework plugins:
|
|
282
|
+
```js
|
|
283
|
+
export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
|
|
284
|
+
```
|
|
285
|
+
Still pass `(command, config, settings)` when you need options like `makeFilesLocal`.
|
|
286
|
+
|
|
248
287
|
---
|
|
249
288
|
|
|
250
289
|
## `needle.config.json`
|
|
@@ -375,7 +414,9 @@ Use this when you need exact method signatures, constructor parameters, or prope
|
|
|
375
414
|
|
|
376
415
|
## Searching the Documentation
|
|
377
416
|
|
|
378
|
-
|
|
417
|
+
Search the docs *before* guessing at API details — they are the source of truth. The corpus covers Needle Engine documentation, the API reference, the community forum, Discord, and Needle source code.
|
|
418
|
+
|
|
419
|
+
**If the `needle_search` MCP tool is available, use it:**
|
|
379
420
|
|
|
380
421
|
```
|
|
381
422
|
needle_search("how to play animation clip from code")
|
|
@@ -383,12 +424,25 @@ needle_search("SyncedTransform multiplayer")
|
|
|
383
424
|
needle_search("deploy to Needle Cloud CI")
|
|
384
425
|
```
|
|
385
426
|
|
|
386
|
-
|
|
427
|
+
**Otherwise, hit the public search API — no key, no setup:**
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
curl -s -H "Accept: application/json" \
|
|
431
|
+
"https://search.needle.tools/api/semantic-search?q=how+to+play+animation+clip+from+code&limit=5"
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Returns JSON `{ query, results: [{ title, source, content, url, score, truncated }], durationMs }`. Optional `limit` (1–20, default 10) and `max_chars` (200–10000, default 2000). Rate-limited to 10 requests/minute for unauthenticated callers.
|
|
435
|
+
|
|
436
|
+
Both are **semantic** search — phrase queries as full questions ("how do I make an object follow the camera in VR"), not keyword soup ("vr camera follow"). Result `url`s include a `#:~:text=` fragment linking to the exact passage; pass them on to the user.
|
|
437
|
+
|
|
438
|
+
See 🔌 [MCP & Search API](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/mcp.md) for the full tool inventory — project file access, live scene inspection via the Needle Inspector, Unity/Blender tools — and for MCP server setup.
|
|
387
439
|
|
|
388
440
|
---
|
|
389
441
|
|
|
390
442
|
## Common Gotchas
|
|
391
443
|
|
|
444
|
+
- **Scene Bindings: bare names are child nodes, components need `$components`.** `ctx.sceneData.MyGlb.Camera.OrbitControls` looks for a child *object* named OrbitControls; the component is `ctx.sceneData.MyGlb.Camera.$components.OrbitControls`. Worse, misses don't throw — they return an error proxy that swallows every get/set and only warns. If a scene-bindings assignment appears to run but changes nothing, check the console for `[SceneData]`.
|
|
445
|
+
- **`autoCleanup`'s teardown timing depends on where you call it.** In `onEnable` → cleaned on disable. In `awake`/`start` → cleaned on destroy, so it survives disable/enable. Registering a subscription in `start()` and expecting it to stop when the component is disabled is the common mistake.
|
|
392
446
|
- **`obj.visible = false` disables components!** Setting `visible = false` on a parent disables the entire hierarchy including component lifecycle (SyncedTransform, etc.) — like Unity's `setActive`. To hide visually but keep components running, hide child meshes instead: `obj.traverse(c => { if (c.isMesh) c.visible = false; })`. Or use `Renderer.setVisible(obj, false)` which only affects rendering.
|
|
393
447
|
- `@registerType` is required or the component won't be instantiated from GLB. Unity/Blender export adds this automatically via codegen; hand-written components need it explicitly.
|
|
394
448
|
- GLB assets go in `assets/`, static files (fonts, images, videos) in `public/` (configurable via `needle.config.json`)
|
|
@@ -406,6 +460,7 @@ Use this *before* guessing at API details — the docs are the source of truth.
|
|
|
406
460
|
- WebXR requires HTTPS — the Needle project templates include a local HTTPS dev server by default. Use `--host` when running the dev server (e.g. `npx vite --host`) to expose it on your local network IP, allowing you to test on phones/headsets via QR code
|
|
407
461
|
- **Avoid unnecessary allocations.** Do NOT write `obj.worldPosition.clone()` or `new Vector3()` in per-frame code. The `world___` getters (`worldPosition`, `worldQuaternion`, `worldScale`) return temp vectors that can be read directly and re-assigned (`obj.worldPosition = otherObj.worldPosition`). When you need a temporary vector for math, use `getTempVector()` / `getTempQuaternion()` from `@needle-tools/engine` — these come from a circular buffer with zero GC pressure. Only use `.clone()` when you truly need to store a value across frames.
|
|
408
462
|
- **NEVER import from `@needle-tools/engine` subpaths** like `@needle-tools/engine/lib/...` or `@needle-tools/engine/src/...`. These are internal paths that break across versions. Everything is exported from the package root: `import { NEEDLE_ENGINE_MODULES, Rigidbody, BloomEffect, ... } from "@needle-tools/engine"`. The only exception is the vite plugin: `import { needlePlugins } from "@needle-tools/engine/vite"`.
|
|
463
|
+
- **Optimization & compression run at build time (not a scene upload).** Texture (KTX2/WebP) and mesh (Draco/Meshopt) compression, progressive loading, and automatic LODs are applied during the production build (Compression & LOD Settings component). It's **license-gated**: sign in to Needle Cloud (`npx needle-cloud login`; the Unity/Blender integrations handle this) or, for CI, set `NEEDLE_CLOUD_TOKEN` to an `nc_*` access token. It's a license check, not a scene upload. Details: [Optimization & Compression](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/optimization.md).
|
|
409
464
|
|
|
410
465
|
---
|
|
411
466
|
|
|
@@ -413,6 +468,7 @@ Use this *before* guessing at API details — the docs are the source of truth.
|
|
|
413
468
|
|
|
414
469
|
Read these **only when needed** — don't load them all upfront:
|
|
415
470
|
|
|
471
|
+
- 🆕 [What's New (5.1 → 6.0)](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/whats-new.md) — Scene Bindings (`ctx.sceneData`), `needle` shorthand, `Context.events`, `autoCleanup`, builder APIs, `<needle-app>`, GaussianSplat. **Check this before assuming an API doesn't exist** — much of it postdates the rest of these references.
|
|
416
472
|
- 📖 [Core API](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/api.md) — lifecycle, decorators, context (input, physics, time), gameobject, coroutines, asset loading, renderer/materials, async modules
|
|
417
473
|
- 🧩 [Components](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/components.md) — animation, audio, video, lighting, camera, scene switching, interaction, splines, particles, debug tools
|
|
418
474
|
- ⚡ [Physics](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/physics.md) — colliders, Rigidbody (forces, velocity, impulse), raycasting, async Rapier loading
|
|
@@ -420,14 +476,18 @@ Read these **only when needed** — don't load them all upfront:
|
|
|
420
476
|
- 🌐 [Networking](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/networking.md) — connection API, SyncedRoom, PlayerSync, @syncField, SyncedTransform, Voip, ScreenCapture, guid persistence
|
|
421
477
|
- 🥽 [WebXR](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/xr.md) — VR/AR sessions, XRRig, controllers, pointer events in XR, image tracking, depth sensing, camera access, mesh detection, DOM overlay, iOS AR, multiplayer avatars
|
|
422
478
|
- 🚀 [Deployment](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/deployment.md) — Needle Cloud (GitHub Actions, CLI), Vercel, Netlify, other platforms
|
|
479
|
+
- 📦 [Optimization & Compression](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/optimization.md) — build-time (local) texture/mesh compression (KTX2, Draco/Meshopt), progressive loading, automatic LODs — **not** a cloud upload
|
|
423
480
|
- 🔗 [Framework Integration](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/integration.md) — React, Svelte, Vue, Next.js, SvelteKit patterns, CDN with import maps
|
|
424
481
|
- 💡 [Component Examples](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/examples.md) — practical examples: click handling, runtime loading, networking, materials, code-only scenes, input, coroutines
|
|
425
482
|
- 🐛 [Troubleshooting](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/troubleshooting.md) — error messages, unexpected behavior, build failures, **runtime logs at `node_modules/.needle/logs/`**, build info
|
|
483
|
+
- 🔌 [MCP & Search API](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/references/mcp.md) — `needle_search`, the public search HTTP API, MCP server setup, project file tools, live scene inspection via the Needle Inspector, Unity/Blender tools
|
|
426
484
|
- 🧩 [Component Template](https://raw.githubusercontent.com/needle-tools/ai/refs/heads/main/providers/claude/plugin/skills/needle-engine/templates/my-component.ts) — annotated starting point for new components
|
|
427
485
|
|
|
428
486
|
## Important URLs
|
|
429
487
|
|
|
430
|
-
- Docs: https://engine.needle.tools/docs/
|
|
488
|
+
- Docs: https://engine.needle.tools/docs/ (any page also available as markdown — swap `.html` for `.md`)
|
|
489
|
+
- Search API: https://search.needle.tools/api/semantic-search?q=your+question ([API reference](https://search.needle.tools/api-docs))
|
|
490
|
+
- AI & MCP docs: https://engine.needle.tools/docs/ai/
|
|
431
491
|
- Samples: https://engine.needle.tools/samples/
|
|
432
492
|
- Samples index (all official samples with source): https://github.com/needle-tools/needle-engine-samples/blob/main/samples.json
|
|
433
493
|
- GitHub: https://github.com/needle-tools/needle-engine-support
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";const c=require("./three.umd.cjs"),X=require("./three-examples.umd.cjs");var le=typeof document<"u"?document.currentScript:null;const $e="";globalThis.GLTF_PROGRESSIVE_VERSION=$e;console.debug("[gltf-progressive] version -");let B="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",N="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Be=B,Ge=N,Oe=new URL(B+"draco_decoder.js");Oe.searchParams.append("range","true");fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${B} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),B===Be&&Se("./include/draco/"),N===Ge&&Te("./include/ktx2/")}).finally(()=>{Pe()});const Fe=()=>({dracoDecoderPath:B,ktx2TranscoderPath:N});function Se(i){B=i,k&&k[pe]!=B?(console.debug("Updating Draco decoder path to "+i),k[pe]=B,k.setDecoderPath(B),k.preload()):console.debug("Setting Draco decoder path to "+i)}function Te(i){N=i,G&&G.transcoderPath!=N?(console.debug("Updating KTX2 transcoder path to "+i),G.setTranscoderPath(N),G.init()):console.debug("Setting KTX2 transcoder path to "+i)}function oe(i){return Pe(),i?G.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:k,ktx2Loader:G,meshoptDecoder:ne}}function xe(i){i.dracoLoader||i.setDRACOLoader(k),i.ktx2Loader||i.setKTX2Loader(G),i.meshoptDecoder||i.setMeshoptDecoder(ne)}const pe=Symbol("dracoDecoderPath");let k,ne,G;function Pe(){k||(k=new X.DRACOLoader,k[pe]=B,k.setDecoderPath(B),k.setDecoderConfig({type:"js"}),k.preload()),G||(G=new X.KTX2Loader,G.setTranscoderPath(N),G.init()),ne||(ne=X.MeshoptDecoder)}const me=new WeakMap;function we(i,t){let e=me.get(i);e?e=Object.assign(e,t):e=t,me.set(i,e)}const Ue=X.GLTFLoader.prototype.load;function We(...i){const t=me.get(this);let e=i[0];const s=new URL(e,window.location.href);if(s.hostname.endsWith("needle.tools")){const n=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";n?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=s.toString()}return i[0]=e,Ue?.call(this,...i)}X.GLTFLoader.prototype.load=We;q("debugprogressive");function q(i){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(i);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function ze(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const s=i.substring(0,e+1);for(;s.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return s+t}return t}function Le(){return Y!==void 0||(Y=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),q("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",Y)),Y}let Y;function Me(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class qe{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_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 Ve(i){const t=i.image?.width??0,e=i.image?.height??0,s=i.image?.depth??1,r=Math.floor(Math.log2(Math.max(t,e,s)))+1,n=Ne(i);return t*e*s*n*(1-Math.pow(.25,r))/(1-.25)}function Ne(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let s=1;const r=i.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 Ee=typeof window>"u"&&typeof document>"u",ye=Symbol("needle:raycast-mesh");function j(i){return i?.[ye]instanceof c.BufferGeometry?i[ye]:null}function Xe(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!j(i)){const s=Ke(t);s.userData={isRaycastMesh:!0},i[ye]=s}}function je(i=!0){if(i){if(H)return;const t=H=c.Mesh.prototype.raycast;c.Mesh.prototype.raycast=function(e,s){const r=this,n=j(r);let o;n&&r.isMesh&&(o=r.geometry,r.geometry=n),t.call(this,e,s),o&&(r.geometry=o)}}else{if(!H)return;c.Mesh.prototype.raycast=H,H=null}}let H=null;function Ke(i){const t=new c.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const z=new Array,g=q("debugprogressive");let J,V=-1;if(g){let i=function(){V+=1,V>=t&&(V=-1),console.log(`Toggle LOD level [${V}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(J=!J,console.log(`Toggle wireframe [${J}]`));const s=parseInt(e.key);!isNaN(s)&&s>=0&&(V=s,console.log(`Set LOD level to [${V}]`))})}function Ce(i){if(g&&J!==void 0)if(Array.isArray(i))for(const t of i)Ce(t);else i&&"wireframe"in i&&(i.wireframe=J===!0)}const Q=new Array;let Ye=0;const He=Le()?2:10;function Qe(i){if(Q.length<He){const s=Q.length;g&&console.warn(`[Worker] Creating new worker #${s}`);const r=_e.createWorker(i||{});return Q.push(r),r}const t=Ye++%Q.length;return Q[t]}class _e{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){Je(r.result),n.resolve(r.result);const o=n.url;o.startsWith("blob:")&&URL.revokeObjectURL(o)}}},t.onerror=s=>{console.error("[Worker] Error in gltf-progressive worker:",s)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/loader.worker-BqODMeeW.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:le&&le.tagName.toUpperCase()==="SCRIPT"&&le.src||new URL("gltf-progressive-DLDvZ8Xm.umd.cjs",document.baseURI).href),{type:"module"});return new _e(e,t)}_running=[];_webglRenderer=null;async load(t,e){const s=Fe();let r=e?.renderer;r||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await Promise.resolve().then(()=>require("./three.umd.cjs")).then(d=>d.THREE);return new u})(),r=await this._webglRenderer);const a=oe(r).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const l={type:"load",url:t,dracoDecoderPath:s.dracoDecoderPath,ktx2TranscoderPath:s.ktx2TranscoderPath,ktx2LoaderConfig:a};return this._debug&&console.debug("[Worker] Sending load request",l),this.worker.postMessage(l),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function Je(i){for(const t of i.geometries){const e=t.geometry,s=new c.BufferGeometry;if(s.name=e.name||"",e.index){const r=e.index;s.setIndex(ue(r))}for(const r in e.attributes){const n=e.attributes[r],o=ue(n);s.setAttribute(r,o)}if(e.morphAttributes)for(const r in e.morphAttributes){const o=e.morphAttributes[r].map(a=>ue(a));s.morphAttributes[r]=o}if(s.morphTargetsRelative=e.morphTargetsRelative??!1,s.boundingBox=new c.Box3,s.boundingBox.min=new c.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),s.boundingBox.max=new c.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),s.boundingSphere=new c.Sphere(new c.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 i.textures){const e=t.texture;let s=null;if(e.isCompressedTexture){const r=e.mipmaps,n=e.image?.width||e.source?.data?.width||-1,o=e.image?.height||e.source?.data?.height||-1;s=new c.CompressedTexture(r,n,o,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else s=new c.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 c.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 i}function ue(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,s=e.array,r=new c.InterleavedBuffer(s,e.stride);t=new c.InterleavedBufferAttribute(r,i.itemSize,s.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new c.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const Ze=q("gltf-progressive-worker");q("gltf-progressive-reduce-mipmaps");const E=q("gltf-progressive-gc"),ce=Symbol("needle-progressive-texture"),U="NEEDLE_progressive";class y{get name(){return U}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 a of t)this.getMaterialMinMaxLODsCount(a,e);return t[r]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;u?.isTexture===!0&&o(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];l?.isTexture===!0&&o(l,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[r]=e,e;function o(a,l){const u=s.getAssignedLODInformation(a);if(u){const d=s.lodInfos.get(u.key);if(d&&d.lods){l.min_count=Math.min(l.min_count,d.lods.length),l.max_count=Math.max(l.max_count,d.lods.length);for(let p=0;p<d.lods.length;p++){const x=d.lods[p];x.width&&(l.lods[p]=l.lods[p]||{min_height:1/0,max_height:0},l.lods[p].min_height=Math.min(l.lods[p].min_height,x.height),l.lods[p].max_height=Math.max(l.lods[p].max_height,x.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 o=t[n];if(o&&o.isTexture&&this.hasLODLevelAvailable(o,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){if(!t)return Promise.resolve(null);if(t instanceof c.Mesh||t.isMesh===!0){const s=t.geometry,r=this.getAssignedLODInformation(s);if(!r)return Promise.resolve(null);for(const n of z)n.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,y.getOrLoadLOD(s,e).then(n=>{if(Array.isArray(n)){const o=r.index||0;n=n[o]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],n&&s!=n&&(n?.isBufferGeometry?t.geometry=n:g&&console.error("Invalid LOD geometry",n))),n}).catch(n=>(console.error("Error loading mesh LOD",t,n),null))}else g&&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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const s=t;if(Array.isArray(s.material)){const r=new Array;for(const n of s.material){const o=this.assignTextureLOD(n,e);r.push(o)}return Promise.all(r).then(n=>{const o=new Array;for(const a of n)Array.isArray(a)&&o.push(...a);return o})}else return this.assignTextureLOD(s.material,e)}if(t.isMaterial===!0){const s=t,r=[],n=new Array;if(s.uniforms&&(s.isRawShaderMaterial||s.isShaderMaterial===!0)){const o=s;for(const a of Object.keys(o.uniforms)){const l=o.uniforms[a].value;if(l?.isTexture===!0){const u=this.assignTextureLODForSlot(l,e,s,a).then(d=>(d&&o.uniforms[a].value!=d&&(o.uniforms[a].value=d,o.uniformsNeedUpdate=!0),d));r.push(u),n.push(a)}}}else for(const o of Object.keys(s)){const a=s[o];if(a?.isTexture===!0){const l=this.assignTextureLODForSlot(a,e,s,o);r.push(l),n.push(o)}}return Promise.all(r).then(o=>{const a=new Array;for(let l=0;l<o.length;l++){const u=o[l],d=n[l];u&&u.isTexture===!0?a.push({material:s,slot:d,texture:u,level:e}):a.push({material:s,slot:d,texture:null,level:e})}return a})}if(t instanceof c.Texture||t.isTexture===!0){const s=t;return this.assignTextureLODForSlot(s,e,null,null)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){y.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return y.queue.maxConcurrent}static assignTextureLODForSlot(t,e,s,r){return t?.isTexture!==!0?Promise.resolve(null):r==="glyphMap"?Promise.resolve(t):y.getOrLoadLOD(t,e).then(n=>{if(Array.isArray(n))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(n?.isTexture===!0){if(n!=t&&s&&r){const o=s[r];if(o&&!g){const a=this.getAssignedLODInformation(o);if(a&&a?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",a.level,e,s,o,n),n&&n!==o&&((g||E)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${a.level})`,n.uuid),n.dispose()),null}if(this.trackTextureUsage(n),o&&o!==n&&this.untrackTextureUsage(o)&&(g||E)){const l=this.getAssignedLODInformation(o);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} → ${e} for ${s.name||s.type}.${r}`,o.uuid)}s[r]=n}return n}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(n=>(console.error("Error loading LOD",t,n),null))}parser;url;constructor(t){const e=t.options.path;g&&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?.[U];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(s=>(this._isLoadingMesh=!1,s&&y.registerMesh(this.url,e.guid,s,e.lods?.length,0,e),s))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[U];if(r){if(!r.lods){g&&console.warn("Texture has no LODs",r);return}let n=!1;for(const o of this.parser.associations.keys())o.isTexture===!0&&this.parser.associations.get(o)?.textures===s&&(n=!0,y.registerTexture(this.url,o,r.lods?.length,s,r));n||this.parser.getDependency("texture",s).then(o=>{o&&y.registerTexture(this.url,o,r.lods?.length,s,r)})}}}),this.parser.json.meshes?.forEach((e,s)=>{if(e?.extensions){const r=e?.extensions[U];if(r&&r.lods){for(const n of this.parser.associations.keys())if(n.isMesh){const o=this.parser.associations.get(n);o?.meshes===s&&y.registerMesh(this.url,r.guid,n,r.lods.length,o.primitives,r)}}}}),null}static registerTexture=(t,e,s,r,n)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const a=e.image?.width||e.source?.data?.width||0,l=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${r}] "${e.name||e.uuid}", Current: ${a}x${l}, Max: ${n.lods[0]?.width}x${n.lods[0]?.height}, uuid: ${e.uuid}`,n,e)}e.source&&(e.source[ce]=n);const o=n.guid;y.assignLODInformation(t,e,o,s,r),y.lodInfos.set(o,n),y.lowresCache.set(o,new WeakRef(e))};static registerMesh=(t,e,s,r,n,o)=>{const a=s.geometry;if(!a){g&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),g&&console.log("> Progressive: register mesh "+s.name,{index:n,uuid:s.uuid},o,s),y.assignLODInformation(t,a,e,r,n),y.lodInfos.set(e,o);let u=y.lowresCache.get(e)?.deref();u?u.push(s.geometry):u=[s.geometry],y.lowresCache.set(e,new WeakRef(u)),r>0&&!j(s)&&Xe(s,a);for(const d of z)d.onRegisteredNewMesh?.(s,o)};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()}}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=y.cache.get(t);(g||E)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(y.cache.delete(t),(g||E)&&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),g==="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(g==="verbose"||E)&&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),(g||E)&&n("[gltf-progressive] Memory: Dispose texture",r),t.dispose(),!0;return this.textureRefCounts.set(e,r),g==="verbose"&&n("[gltf-progressive] Memory: Untrack texture",r),!1;function n(o,a){let l=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const d=l&&u?`${l}x${u}`:"N/A";let p="N/A";l&&u&&(p=`~${(Ve(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${o} — ${t.name} ${d} (${p}), refCount: ${s} → ${a}
|
|
3
|
+
${e}`)}}static workers=[];static cacheGeneration=0;static disposedRequests=new WeakSet;static _workersIndex=0;static async getOrLoadLOD(t,e){const s=this.cacheGeneration,r=g=="verbose",n=this.getAssignedLODInformation(t);if(!n)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const o=n?.key;let a;if(t.isTexture===!0){const u=t;u.source&&u.source[ce]&&(a=u.source[ce])}if(a||(a=y.lodInfos.get(o)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`,t.type,y.lodInfos);else{if(e>0){let p=!1;const x=Array.isArray(a.lods);if(x&&e>=a.lods.length?p=!0:x||(p=!0),p){const D=this.lowresCache.get(o);if(D){const M=D.deref();if(M)return M;this.lowresCache.delete(o),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`)}return null}}const u=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!u)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const d=ze(n.url,u);if(d.endsWith(".glb")||d.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const p=d+"_"+a.guid,x=await this.queue.slot(d);if(s!==this.cacheGeneration)return null;const D=this.cache.get(p);if(D!==void 0)if(r&&console.log(`LOD ${e} was already loading/loaded: ${p}`),D instanceof WeakRef){const h=D.deref();if(h){let _=h,b=!1;if(_ instanceof c.Texture&&t instanceof c.Texture?_.image?.data||_.source?.data?_=this.copySettings(t,_):b=!0:_ instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(_.attributes.position?.array||(b=!0)),!b)return _}this.cache.delete(p),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${p}`)}else{let h=await D.catch(b=>(console.error(`Error loading LOD ${e} from ${d}
|
|
4
|
+
`,b),null));if(s!==this.cacheGeneration||this.disposedRequests.has(D))return null;let _=!1;if(h==null||(h instanceof c.Texture&&t instanceof c.Texture?h.image?.data||h.source?.data?h=this.copySettings(t,h):(_=!0,this.cache.delete(p)):h instanceof c.BufferGeometry&&t instanceof c.BufferGeometry&&(h.attributes.position?.array||(_=!0,this.cache.delete(p)))),!_)return h}if(!x.use)return g&&console.log(`LOD ${e} was aborted: ${d}`),null;const M=a,C=new Promise(async(h,_)=>{if(Ze){const f=await(await Qe({})).load(d);if(f.textures.length>0)for(const m of f.textures){let L=m.texture;return y.assignLODInformation(n.url,L,o,e,void 0),t instanceof c.Texture&&(L=this.copySettings(t,L)),L&&(L.guid=M.guid),h(L)}if(f.geometries.length>0){const m=new Array;for(const L of f.geometries){const v=L.geometry;y.assignLODInformation(n.url,v,o,e,L.primitiveIndex),m.push(v)}return h(m)}return h(null)}const b=new X.GLTFLoader;xe(b),g&&(await new Promise(S=>setTimeout(S,1e3)),r&&console.warn("Start loading (delayed) "+d,M.guid));let I=d;if(M&&Array.isArray(M.lods)){const S=M.lods[e];S.hash&&(I+="?v="+S.hash)}const A=await b.loadAsync(I).catch(S=>(console.error(`Error loading LOD ${e} from ${d}
|
|
5
|
+
`,S),h(null)));if(!A)return h(null);const F=A.parser;r&&console.log("Loading finished "+d,M.guid);let O=0;if(A.parser.json.textures){let S=!1;for(const f of A.parser.json.textures){if(f?.extensions){const m=f?.extensions[U];if(m?.guid&&m.guid===M.guid){S=!0;break}}O++}if(S){let f=await F.getDependency("texture",O);return f&&y.assignLODInformation(n.url,f,o,e,void 0),r&&console.log('change "'+t.name+'" → "'+f.name+'"',d,O,f,p),t instanceof c.Texture&&(f=this.copySettings(t,f)),f&&(f.guid=M.guid),h(f)}else g&&console.warn("Could not find texture with guid",M.guid,A.parser.json)}if(O=0,A.parser.json.meshes){let S=!1;for(const f of A.parser.json.meshes){if(f?.extensions){const m=f?.extensions[U];if(m?.guid&&m.guid===M.guid){S=!0;break}}O++}if(S){const f=await F.getDependency("mesh",O);if(r&&console.log(`Loaded Mesh "${f.name}"`,d,O,f,p),f.isMesh===!0){const m=f.geometry;return y.assignLODInformation(n.url,m,o,e,0),h(m)}else{const m=new Array;for(let L=0;L<f.children.length;L++){const v=f.children[L];if(v.isMesh===!0){const R=v.geometry;y.assignLODInformation(n.url,R,o,e,L),m.push(R)}}return h(m)}}else g&&console.warn("Could not find mesh with guid",M.guid,A.parser.json)}return h(null)});this.cache.set(p,C),x.use(C);const w=await C;return s!==this.cacheGeneration||this.cache.get(p)!==C?null:(w!=null?w instanceof c.Texture?(this.cache.set(p,new WeakRef(w)),y._resourceRegistry.register(w,p)):Array.isArray(w)?this.cache.set(p,Promise.resolve(w)):this.cache.set(p,Promise.resolve(w)):this.cache.set(p,Promise.resolve(null)),w)}else if(t instanceof c.Texture){r&&console.log("Load texture from uri: "+d);const x=await new c.TextureLoader().loadAsync(d);return s!==this.cacheGeneration?(x?.dispose(),null):(x?(x.guid=a.guid,x.flipY=!1,x.needsUpdate=!0,x.colorSpace=t.colorSpace,r&&console.log(a,x)):g&&console.warn("failed loading",d),x)}}return null}static queue=new qe(Le()?20:50,{debug:g!=!1});static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const o=new et(t,s,r,n);e.userData.LODS=o,"source"in e&&typeof e.source=="object"&&(e.source.LODS=o)}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?(g==="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 et{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class de{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){g&&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)){let r=this._seen.get(e);if(r>=this._maxPromisesPerObject){g&&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 $=q("debugprogressive"),tt=q("noprogressive"),fe=Symbol("Needle:LODSManager"),he=Symbol("Needle:LODState"),W=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};let Z=class T{static debugDrawLine;static getObjectLODState(t){return t[he]}static addPlugin(t){z.push(t)}static removePlugin(t){const e=z.indexOf(t);e>=0&&z.splice(e,1)}static get(t,e){if(t[fe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[fe];const s=new T(t,{engine:"unknown",...e});return t[fe]=s,s}renderer;context;projectionScreenMatrix=new c.Matrix4;get plugins(){return z}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 de(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),Me()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}_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){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&this._lodchangedlisteners.splice(s,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new c.Clock;#r=0;#n=0;#i=0;#s=0;_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;oe(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.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,$&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const o=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,o)}}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 n=this.renderer.renderLists.get(t,0).opaque;let o=!0;if(n.length===1){const a=n[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(o=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(o=!1),o){if(tt||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,$&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,$&&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.renderer.renderLists.get(t,0),r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const l of r){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){$&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if($==="color"&&l.material&&!l.object.progressive_debug_color){l.object.progressive_debug_color=!0;const d=Math.random()*16777215,p=new c.MeshStandardMaterial({color:d});l.object.material=p}const u=l.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const o=s.transparent;for(const l of o){const u=l.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const a=s.transmissive;for(const l of a){const u=l.object;(u instanceof c.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[he];if(n||(n=new st,s[he]=n),n.frames++<2)return;for(const a of z)a.onBeforeUpdateLOD?.(this.renderer,t,e,s);const o=this.overrideLodLevel!==void 0?this.overrideLodLevel:V;o>=0?(P.mesh_lod=o,P.texture_lod=o):(this.calculateLodLevel(e,s,n,r,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(s,P.mesh_lod),s.material&&P.texture_lod>=0&&this.loadProgressiveTextures(s.material,P.texture_lod,o),g&&s.material&&!s.isGizmo&&Ce(s.material);for(const a of z)a.onAfterUpdatedLOD?.(this.renderer,t,e,s,P);n.lastLodLevel_Mesh=P.mesh_lod,n.lastLodLevel_Texture=P.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const n of t)this.loadProgressiveTextures(n,e);return}let r=!1;if((t[W]===void 0||e<t[W])&&(r=!0),s!==void 0&&s>=0&&(r=t[W]!=s,e=s),r){t[W]=e;const n=y.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(a=>a({type:"texture",level:e,object:t}))});de.addPromise("texture",t,n,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[W]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[W]!=r,e=r),s){t[W]=e;const n=t.geometry,o=y.assignMeshLOD(t,e).then(a=>(a&&t[W]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return de.addPromise("mesh",t,o,this._newPromiseGroups),o}return Promise.resolve(null)}_sphere=new c.Sphere;_tempBox=new c.Box3;_tempBox2=new c.Box3;tempMatrix=new c.Matrix4;_tempWorldPosition=new c.Vector3;_tempBoxSize=new c.Vector3;_tempBox2Size=new c.Vector3;static corner0=new c.Vector3;static corner1=new c.Vector3;static corner2=new c.Vector3;static corner3=new c.Vector3;static _tempPtInside=new c.Vector3;static isInside(t,e){const s=t.min,r=t.max,n=(s.x+r.x)*.5,o=(s.y+r.y)*.5;return this._tempPtInside.set(n,o,s.z).applyMatrix4(e).z<0}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 a=10+1,l=!1;if($&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=y.getMeshLODExtension(e.geometry)?.lods,d=y.getPrimitiveIndex(e.geometry),p=u&&u.length>0,x=y.getMaterialMinMaxLODsCount(e.material),D=x.min_count!==1/0&&x.min_count>=0&&x.max_count>=0;if(!p&&!D){n.mesh_lod=0,n.texture_lod=0;return}p||(l=!0,a=0);const M=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let C=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const w=e;if(!w.boundingBox)w.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!w[T.$skinnedMeshBoundsOffset]){const _=T.skinnedMeshBoundsFrameOffsetCounter++;w[T.$skinnedMeshBoundsOffset]=_}const h=w[T.$skinnedMeshBoundsOffset];if((s.frames+h)%this.skinnedMeshAutoUpdateBoundsInterval===0){const _=j(w),b=w.geometry;_&&(w.geometry=_),w.computeBoundingBox(),w.geometry=b}}C=w.boundingBox}if(C){const w=t;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 f=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(f)){n.mesh_lod=0,n.texture_lod=0;return}}if(this._tempBox.copy(C),this._tempBox.applyMatrix4(e.matrixWorld),w.isPerspectiveCamera&&T.isInside(this._tempBox,this.projectionScreenMatrix)){n.mesh_lod=0,n.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&w.isPerspectiveCamera&&w.fov>70){const f=this._tempBox.min,m=this._tempBox.max;let L=f.x,v=f.y,R=m.x,K=m.y;const ee=2,ie=1.5,te=(f.x+m.x)*.5,se=(f.y+m.y)*.5;L=(L-te)*ee+te,v=(v-se)*ee+se,R=(R-te)*ee+te,K=(K-se)*ee+se;const ke=L<0&&R>0?0:Math.min(Math.abs(f.x),Math.abs(m.x)),Ie=v<0&&K>0?0:Math.min(Math.abs(f.y),Math.abs(m.y)),ae=Math.max(ke,Ie);s.lastCentrality=(ie-ae)*(ie-ae)*(ie-ae)}else s.lastCentrality=1;const h=this._tempBox.getSize(this._tempBoxSize);h.multiplyScalar(.5),screen.availHeight>0&&M>0&&h.multiplyScalar(M/screen.availHeight),t.isPerspectiveCamera?h.x*=t.aspect:t.isOrthographicCamera;const _=t.matrixWorldInverse,b=this._tempBox2;b.copy(C),b.applyMatrix4(e.matrixWorld),b.applyMatrix4(_);const I=b.getSize(this._tempBox2Size),A=Math.max(I.x,I.y);if(Math.max(h.x,h.y)!=0&&A!=0&&(h.z=I.z/Math.max(I.x,I.y)*Math.max(h.x,h.y)),s.lastScreenCoverage=Math.max(h.x,h.y,h.z),s.lastScreenspaceVolume.copy(h),s.lastScreenCoverage*=s.lastCentrality,$&&T.debugDrawLine){const f=this.tempMatrix.copy(this.projectionScreenMatrix);f.invert();const m=T.corner0,L=T.corner1,v=T.corner2,R=T.corner3;m.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=m.x,v.copy(this._tempBox.max),v.y=m.y,R.copy(this._tempBox.max);const K=(m.z+R.z)*.5;m.z=L.z=v.z=R.z=K,m.applyMatrix4(f),L.applyMatrix4(f),v.applyMatrix4(f),R.applyMatrix4(f),T.debugDrawLine(m,L,255),T.debugDrawLine(m,v,255),T.debugDrawLine(L,R,255),T.debugDrawLine(v,R,255)}let O=999;if(u&&s.lastScreenCoverage>0)for(let f=0;f<u.length;f++){const m=u[f],v=(m.densities?.[d]||m.density||1e-5)/s.lastScreenCoverage;if(d>0&&Me()&&!m.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["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.")),v<r){O=f;break}}O<a&&(a=O,l=!0)}if(l?n.mesh_lod=a:n.mesh_lod=s.lastLodLevel_Mesh,$&&n.mesh_lod!=s.lastLodLevel_Mesh){const h=u?.[n.mesh_lod];h&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${h.densities?.[d].toFixed(0)}) | ${e.name}`)}if(D){const w="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=x.max_count-1,$){const h=x.lods[x.max_count-1];$&&console.log(`First Texture LOD ${n.texture_lod} (${h.max_height}px) - ${e.name}`)}}else{const h=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let _=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(_*=1.5);const I=M/window.devicePixelRatio*_;let A=!1;for(let F=x.lods.length-1;F>=0;F--){const O=x.lods[F];if(!(w&&O.max_height>=2048)&&!(Le()&&O.max_height>4096)&&(O.max_height>I||!A&&F===0)){if(A=!0,n.texture_lod=F,$&&n.texture_lod<s.lastLodLevel_Texture){const S=O.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${S}px
|
|
8
|
+
Screensize: ${I.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${h.toFixed(1)}
|
|
9
|
+
${e.name}`)}break}}}}else n.texture_lod=0}};class st{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new c.Vector3;lastCentrality=0}const ve=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let ge=null;function Ae(){const i=rt();i&&(i.mapURLs(function(t){return De(),t}),De(),ge?.disconnect(),ge=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&Re(s)})})}),ge.observe(document,{childList:!0,subtree:!0}))}function rt(){if(typeof customElements>"u")return null;const i=customElements.get("model-viewer");return i||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ae()}),null)}function De(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Re(t)})}const be=new WeakSet;let nt=0;function Re(i){if(!i||be.has(i))return null;be.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++nt+`
|
|
10
|
+
`,i.getAttribute("src"));let t=null,e=null,s=null;for(let r=i;r!=null;r=Object.getPrototypeOf(r)){const n=Object.getOwnPropertySymbols(r),o=n.find(u=>u.toString()=="Symbol(renderer)"),a=n.find(u=>u.toString()=="Symbol(scene)"),l=n.find(u=>u.toString()=="Symbol(needsRender)");!t&&o!=null&&(t=i[o].threeRenderer),!e&&a!=null&&(e=i[a]),!s&&l!=null&&(s=i[l])}if(t&&e){let r=function(){if(s){let o=0,a=setInterval(()=>{if(o++>5){clearInterval(a);return}s?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=Z.get(t,{engine:"model-viewer"});return Z.addPlugin(new ot),n.enable(),n.addEventListener("changed",()=>{s?.call(i)}),i.addEventListener("model-visibility",o=>{o.detail.visible&&s?.call(i)}),i.addEventListener("load",()=>{r()}),()=>{n.disable()}}return null}class ot{_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[re]==!0)return;e[re]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(n&&s&&e.material){let o=function(l){if(l[re]==!0)return;l[re]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let d=0;d<u.length;d++){const p=u[d],x=l[p];if(x?.isTexture===!0){const D=x.userData?.associations?.textures;if(D==null)continue;const M=s.parser.json.textures[D];if(!M){console.warn("Texture data not found for texture index "+D);continue}if(M?.extensions?.[U]){const C=M.extensions[U];C&&n&&y.registerTexture(n,x,C.lods.length,D,C)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)o(l);else o(a)}}tryParseMeshLOD(t,e){if(e[ve]==!0)return;e[ve]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const n=e.userData?.gltfExtensions?.[U];if(n&&r){const o=e.uuid;y.registerMesh(r,o,e,0,n.lods.length,n)}}}function it(...i){let t,e,s,r;switch(i.length){case 2:[s,e]=i,r={};break;case 3:[s,e,r]=i;break;case 4:[t,e,s,r]=i;break;default:throw new Error("Invalid arguments")}oe(e),xe(s),we(s,{progressive:!0,...r?.hints}),s.register(o=>new y(o));const n=Z.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}Ae();if(!Ee){const i={gltfProgressive:{useNeedleProgressive:it,LODsManager:Z,configureLoader:we,getRaycastMesh:j,useRaycastMeshes:je}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.LODsManager=Z;exports.NEEDLE_progressive=y;exports.addDracoAndKTX2Loaders=xe;exports.configureLoader=we;exports.createLoaders=oe;exports.getRaycastMesh=j;exports.setDracoDecoderLocation=Se;exports.setKTX2TranscoderLocation=Te;
|