@needle-tools/engine 4.13.1-beta → 4.13.1-next.6cb1b73

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components.needle.json +1 -1
  3. package/dist/gltf-progressive-BTd714P0.min.js +9 -0
  4. package/dist/gltf-progressive-CrHduHPm.umd.cjs +9 -0
  5. package/dist/{gltf-progressive-eiJCrjLb.js → gltf-progressive-CsyU_5lc.js} +364 -287
  6. package/dist/{needle-engine.bundle-6774fXoY.min.js → needle-engine.bundle-DRSn34Va.min.js} +47 -47
  7. package/dist/{needle-engine.bundle-fbFZTOKP.umd.cjs → needle-engine.bundle-qTjHpin6.umd.cjs} +29 -29
  8. package/dist/{needle-engine.bundle-CGtsEhyJ.js → needle-engine.bundle-wB3o8RB6.js} +335 -312
  9. package/dist/needle-engine.d.ts +446 -39
  10. package/dist/needle-engine.js +3 -3
  11. package/dist/needle-engine.min.js +1 -1
  12. package/dist/needle-engine.umd.cjs +1 -1
  13. package/lib/engine/engine_context.js +12 -2
  14. package/lib/engine/engine_context.js.map +1 -1
  15. package/lib/engine/engine_physics.d.ts +2 -0
  16. package/lib/engine/engine_physics.js +2 -0
  17. package/lib/engine/engine_physics.js.map +1 -1
  18. package/lib/engine/engine_physics_rapier.d.ts +2 -0
  19. package/lib/engine/engine_physics_rapier.js +2 -0
  20. package/lib/engine/engine_physics_rapier.js.map +1 -1
  21. package/lib/engine/engine_utils.d.ts +1 -0
  22. package/lib/engine/engine_utils.js +1 -0
  23. package/lib/engine/engine_utils.js.map +1 -1
  24. package/lib/engine/engine_utils_screenshot.d.ts +171 -14
  25. package/lib/engine/engine_utils_screenshot.js +65 -0
  26. package/lib/engine/engine_utils_screenshot.js.map +1 -1
  27. package/lib/engine/engine_utils_screenshot.xr.d.ts +1 -1
  28. package/lib/engine/engine_utils_screenshot.xr.js +1 -1
  29. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +3 -0
  30. package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
  31. package/lib/engine/xr/NeedleXRSession.d.ts +5 -0
  32. package/lib/engine/xr/NeedleXRSession.js +5 -0
  33. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  34. package/lib/engine-components/ContactShadows.d.ts +2 -0
  35. package/lib/engine-components/ContactShadows.js +2 -0
  36. package/lib/engine-components/ContactShadows.js.map +1 -1
  37. package/lib/engine-components/EventList.js +2 -2
  38. package/lib/engine-components/EventList.js.map +1 -1
  39. package/lib/engine-components/Renderer.d.ts +4 -3
  40. package/lib/engine-components/Renderer.js +4 -3
  41. package/lib/engine-components/Renderer.js.map +1 -1
  42. package/lib/engine-components/RigidBody.d.ts +57 -5
  43. package/lib/engine-components/RigidBody.js +57 -5
  44. package/lib/engine-components/RigidBody.js.map +1 -1
  45. package/lib/engine-components/ScreenCapture.d.ts +1 -0
  46. package/lib/engine-components/ScreenCapture.js +1 -0
  47. package/lib/engine-components/ScreenCapture.js.map +1 -1
  48. package/lib/engine-components/SeeThrough.d.ts +70 -5
  49. package/lib/engine-components/SeeThrough.js +70 -5
  50. package/lib/engine-components/SeeThrough.js.map +1 -1
  51. package/lib/engine-components/ShadowCatcher.d.ts +56 -4
  52. package/lib/engine-components/ShadowCatcher.js +56 -4
  53. package/lib/engine-components/ShadowCatcher.js.map +1 -1
  54. package/lib/engine-components/Skybox.d.ts +43 -7
  55. package/lib/engine-components/Skybox.js +43 -7
  56. package/lib/engine-components/Skybox.js.map +1 -1
  57. package/lib/engine-components/VideoPlayer.d.ts +1 -1
  58. package/lib/engine-components/VideoPlayer.js +7 -1
  59. package/lib/engine-components/VideoPlayer.js.map +1 -1
  60. package/lib/engine-components/timeline/PlayableDirector.d.ts +4 -0
  61. package/lib/engine-components/timeline/PlayableDirector.js +4 -0
  62. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  63. package/lib/engine-components/timeline/TimelineModels.d.ts +14 -0
  64. package/lib/engine-components/timeline/TimelineModels.js +4 -0
  65. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  66. package/lib/engine-components/utils/OpenURL.d.ts +1 -0
  67. package/lib/engine-components/utils/OpenURL.js +1 -0
  68. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  69. package/lib/engine-components/web/CursorFollow.d.ts +1 -0
  70. package/lib/engine-components/web/CursorFollow.js +1 -0
  71. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  72. package/lib/engine-components/web/ScrollFollow.d.ts +1 -0
  73. package/lib/engine-components/web/ScrollFollow.js +1 -0
  74. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  75. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +9 -0
  76. package/lib/engine-components/webxr/WebARCameraBackground.js +9 -0
  77. package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
  78. package/lib/engine-components/webxr/WebXR.d.ts +1 -0
  79. package/lib/engine-components/webxr/WebXR.js +1 -0
  80. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  81. package/package.json +3 -3
  82. package/src/engine/engine_context.ts +17 -3
  83. package/src/engine/engine_physics.ts +4 -0
  84. package/src/engine/engine_physics_rapier.ts +8 -4
  85. package/src/engine/engine_utils.ts +1 -0
  86. package/src/engine/engine_utils_screenshot.ts +241 -17
  87. package/src/engine/engine_utils_screenshot.xr.ts +1 -1
  88. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +3 -0
  89. package/src/engine/xr/NeedleXRSession.ts +5 -0
  90. package/src/engine-components/ContactShadows.ts +6 -1
  91. package/src/engine-components/EventList.ts +2 -2
  92. package/src/engine-components/Renderer.ts +14 -13
  93. package/src/engine-components/RigidBody.ts +64 -8
  94. package/src/engine-components/ScreenCapture.ts +1 -0
  95. package/src/engine-components/SeeThrough.ts +76 -9
  96. package/src/engine-components/ShadowCatcher.ts +61 -6
  97. package/src/engine-components/Skybox.ts +48 -12
  98. package/src/engine-components/VideoPlayer.ts +7 -1
  99. package/src/engine-components/timeline/PlayableDirector.ts +5 -1
  100. package/src/engine-components/timeline/SignalAsset.ts +1 -1
  101. package/src/engine-components/timeline/TimelineModels.ts +18 -2
  102. package/src/engine-components/utils/OpenURL.ts +1 -0
  103. package/src/engine-components/web/CursorFollow.ts +1 -0
  104. package/src/engine-components/web/ScrollFollow.ts +1 -0
  105. package/src/engine-components/webxr/WebARCameraBackground.ts +12 -3
  106. package/src/engine-components/webxr/WebXR.ts +1 -0
  107. package/dist/gltf-progressive-BURrJW0U.umd.cjs +0 -8
  108. package/dist/gltf-progressive-DHLDFNvQ.min.js +0 -8
@@ -4,6 +4,9 @@ import { ObjectUtils, PrimitiveType } from "../engine/engine_create_objects.js";
4
4
  import { serializable } from "../engine/engine_serialization_decorator.js";
5
5
  import { RGBAColor } from "../engine/js-extensions/index.js";
6
6
  import { Behaviour } from "./Component.js";
7
+ import type { ContactShadows } from "./ContactShadows.js";
8
+ import type { Light } from "./Light.js";
9
+ import type { Renderer } from "./Renderer.js";
7
10
 
8
11
  /**
9
12
  * The mode of the ShadowCatcher.
@@ -18,14 +21,66 @@ enum ShadowMode {
18
21
  }
19
22
 
20
23
  /**
21
- * ShadowCatcher can be added to an Object3D to make it render shadows (or light) in the scene. It can also be used to create a shadow mask, or to occlude light.
22
- * If the GameObject is a Mesh, it will apply a shadow-catching material to it - otherwise it will create a quad with the shadow-catching material.
23
- *
24
- * Note that ShadowCatcher meshes are not raycastable by default; if you want them to be raycastable, change the layers in `onEnable()`.
25
- *
26
- * @summary Creates a shadow mask or a light occluder
24
+ * ShadowCatcher renders real-time shadows cast by lights onto a mesh surface.
25
+ * Captures actual shadow data from the scene's lighting system (directional lights, point lights, spot lights).
26
+ *
27
+ * If the GameObject is a Mesh, it applies a shadow-catching material to it.
28
+ * Otherwise, it creates a quad mesh with the shadow-catching material automatically.
29
+ *
30
+ * [![](https://cloud.needle.tools/-/media/pFXPchA4vynNKOjgG_KucQ.gif)](https://engine.needle.tools/samples/shadow-catcher/)
31
+ * *Additive ShadowCatcher mode with point light shadows*
32
+ *
33
+ * [![](https://cloud.needle.tools/-/media/oIWgEU49rEA0xJ2TrbzVlg.gif)](https://engine.needle.tools/samples/transmission/)
34
+ * *ShadowCatcher with directional light shadows*
35
+ *
36
+ * **Shadow Modes:**
37
+ * - `ShadowMask` - Only renders shadows (works best with directional lights)
38
+ * - `Additive` - Renders light additively (works best with point/spot lights)
39
+ * - `Occluder` - Occludes light without rendering shadows
40
+ *
41
+ * **ShadowCatcher vs ContactShadows:**
42
+ * - **ShadowCatcher**: Real-time shadows from actual lights. Accurate directional shadows that match light sources. Requires lights with shadows enabled. Updates every frame.
43
+ * - **{@link ContactShadows}**: Proximity-based ambient occlusion-style shadows. Extremely soft and diffuse, ideal for subtle grounding. Better performance, works without lights.
44
+ *
45
+ * **When to use ShadowCatcher:**
46
+ * - You need accurate shadows that match specific light directions
47
+ * - Scene has real-time lighting with shadow-casting lights
48
+ * - Shadows need to follow light attenuation and angles
49
+ * - AR/VR scenarios where light estimation is available
50
+ * - Hard or semi-hard shadow edges are desired
51
+ *
52
+ * **When to use ContactShadows instead:**
53
+ * - You want very soft, ambient occlusion-style ground shadows
54
+ * - Performance is critical (no per-frame shadow rendering)
55
+ * - Scene doesn't have shadow-casting lights
56
+ * - Product visualization or configurators (subtle grounding effect)
57
+ * - Soft, diffuse shadows are more visually appealing than accurate ones
58
+ *
59
+ * **Note:** ShadowCatcher meshes are not raycastable by default (layer 2). Change layers in `onEnable()` if raycasting is needed.
60
+ *
61
+ * @example Basic shadow catcher plane
62
+ * ```ts
63
+ * const plane = new Object3D();
64
+ * const catcher = addComponent(plane, ShadowCatcher);
65
+ * catcher.mode = ShadowMode.ShadowMask;
66
+ * catcher.shadowColor = new RGBAColor(0, 0, 0, 0.8);
67
+ * ```
68
+ *
69
+ * @example Apply to existing mesh
70
+ * ```ts
71
+ * const mesh = this.gameObject.getComponent(Mesh);
72
+ * const catcher = addComponent(mesh, ShadowCatcher);
73
+ * // The mesh will now catch shadows from scene lights
74
+ * ```
75
+ *
76
+ * @summary Renders real-time shadows from lights onto surfaces
27
77
  * @category Rendering
28
78
  * @group Components
79
+ * @see {@link ContactShadows} for proximity-based fake shadows (better performance)
80
+ * @see {@link Light} for shadow-casting light configuration
81
+ * @see {@link Renderer} for shadow receiving settings
82
+ * @link https://engine.needle.tools/samples/shadow-catcher/
83
+ * @link https://engine.needle.tools/samples/transmission/
29
84
  */
30
85
  export class ShadowCatcher extends Behaviour {
31
86
 
@@ -114,20 +114,52 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, () => {
114
114
 
115
115
 
116
116
  /**
117
- * [RemoteSkybox](https://engine.needle.tools/docs/api/RemoteSkybox) Is a component that allows you to set the skybox or environment texture of a scene from a URL, a local file or a static skybox name.
118
- * It supports .hdr, .exr, .jpg, .png files.
119
- *
117
+ * The [RemoteSkybox](https://engine.needle.tools/docs/api/RemoteSkybox) component allows you to set the skybox or environment texture of a scene from a URL, a local file or a static skybox name.
118
+ * It supports .hdr, .exr, .jpg, .png, and .ktx2 files.
119
+ *
120
+ * **HTML Attributes:**
121
+ * You can control skybox and environment from HTML using `<needle-engine>` attributes:
122
+ * - `background-image`: Sets the scene background/skybox image
123
+ * - `environment-image`: Sets the scene environment map (for reflections and lighting)
124
+ *
125
+ * These attributes accept URLs or magic skybox names (see examples below).
126
+ *
127
+ * **Magic Skybox Names:**
128
+ * Built-in optimized skyboxes hosted on Needle CDN:
129
+ * - `"studio"` - Neutral studio lighting (default)
130
+ * - `"blurred-skybox"` - Blurred environment
131
+ * - `"quicklook"` - Apple QuickLook object mode style
132
+ * - `"quicklook-ar"` - Apple QuickLook AR mode style
133
+ *
120
134
  * ### Events
121
135
  * - `dropped-unknown-url`: Emitted when a file is dropped on the scene. The event detail contains the sender, the url and a function to apply the url.
122
- *
123
- * @example adding a skybox
136
+ *
137
+ * @example Using HTML attributes
138
+ * ```html
139
+ * <needle-engine
140
+ * background-image="https://example.com/skybox.hdr"
141
+ * environment-image="studio">
142
+ * </needle-engine>
143
+ * ```
144
+ *
145
+ * @example Using magic skybox names
146
+ * ```html
147
+ * <needle-engine background-image="studio"></needle-engine>
148
+ * <needle-engine environment-image="quicklook"></needle-engine>
149
+ * ```
150
+ *
151
+ * @example Adding via code
124
152
  * ```ts
125
- * GameObject.addComponent(gameObject, Skybox, { url: "https://example.com/skybox.hdr", background: true, environment: true });
153
+ * GameObject.addComponent(gameObject, RemoteSkybox, {
154
+ * url: "https://example.com/skybox.hdr",
155
+ * background: true,
156
+ * environment: true
157
+ * });
126
158
  * ```
127
- *
128
- * @example handle custom url
159
+ *
160
+ * @example Handle custom dropped URL
129
161
  * ```ts
130
- * const skybox = GameObject.addComponent(gameObject, Skybox);
162
+ * const skybox = GameObject.addComponent(gameObject, RemoteSkybox);
131
163
  * skybox.addEventListener("dropped-unknown-url", (evt) => {
132
164
  * let url = evt.detail.url;
133
165
  * console.log("User dropped file", url);
@@ -137,15 +169,19 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, () => {
137
169
  * evt.detail.apply(url);
138
170
  * });
139
171
  * ```
140
- *
141
- * @example update skybox url
172
+ *
173
+ * @example Update skybox at runtime
142
174
  * ```ts
143
175
  * skybox.setSkybox("https://example.com/skybox.hdr");
176
+ * // Or use a magic name:
177
+ * skybox.setSkybox("studio");
144
178
  * ```
145
- *
179
+ *
146
180
  * @summary Sets the skybox or environment texture of a scene
147
181
  * @category Rendering
148
182
  * @group Components
183
+ * @see {@link Camera} for clearFlags and background control
184
+ * @link https://engine.needle.tools/docs/html.html#needle-engine-element
149
185
  */
150
186
  export class RemoteSkybox extends Behaviour {
151
187
 
@@ -53,7 +53,7 @@ export enum VideoRenderMode {
53
53
  * - Media streams (from webcam, screen capture, etc.)
54
54
  * - HLS playlists (m3u8) for livestreaming
55
55
  *
56
- * ![](https://cloud.needle.tools/-/media/w1uHur5yu3Ni7qFaKIFX-g.gif)
56
+ * [![](https://cloud.needle.tools/-/media/w1uHur5yu3Ni7qFaKIFX-g.gif)](https://engine.needle.tools/samples/video-playback/)
57
57
  *
58
58
  * **Rendering modes:**
59
59
  * Video can be rendered to a material texture, render texture, or camera planes.
@@ -604,11 +604,17 @@ export class VideoPlayer extends Behaviour {
604
604
  const videoMaterial = this._videoMaterial as any;
605
605
 
606
606
  if (!this.targetMaterialProperty) {
607
+ if (debug && videoMaterial[fieldName] === undefined) {
608
+ console.warn(`The target material does not have a '${fieldName}' property, video might not render correctly.`);
609
+ }
607
610
  videoMaterial[fieldName] = this._videoTexture;
608
611
  }
609
612
  else {
610
613
  switch (this.targetMaterialProperty) {
611
614
  default:
615
+ if (debug && videoMaterial[this.targetMaterialProperty] === undefined) {
616
+ console.warn(`The target material does not have a '${this.targetMaterialProperty}' property, video might not render correctly.`);
617
+ }
612
618
  videoMaterial[fieldName] = this._videoTexture;
613
619
  break;
614
620
  // doesnt render:
@@ -50,7 +50,10 @@ export type CreateTrackFunction = (director: PlayableDirector, track: Models.Tra
50
50
  /**
51
51
  * PlayableDirector is the main component for controlling timelines in Needle Engine.
52
52
  * It orchestrates playback of TimelineAssets containing animation, audio, signal,
53
- * control, and activation tracks.
53
+ * control, and activation tracks.
54
+ *
55
+ * ![](https://cloud.needle.tools/-/media/CkJal5dIBwFe6erA-MmiGw.webp)
56
+ * *Screenshot: Timeline in Unity*
54
57
  *
55
58
  * **Supported track types:**
56
59
  * - Animation tracks - animate objects using AnimationClips
@@ -94,6 +97,7 @@ export type CreateTrackFunction = (director: PlayableDirector, track: Models.Tra
94
97
  * @see {@link SignalReceiver} for handling timeline signals
95
98
  * @link https://engine.needle.tools/samples/?overlay=samples&tag=animation
96
99
  * @link https://app.songsofcultures.com/
100
+ * @link https://engine.needle.tools/docs/blender/animation.html Blender timeline and animation export
97
101
  */
98
102
  export class PlayableDirector extends Behaviour {
99
103
 
@@ -8,7 +8,7 @@ const debug = getParam("debugsignals")
8
8
 
9
9
 
10
10
  /**
11
- * Used to reference a signal asset in a SignalReceiver. This is internally used by the {@link SignalReceiverEvent}.
11
+ * Used to reference a signal asset in a SignalReceiver. This is internally used by the {@link SignalReceiverEvent}.
12
12
  */
13
13
  export class SignalAsset {
14
14
  @serializable()
@@ -1,7 +1,10 @@
1
- import { AnimationClip, Object3D, Quaternion, Vector3 } from "three";
1
+ import { AnimationClip, Object3D, Quaternion, Vector3 } from "three";
2
+
3
+ import type { PlayableDirector } from "./PlayableDirector";
2
4
 
3
5
  /**
4
6
  * @category Animation and Sequencing
7
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
5
8
  */
6
9
  export declare type TimelineAssetModel = {
7
10
  name: string;
@@ -10,6 +13,8 @@ export declare type TimelineAssetModel = {
10
13
 
11
14
  /**
12
15
  * @category Animation and Sequencing
16
+ * @see {@link TimelineAssetModel} for the data structure of a timeline asset, which can be played using the PlayableDirector component.
17
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
13
18
  */
14
19
  export enum TrackType {
15
20
  Activation = "ActivationTrack",
@@ -21,7 +26,9 @@ export enum TrackType {
21
26
  }
22
27
 
23
28
  /**
24
- * @category Animation and Sequencing
29
+ * @category Animation and Sequencing
30
+ * @see {@link TimelineAssetModel} for the data structure of a timeline asset, which can be played using the PlayableDirector component.
31
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
25
32
  */
26
33
  export enum ClipExtrapolation {
27
34
  None = 0,
@@ -33,6 +40,7 @@ export enum ClipExtrapolation {
33
40
 
34
41
  /**
35
42
  * @category Animation and Sequencing
43
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
36
44
  */
37
45
  export declare type TrackModel = {
38
46
  name: string;
@@ -50,6 +58,7 @@ declare type Quat = { x: number, y: number, z: number, w: number };
50
58
 
51
59
  /**
52
60
  * @category Animation and Sequencing
61
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
53
62
  */
54
63
  export declare type TrackOffset = {
55
64
  position: Vec3 | Vector3;
@@ -58,6 +67,7 @@ export declare type TrackOffset = {
58
67
 
59
68
  /**
60
69
  * @category Animation and Sequencing
70
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
61
71
  */
62
72
  export declare type ClipModel = {
63
73
  start: number;
@@ -75,6 +85,7 @@ export declare type ClipModel = {
75
85
 
76
86
  /**
77
87
  * @category Animation and Sequencing
88
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
78
89
  */
79
90
  export declare type AnimationClipModel = {
80
91
  clip: string | number | AnimationClip;
@@ -87,6 +98,7 @@ export declare type AnimationClipModel = {
87
98
 
88
99
  /**
89
100
  * @category Animation and Sequencing
101
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
90
102
  */
91
103
  export declare type AudioClipModel = {
92
104
  clip: string;
@@ -96,6 +108,7 @@ export declare type AudioClipModel = {
96
108
 
97
109
  /**
98
110
  * @category Animation and Sequencing
111
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
99
112
  */
100
113
  export declare type ControlClipModel = {
101
114
  sourceObject: string | Object3D;
@@ -109,6 +122,7 @@ export enum MarkerType {
109
122
 
110
123
  /**
111
124
  * @category Animation and Sequencing
125
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
112
126
  */export declare class MarkerModel {
113
127
  type: MarkerType;
114
128
  time: number;
@@ -116,6 +130,7 @@ export enum MarkerType {
116
130
 
117
131
  /**
118
132
  * @category Animation and Sequencing
133
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
119
134
  */
120
135
  export declare class SignalMarkerModel extends MarkerModel {
121
136
  retroActive: boolean;
@@ -133,5 +148,6 @@ export declare class SignalMarkerModel extends MarkerModel {
133
148
  *
134
149
  * @link [Example Project using ScrollMarker](https://scrollytelling-bike-z23hmxb2gnu5a.needle.run/)
135
150
  * @category Animation and Sequencing
151
+ * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
136
152
  */
137
153
  export type ScrollMarkerModel = MarkerModel & { name?: string };
@@ -19,6 +19,7 @@ export enum OpenURLMode {
19
19
  * OpenURL behaviour opens a URL in a new tab or window when the object (or any if it's children) is clicked.
20
20
  *
21
21
  * @category Interactivity
22
+ * @category Web
22
23
  * @group Components
23
24
  */
24
25
  export class OpenURL extends Behaviour implements IPointerClickHandler {
@@ -87,6 +87,7 @@ const debug = getParam("debugcursor");
87
87
  *
88
88
  * @summary Makes objects follow the cursor/touch position in 3D space
89
89
  * @category Interactivity
90
+ * @category Web
90
91
  * @group Components
91
92
  * @component
92
93
  */
@@ -66,6 +66,7 @@ type ScrollFollowEvent = {
66
66
  *
67
67
  * @summary Links scroll position to target objects
68
68
  * @category Web
69
+ * @category Interaction
69
70
  * @group Components
70
71
  * @component
71
72
  */
@@ -22,13 +22,22 @@ import { Behaviour } from "../Component.js";
22
22
  const debug = getParam("debugarcamera");
23
23
 
24
24
  /**
25
- * WebARCameraBackground is a component that allows to display the camera feed as a background in an AR session to more easily blend the real world with the virtual world or applying effects to the camera feed.
26
- *
25
+ * WebARCameraBackground is a component that allows to display the camera feed as a background in an AR session to more easily blend the real world with the virtual world or applying effects to the camera feed.
26
+ *
27
+ * This component automatically requests `camera-access` permission when entering AR mode, which is required to:
28
+ * - Display the real-world camera feed as a background
29
+ * - Include the camera feed in AR screenshots taken with {@link screenshot2}
30
+ *
31
+ * **Note**: If you want to take AR screenshots with the camera feed but don't need to display it as a background,
32
+ * you can still add this component to your scene (it will request camera access) or manually request the
33
+ * `camera-access` feature in your `onBeforeXR` method.
34
+ *
27
35
  * - Example: https://samples.needle.tools/ar-camera-background
28
- *
36
+ *
29
37
  * @summary Displays the camera feed as background in WebAR sessions
30
38
  * @category XR
31
39
  * @group Components
40
+ * @see {@link screenshot2} for taking screenshots in AR (requires camera access for camera feed compositing)
32
41
  */
33
42
  export class WebARCameraBackground extends Behaviour {
34
43
 
@@ -59,6 +59,7 @@ const debugQuicklook = getParam("debugusdz");
59
59
  * @see {@link XRControllerMovement} for VR locomotion
60
60
  * @see {@link WebARSessionRoot} for AR session configuration
61
61
  * @see {@link Avatar} for networked user avatars
62
+ * @see {@link screenshot2} for taking screenshots in XR (including AR camera feed compositing)
62
63
  * @link https://engine.needle.tools/docs/xr.html
63
64
  * @link https://engine.needle.tools/samples/?overlay=samples&tag=xr
64
65
  * @link https://engine.needle.tools/samples/collaborative-sandbox
@@ -1,8 +0,0 @@
1
- "use strict";const d=require("./three-BW2s1Yl-.umd.cjs"),q=require("./three-examples-Bpfu6ke_.umd.cjs");var ae=typeof document<"u"?document.currentScript:null;const Re="";globalThis.GLTF_PROGRESSIVE_VERSION=Re;console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",V="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Be=k,$e=V,De=new URL(k+"draco_decoder.js");De.searchParams.append("range","true");fetch(De,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Be&&be("./include/draco/"),V===$e&&Oe("./include/ktx2/")}).finally(()=>{Se()});const Ge=()=>({dracoDecoderPath:k,ktx2TranscoderPath:V});function be(i){k=i,A&&A[ge]!=k?(console.debug("Updating Draco decoder path to "+i),A[ge]=k,A.setDecoderPath(k),A.preload()):console.debug("Setting Draco decoder path to "+i)}function Oe(i){V=i,R&&R.transcoderPath!=V?(console.debug("Updating KTX2 transcoder path to "+i),R.setTranscoderPath(V),R.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ne(i){return Se(),i?R.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:R,meshoptDecoder:se}}function ye(i){i.dracoLoader||i.setDRACOLoader(A),i.ktx2Loader||i.setKTX2Loader(R),i.meshoptDecoder||i.setMeshoptDecoder(se)}const ge=Symbol("dracoDecoderPath");let A,se,R;function Se(){A||(A=new q.DRACOLoader,A[ge]=k,A.setDecoderPath(k),A.setDecoderConfig({type:"js"}),A.preload()),R||(R=new q.KTX2Loader,R.setTranscoderPath(V),R.init()),se||(se=q.MeshoptDecoder)}const pe=new WeakMap;function xe(i,t){let e=pe.get(i);e?e=Object.assign(e,t):e=t,pe.set(i,e)}const Fe=q.GLTFLoader.prototype.load;function Ue(...i){const t=pe.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return i[0]=e,Fe?.call(this,...i)}q.GLTFLoader.prototype.load=Ue;N("debugprogressive");function N(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 We(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 r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function Te(){return j!==void 0||(j=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),N("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",j)),j}let j;function Le(){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 ze{constructor(t=100,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:r,resolve:n}=this._queue.shift();n({use:s=>this.add(r,s)})}}}const Ee=typeof window>"u"&&typeof document>"u",me=Symbol("needle:raycast-mesh");function X(i){return i?.[me]instanceof d.BufferGeometry?i[me]:null}function Ve(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!X(i)){const r=qe(t);r.userData={isRaycastMesh:!0},i[me]=r}}function Ne(i=!0){if(i){if(Y)return;const t=Y=d.Mesh.prototype.raycast;d.Mesh.prototype.raycast=function(e,r){const n=this,s=X(n);let o;s&&n.isMesh&&(o=n.geometry,n.geometry=s),t.call(this,e,r),o&&(n.geometry=o)}}else{if(!Y)return;d.Mesh.prototype.raycast=Y,Y=null}}let Y=null;function qe(i){const t=new d.BufferGeometry;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const W=new Array,p=N("debugprogressive");let Q,E=-1;if(p){let i=function(){E+=1,E>=t&&(E=-1),console.log(`Toggle LOD level [${E}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(Q=!Q,console.log(`Toggle wireframe [${Q}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(E=r,console.log(`Set LOD level to [${E}]`))})}function Pe(i){if(p&&Q!==void 0)if(Array.isArray(i))for(const t of i)Pe(t);else i&&"wireframe"in i&&(i.wireframe=Q===!0)}const H=new Array;let Xe=0;const Ke=Te()?2:10;function je(i){if(H.length<Ke){const r=H.length;p&&console.warn(`[Worker] Creating new worker #${r}`);const n=we.createWorker(i||{});return H.push(n),n}const t=Xe++%H.length;return H[t]}class we{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const n=r.data;switch(this._debug&&console.log("[Worker] EVENT",n),n.type){case"loaded-gltf":for(const s of this._running)if(s.url===n.result.url){Ye(n.result),s.resolve(n.result);const o=s.url;o.startsWith("blob:")&&URL.revokeObjectURL(o)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/loader.worker-C1GG9A7C.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ae&&ae.tagName.toUpperCase()==="SCRIPT"&&ae.src||new URL("gltf-progressive-BURrJW0U.umd.cjs",document.baseURI).href),{type:"module"});return new we(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Ge();let n=e?.renderer;n||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await Promise.resolve().then(()=>require("./three-BW2s1Yl-.umd.cjs")).then(c=>c.THREE);return new u})(),n=await this._webglRenderer);const l=ne(n).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 a={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.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 Ye(i){for(const t of i.geometries){const e=t.geometry,r=new d.BufferGeometry;if(r.name=e.name||"",e.index){const n=e.index;r.setIndex(le(n))}for(const n in e.attributes){const s=e.attributes[n],o=le(s);r.setAttribute(n,o)}if(e.morphAttributes)for(const n in e.morphAttributes){const o=e.morphAttributes[n].map(l=>le(l));r.morphAttributes[n]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new d.Box3,r.boundingBox.min=new d.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new d.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new d.Sphere(new d.Vector3(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const n of e.groups)r.addGroup(n.start,n.count,n.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const n=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,o=e.image?.height||e.source?.data?.height||-1;r=new d.CompressedTexture(n,s,o,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new d.Texture(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new d.Matrix3(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return i}function le(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,n=new d.InterleavedBuffer(r,e.stride);t=new d.InterleavedBufferAttribute(n,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new d.BufferAttribute(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const He=N("gltf-progressive-worker"),Qe=N("gltf-progressive-reduce-mipmaps"),ue=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class m{get name(){return F}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 r=this,n="LODS:minmax",s=t[n];if(s!=null)return s;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[n]=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&&o(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&o(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[n]=e,e;function o(l,a){const u=r.getAssignedLODInformation(l);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){a.min_count=Math.min(a.min_count,c.lods.length),a.max_count=Math.max(a.max_count,c.lods.length);for(let y=0;y<c.lods.length;y++){const _=c.lods[y];_.width&&(a.lods[y]=a.lods[y]||{min_height:1/0,max_height:0},a.lods[y].min_height=Math.min(a.lods[y].min_height,_.height),a.lods[y].max_height=Math.max(a.lods[y].max_height,_.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const o=t[s];if(o&&o.isTexture&&this.hasLODLevelAvailable(o,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,n;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof d.Mesh||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const s of W)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const o=n.index||0;s=s[o]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:p&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const r=t;if(Array.isArray(r.material)){const n=new Array;for(const s of r.material){const o=this.assignTextureLOD(s,e);n.push(o)}return Promise.all(n).then(s=>{const o=new Array;for(const l of s)Array.isArray(l)&&o.push(...l);return o})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,n=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const o=r;for(const l of Object.keys(o.uniforms)){const a=o.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(c=>(c&&o.uniforms[l].value!=c&&(o.uniforms[l].value=c,o.uniformsNeedUpdate=!0),c));n.push(u),s.push(l)}}}else for(const o of Object.keys(r)){const l=r[o];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,o);n.push(a),s.push(o)}}return Promise.all(n).then(o=>{const l=new Array;for(let a=0;a<o.length;a++){const u=o[a],c=s[a];u&&u.isTexture===!0?l.push({material:r,slot:c,texture:u,level:e}):l.push({material:r,slot:c,texture:null,level:e})}return l})}if(t instanceof d.Texture||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(s?.isTexture===!0){if(s!=t&&r&&n){const o=r[n];if(o&&!p){const l=this.getAssignedLODInformation(o);if(l&&l?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,o,s),null}if(Qe&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&p&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[n]=s}return s}else p=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}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?.[F];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&m.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return p&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[F];if(n){if(!n.lods){p&&console.warn("Texture has no LODs",n);return}let s=!1;for(const o of this.parser.associations.keys())o.isTexture===!0&&this.parser.associations.get(o)?.textures===r&&(s=!0,m.registerTexture(this.url,o,n.lods?.length,r,n));s||this.parser.getDependency("texture",r).then(o=>{o&&m.registerTexture(this.url,o,n.lods?.length,r,n)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[F];if(n&&n.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const o=this.parser.associations.get(s);o?.meshes===r&&m.registerMesh(this.url,n.guid,s,n.lods.length,o.primitives,n)}}}}),null}static registerTexture=(t,e,r,n,s)=>{if(!e){p&&console.error("!! gltf-progressive: Called register texture without texture");return}if(p){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${n}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${s.lods[0]?.width}x${s.lods[0]?.height}, uuid: ${e.uuid}`,s,e)}e.source&&(e.source[ue]=s);const o=s.guid;m.assignLODInformation(t,e,o,r,n),m.lodInfos.set(o,s),m.lowresCache.set(o,e)};static registerMesh=(t,e,r,n,s,o)=>{const l=r.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},o,r),m.assignLODInformation(t,l,e,n,s),m.lodInfos.set(e,o);let a=m.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],m.lowresCache.set(e,a),n>0&&!X(r)&&Ve(r,l);for(const u of W)u.onRegisteredNewMesh?.(r,o)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=p=="verbose",n=this.getAssignedLODInformation(t);if(!n)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=n?.key;let o;if(t.isTexture===!0){const a=t;a.source&&a.source[ue]&&(o=a.source[ue])}if(o||(o=m.lodInfos.get(s)),!o)p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let c=!1;const y=Array.isArray(o.lods);if(y&&e>=o.lods.length?c=!0:y||(c=!0),c)return this.lowresCache.get(s)}const a=Array.isArray(o.lods)?o.lods[e]?.path:o.lods;if(!a)return p&&!o["missing:uri"]&&(o["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,o)),null;const u=We(n.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!o.guid)return console.warn("missing pointer for glb/gltf texture",o),null;const c=u+"_"+o.guid,y=await this.queue.slot(u),_=this.previouslyLoaded.get(c);if(_!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${c}`);let f=await _.catch(S=>(console.error(`Error loading LOD ${e} from ${u}
2
- `,S),null)),w=!1;if(f==null||(f instanceof d.Texture&&t instanceof d.Texture?f.image?.data||f.source?.data?f=this.copySettings(t,f):(w=!0,this.previouslyLoaded.delete(c)):f instanceof d.BufferGeometry&&t instanceof d.BufferGeometry&&(f.attributes.position?.array||(w=!0,this.previouslyLoaded.delete(c)))),!w)return f}if(!y.use)return p&&console.log(`LOD ${e} was aborted: ${u}`),null;const M=o,I=new Promise(async(f,w)=>{if(He){const x=await(await je({})).load(u);if(x.textures.length>0)for(const h of x.textures){let g=h.texture;return m.assignLODInformation(n.url,g,s,e,void 0),t instanceof d.Texture&&(g=this.copySettings(t,g)),g&&(g.guid=M.guid),f(g)}if(x.geometries.length>0){const h=new Array;for(const g of x.geometries){const D=g.geometry;m.assignLODInformation(n.url,D,s,e,g.primitiveIndex),h.push(D)}return f(h)}return f(null)}const S=new q.GLTFLoader;ye(S),p&&(await new Promise(L=>setTimeout(L,1e3)),r&&console.warn("Start loading (delayed) "+u,M.guid));let B=u;if(M&&Array.isArray(M.lods)){const L=M.lods[e];L.hash&&(B+="?v="+L.hash)}const v=await S.loadAsync(B).catch(L=>(console.error(`Error loading LOD ${e} from ${u}
3
- `,L),f(null)));if(!v)return f(null);const z=v.parser;r&&console.log("Loading finished "+u,M.guid);let T=0;if(v.parser.json.textures){let L=!1;for(const x of v.parser.json.textures){if(x?.extensions){const h=x?.extensions[F];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){let x=await z.getDependency("texture",T);return x&&m.assignLODInformation(n.url,x,s,e,void 0),r&&console.log('change "'+t.name+'" → "'+x.name+'"',u,T,x,c),t instanceof d.Texture&&(x=this.copySettings(t,x)),x&&(x.guid=M.guid),f(x)}else p&&console.warn("Could not find texture with guid",M.guid,v.parser.json)}if(T=0,v.parser.json.meshes){let L=!1;for(const x of v.parser.json.meshes){if(x?.extensions){const h=x?.extensions[F];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){const x=await z.getDependency("mesh",T);if(r&&console.log(`Loaded Mesh "${x.name}"`,u,T,x,c),x.isMesh===!0){const h=x.geometry;return m.assignLODInformation(n.url,h,s,e,0),f(h)}else{const h=new Array;for(let g=0;g<x.children.length;g++){const D=x.children[g];if(D.isMesh===!0){const O=D.geometry;m.assignLODInformation(n.url,O,s,e,g),h.push(O)}}return f(h)}}else p&&console.warn("Could not find mesh with guid",M.guid,v.parser.json)}return f(null)});return this.previouslyLoaded.set(c,I),y.use(I),await I}else if(t instanceof d.Texture){r&&console.log("Load texture from uri: "+u);const y=await new d.TextureLoader().loadAsync(u);return y?(y.guid=o.guid,y.flipY=!1,y.needsUpdate=!0,y.colorSpace=t.colorSpace,r&&console.log(o,y)):p&&console.warn("failed loading",u),y}}return null}static maxConcurrent=50;static queue=new ze(m.maxConcurrent,{debug:p!=!1});static assignLODInformation(t,e,r,n,s){if(!e)return;e.userData||(e.userData={});const o=new Je(t,r,n,s);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?(p==="verbose"&&console.debug(`Copy texture settings
4
- `,t.uuid,`
5
- `,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 Je{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class de{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.add(t,e,r)})};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 n=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=n,this.ready=new Promise(s=>{this._resolve=s}),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,r){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)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){p&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,n+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const C=N("debugprogressive"),Ze=N("noprogressive"),ce=Symbol("Needle:LODSManager"),fe=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};let J=class b{static debugDrawLine;static getObjectLODState(t){return t[fe]}static addPlugin(t){W.push(t)}static removePlugin(t){const e=W.indexOf(t);e>=0&&W.splice(e,1)}static get(t,e){if(t[ce])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ce];const r=new b(t,{engine:"unknown",...e});return t[ce]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return W}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new de(this.#s,{...t});this._newPromiseGroups.push(r);const n=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Le()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new d.Clock;#s=0;#n=0;#i=0;#r=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;ne(this.renderer),this.renderer.render=function(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,C&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const o=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,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,r){if(this.pause)return;const s=this.renderer.renderLists.get(t,0).opaque;let o=!0;if(s.length===1){const l=s[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(o=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(o=!1),o){if(Ze||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,C&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,C&&console.warn("↑ Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),n=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of n){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")){C&&(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}if(C==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const c=Math.random()*16777215,y=new d.MeshStandardMaterial({color:c});a.object.material=y}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const o=r.transparent;for(const a of o){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[fe];if(s||(s=new et,r[fe]=s),s.frames++<2)return;for(const l of W)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const o=this.overrideLodLevel!==void 0?this.overrideLodLevel:E;o>=0?(P.mesh_lod=o,P.texture_lod=o):(this.calculateLodLevel(e,r,s,n,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod),r.material&&P.texture_lod>=0&&this.loadProgressiveTextures(r.material,P.texture_lod,o),p&&r.material&&!r.isGizmo&&Pe(r.material);for(const l of W)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lastLodLevel_Texture=P.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let n=!1;if((t[U]===void 0||e<t[U])&&(n=!0),r!==void 0&&r>=0&&(n=t[U]!=r,e=r),n){t[U]=e;const s=m.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});de.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[U]!=n,e=n),r){t[U]=e;const s=t.geometry,o=m.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return de.addPromise("mesh",t,o,this._newPromiseGroups),o}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,o=(r.y+n.y)*.5;return this._tempPtInside.set(s,o,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,n,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let l=10+1,a=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=m.getMeshLODExtension(e.geometry)?.lods,c=m.getPrimitiveIndex(e.geometry),y=u&&u.length>0,_=m.getMaterialMinMaxLODsCount(e.material),M=_.min_count!==1/0&&_.min_count>=0&&_.max_count>=0;if(!y&&!M){s.mesh_lod=0,s.texture_lod=0;return}y||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let G=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const f=e;if(!f.boundingBox)f.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!f[b.$skinnedMeshBoundsOffset]){const S=b.skinnedMeshBoundsFrameOffsetCounter++;f[b.$skinnedMeshBoundsOffset]=S}const w=f[b.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const S=X(f),B=f.geometry;S&&(f.geometry=S),f.computeBoundingBox(),f.geometry=B}}G=f.boundingBox}if(G){const f=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 h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(G),this._tempBox.applyMatrix4(e.matrixWorld),f.isPerspectiveCamera&&b.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&f.isPerspectiveCamera&&f.fov>70){const h=this._tempBox.min,g=this._tempBox.max;let D=h.x,O=h.y,$=g.x,K=g.y;const Z=2,oe=1.5,ee=(h.x+g.x)*.5,te=(h.y+g.y)*.5;D=(D-ee)*Z+ee,O=(O-te)*Z+te,$=($-ee)*Z+ee,K=(K-te)*Z+te;const Ce=D<0&&$>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),ke=O<0&&K>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ie=Math.max(Ce,ke);r.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&I>0&&w.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const S=t.matrixWorldInverse,B=this._tempBox2;B.copy(G),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(S);const v=B.getSize(this._tempBox2Size),z=Math.max(v.x,v.y);if(Math.max(w.x,w.y)!=0&&z!=0&&(w.z=v.z/Math.max(v.x,v.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,C&&b.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=b.corner0,D=b.corner1,O=b.corner2,$=b.corner3;g.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=g.x,O.copy(this._tempBox.max),O.y=g.y,$.copy(this._tempBox.max);const K=(g.z+$.z)*.5;g.z=D.z=O.z=$.z=K,g.applyMatrix4(h),D.applyMatrix4(h),O.applyMatrix4(h),$.applyMatrix4(h),b.debugDrawLine(g,D,255),b.debugDrawLine(g,O,255),b.debugDrawLine(D,$,255),b.debugDrawLine(O,$,255)}let L=999;if(u&&r.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const g=u[h],O=(g.densities?.[c]||g.density||1e-5)/r.lastScreenCoverage;if(c>0&&Le()&&!g.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.")),O<n){L=h;break}}L<l&&(l=L,a=!0)}if(a?s.mesh_lod=l:s.mesh_lod=r.lastLodLevel_Mesh,C&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${s.mesh_lod} (density: ${w.densities?.[c].toFixed(0)}) | ${e.name}`)}if(M){const f="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=_.max_count-1,C){const w=_.lods[_.max_count-1];C&&console.log(`First Texture LOD ${s.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let S=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(S*=1.5);const v=I/window.devicePixelRatio*S;let z=!1;for(let T=_.lods.length-1;T>=0;T--){const L=_.lods[T];if(!(f&&L.max_height>=2048)&&!(Te()&&L.max_height>4096)&&(L.max_height>v||!z&&T===0)){if(z=!0,s.texture_lod=T,C&&s.texture_lod<r.lastLodLevel_Texture){const x=L.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${x}px
6
- Screensize: ${v.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${w.toFixed(1)}
7
- ${e.name}`)}break}}}}else s.texture_lod=0}};class et{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}const _e=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let he=null;function Ae(){const i=tt();i&&(i.mapURLs(function(t){return Me(),t}),Me(),he?.disconnect(),he=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Ie(r)})})}),he.observe(document,{childList:!0,subtree:!0}))}function tt(){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 Me(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Ie(t)})}const ve=new WeakSet;let rt=0;function Ie(i){if(!i||ve.has(i))return null;ve.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++rt+`
8
- `,i.getAttribute("src"));let t=null,e=null,r=null;for(let n=i;n!=null;n=Object.getPrototypeOf(n)){const s=Object.getOwnPropertySymbols(n),o=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&o!=null&&(t=i[o].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let n=function(){if(r){let o=0,l=setInterval(()=>{if(o++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=J.get(t,{engine:"model-viewer"});return J.addPlugin(new st),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",o=>{o.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{n()}),()=>{s.disable()}}return null}class st{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,n){this.tryParseMeshLOD(e,n),this.tryParseTextureLOD(e,n)}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 r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let o=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let c=0;c<u.length;c++){const y=u[c],_=a[y];if(_?.isTexture===!0){const M=_.userData?.associations?.textures;if(M==null)continue;const I=r.parser.json.textures[M];if(!I){console.warn("Texture data not found for texture index "+M);continue}if(I?.extensions?.[F]){const G=I.extensions[F];G&&s&&m.registerTexture(s,_,G.lods.length,M,G)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)o(a);else o(l)}}tryParseMeshLOD(t,e){if(e[_e]==!0)return;e[_e]=!0;const r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(!n)return;const s=e.userData?.gltfExtensions?.[F];if(s&&n){const o=e.uuid;m.registerMesh(n,o,e,0,s.lods.length,s)}}}function nt(...i){let t,e,r,n;switch(i.length){case 2:[r,e]=i,n={};break;case 3:[r,e,n]=i;break;case 4:[t,e,r,n]=i;break;default:throw new Error("Invalid arguments")}ne(e),ye(r),xe(r,{progressive:!0,...n?.hints}),r.register(o=>new m(o));const s=J.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}Ae();if(!Ee){const i={gltfProgressive:{useNeedleProgressive:nt,LODsManager:J,configureLoader:xe,getRaycastMesh:X,useRaycastMeshes:Ne}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.LODsManager=J;exports.NEEDLE_progressive=m;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=ne;exports.getRaycastMesh=X;exports.setDracoDecoderLocation=be;exports.setKTX2TranscoderLocation=Oe;
@@ -1,8 +0,0 @@
1
- import{BufferGeometry as V,Mesh as j,Box3 as ie,Vector3 as I,Sphere as ve,CompressedTexture as $e,Texture as N,Matrix3 as Re,InterleavedBuffer as Ge,InterleavedBufferAttribute as je,BufferAttribute as Ne,TextureLoader as Ue,Matrix4 as Le,Clock as Fe,MeshStandardMaterial as We}from"./three-I__hSXzr.min.js";import{DRACOLoader as ze,KTX2Loader as qe,MeshoptDecoder as Ve,GLTFLoader as ae}from"./three-examples-D8zAE_7t.min.js";const Xe="";globalThis.GLTF_PROGRESSIVE_VERSION=Xe,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",U="https://cdn.needle.tools/static/three/0.179.1/basis2/";const He=k,Ke=U,_e=new URL(k+"draco_decoder.js");_e.searchParams.append("range","true"),fetch(_e,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===He&&be("./include/draco/"),U===Ke&&De("./include/ktx2/")}).finally(()=>{Me()});const Ye=()=>({dracoDecoderPath:k,ktx2TranscoderPath:U});function be(i){k=i,P&&P[ue]!=k?(console.debug("Updating Draco decoder path to "+i),P[ue]=k,P.setDecoderPath(k),P.preload()):console.debug("Setting Draco decoder path to "+i)}function De(i){U=i,C&&C.transcoderPath!=U?(console.debug("Updating KTX2 transcoder path to "+i),C.setTranscoderPath(U),C.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ee(i){return Me(),i?C.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:te}}function le(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(C),i.meshoptDecoder||i.setMeshoptDecoder(te)}const ue=Symbol("dracoDecoderPath");let P,te,C;function Me(){P||(P=new ze,P[ue]=k,P.setDecoderPath(k),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new qe,C.setTranscoderPath(U),C.init()),te||(te=Ve)}const de=new WeakMap;function ce(i,t){let e=de.get(i);e?e=Object.assign(e,t):e=t,de.set(i,e)}const Qe=ae.prototype.load;function Je(...i){const t=de.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Qe?.call(this,...i)}ae.prototype.load=Je,F("debugprogressive");function F(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}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 r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function Oe(){return re!==void 0||(re=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),F("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",re)),re}let re;function Se(){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 et{constructor(t=100,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:r,resolve:o}=this._queue.shift();o({use:s=>this.add(r,s)})}}}const tt=typeof window>"u"&&typeof document>"u",he=Symbol("needle:raycast-mesh");function z(i){return i?.[he]instanceof V?i[he]:null}function rt(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!z(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[he]=e}}function st(i=!0){if(i){if(X)return;const t=X=j.prototype.raycast;j.prototype.raycast=function(e,r){const o=this,s=z(o);let n;s&&o.isMesh&&(n=o.geometry,o.geometry=s),t.call(this,e,r),n&&(o.geometry=n)}}else{if(!X)return;j.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new V;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const R=new Array,h=F("debugprogressive");let H,W=-1;if(h){let i=function(){W+=1,W>=t&&(W=-1),console.log(`Toggle LOD level [${W}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(H=!H,console.log(`Toggle wireframe [${H}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(W=r,console.log(`Set LOD level to [${W}]`))})}function Pe(i){if(h&&H!==void 0)if(Array.isArray(i))for(const t of i)Pe(t);else i&&"wireframe"in i&&(i.wireframe=H===!0)}const K=new Array;let nt=0;const it=Oe()?2:10;function at(i){if(K.length<it){const e=K.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ye.createWorker(i||{});return K.push(r),r}const t=nt++%K.length;return K[t]}class ye{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const o=r.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const s of this._running)if(s.url===o.result.url){lt(o.result),s.resolve(o.result);const n=s.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(URL.createObjectURL(new Blob([`import '${new URL("./loader.worker-C1GG9A7C.js",import.meta.url).toString()}';`],{type:"text/javascript"})),{type:"module"});return new ye(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Ye();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("./three-I__hSXzr.min.js").then(a=>a.THREE);return new l})(),o=await this._webglRenderer);const s=ee(o).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 n={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function lt(i){for(const t of i.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const o=e.index;r.setIndex(ge(o))}for(const o in e.attributes){const s=e.attributes[o],n=ge(s);r.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const s=e.morphAttributes[o].map(n=>ge(n));r.morphAttributes[o]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ie,r.boundingBox.min=new I(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new I(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ve(new I(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)r.addGroup(o.start,o.count,o.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const o=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new $e(o,s,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new N(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new Re(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return i}function ge(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,o=new Ge(r,e.stride);t=new je(o,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new Ne(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=F("gltf-progressive-worker"),dt=F("gltf-progressive-reduce-mipmaps"),fe=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class g{get name(){return $}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const r=this,o="LODS:minmax",s=t[o];if(s!=null)return s;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[o]=e,e}if(h==="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&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let w=0;w<f.lods.length;w++){const v=f.lods[w];v.width&&(a.lods[w]=a.lods[w]||{min_height:1/0,max_height:0},a.lods[w].min_height=Math.min(a.lods[w].min_height,v.height),a.lods[w].max_height=Math.max(a.lods[w].max_height,v.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,o;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(o=this.lodInfos.get(s.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof j||t.isMesh===!0){const r=t.geometry,o=this.getAssignedLODInformation(r);if(!o)return Promise.resolve(null);for(const s of R)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,g.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const n=o.index||0;s=s[n]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:h&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else h&&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 r=t;if(Array.isArray(r.material)){const o=new Array;for(const s of r.material){const n=this.assignTextureLOD(s,e);o.push(n)}return Promise.all(o).then(s=>{const n=new Array;for(const l of s)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,o=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const n=r;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&n.uniforms[l].value!=f&&(n.uniforms[l].value=f,n.uniformsNeedUpdate=!0),f));o.push(u),s.push(l)}}}else for(const n of Object.keys(r)){const l=r[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,n);o.push(a),s.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof N||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):g.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(s?.isTexture===!0){if(s!=t&&r&&o){const n=r[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,n,s),null}if(dt&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&h&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[o]=s}return s}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}parser;url;constructor(t){const e=t.options.path;h&&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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&g.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let s=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(s=!0,g.registerTexture(this.url,n,o.lods?.length,r,o));s||this.parser.getDependency("texture",r).then(n=>{n&&g.registerTexture(this.url,n,o.lods?.length,r,o)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const n=this.parser.associations.get(s);n?.meshes===r&&g.registerMesh(this.url,o.guid,s,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,r,o,s)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${o}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${s.lods[0]?.width}x${s.lods[0]?.height}, uuid: ${e.uuid}`,s,e)}e.source&&(e.source[fe]=s);const n=s.guid;g.assignLODInformation(t,e,n,r,o),g.lodInfos.set(n,s),g.lowresCache.set(n,e)};static registerMesh=(t,e,r,o,s,n)=>{const l=r.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},n,r),g.assignLODInformation(t,l,e,o,s),g.lodInfos.set(e,n);let a=g.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],g.lowresCache.set(e,a),o>0&&!z(r)&&rt(r,l);for(const u of R)u.onRegisteredNewMesh?.(r,n)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[fe]&&(n=l.source[fe])}if(n||(n=g.lodInfos.get(s)),!n)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type,g.lodInfos);else{if(e>0){let u=!1;const f=Array.isArray(n.lods);if(f&&e>=n.lods.length?u=!0:f||(u=!0),u)return this.lowresCache.get(s)}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=Ze(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,f=await this.queue.slot(a),w=this.previouslyLoaded.get(u);if(w!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let p=await w.catch(y=>(console.error(`Error loading LOD ${e} from ${a}
2
- `,y),null)),m=!1;if(p==null||(p instanceof N&&t instanceof N?p.image?.data||p.source?.data?p=this.copySettings(t,p):(m=!0,this.previouslyLoaded.delete(u)):p instanceof V&&t instanceof V&&(p.attributes.position?.array||(m=!0,this.previouslyLoaded.delete(u)))),!m)return p}if(!f.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const v=n,T=new Promise(async(p,m)=>{if(ut){const x=await(await at({})).load(a);if(x.textures.length>0)for(const d of x.textures){let c=d.texture;return g.assignLODInformation(o.url,c,s,e,void 0),t instanceof N&&(c=this.copySettings(t,c)),c&&(c.guid=v.guid),p(c)}if(x.geometries.length>0){const d=new Array;for(const c of x.geometries){const L=c.geometry;g.assignLODInformation(o.url,L,s,e,c.primitiveIndex),d.push(L)}return p(d)}return p(null)}const y=new ae;le(y),h&&(await new Promise(x=>setTimeout(x,1e3)),r&&console.warn("Start loading (delayed) "+a,v.guid));let A=a;if(v&&Array.isArray(v.lods)){const x=v.lods[e];x.hash&&(A+="?v="+x.hash)}const _=await y.loadAsync(A).catch(x=>(console.error(`Error loading LOD ${e} from ${a}
3
- `,x),p(null)));if(!_)return p(null);const E=_.parser;r&&console.log("Loading finished "+a,v.guid);let D=0;if(_.parser.json.textures){let x=!1;for(const d of _.parser.json.textures){if(d?.extensions){const c=d?.extensions[$];if(c?.guid&&c.guid===v.guid){x=!0;break}}D++}if(x){let d=await E.getDependency("texture",D);return d&&g.assignLODInformation(o.url,d,s,e,void 0),r&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,D,d,u),t instanceof N&&(d=this.copySettings(t,d)),d&&(d.guid=v.guid),p(d)}else h&&console.warn("Could not find texture with guid",v.guid,_.parser.json)}if(D=0,_.parser.json.meshes){let x=!1;for(const d of _.parser.json.meshes){if(d?.extensions){const c=d?.extensions[$];if(c?.guid&&c.guid===v.guid){x=!0;break}}D++}if(x){const d=await E.getDependency("mesh",D);if(r&&console.log(`Loaded Mesh "${d.name}"`,a,D,d,u),d.isMesh===!0){const c=d.geometry;return g.assignLODInformation(o.url,c,s,e,0),p(c)}else{const c=new Array;for(let L=0;L<d.children.length;L++){const O=d.children[L];if(O.isMesh===!0){const S=O.geometry;g.assignLODInformation(o.url,S,s,e,L),c.push(S)}}return p(c)}}else h&&console.warn("Could not find mesh with guid",v.guid,_.parser.json)}return p(null)});return this.previouslyLoaded.set(u,T),f.use(T),await T}else if(t instanceof N){r&&console.log("Load texture from uri: "+a);const u=await new Ue().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,r&&console.log(n,u)):h&&console.warn("failed loading",a),u}}return null}static maxConcurrent=50;static queue=new et(g.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,r,o,s){if(!e)return;e.userData||(e.userData={});const n=new ct(t,r,o,s);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}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?(h==="verbose"&&console.debug(`Copy texture settings
4
- `,t.uuid,`
5
- `,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 ct{url;key;level;index;constructor(t,e,r,o){this.url=t,this.key=e,this.level=r,o!=null&&(this.index=o)}}class me{static addPromise=(t,e,r,o)=>{o.forEach(s=>{s.add(t,e,r)})};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(o=>{this._resolve=o}),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,r){if(this._resolved){h&&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 o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const B=F("debugprogressive"),ht=F("noprogressive"),pe=Symbol("Needle:LODSManager"),xe=Symbol("Needle:LODState"),G=Symbol("Needle:CurrentLOD"),M={mesh_lod:-1,texture_lod:-1};let Y=class b{static debugDrawLine;static getObjectLODState(t){return t[xe]}static addPlugin(t){R.push(t)}static removePlugin(t){const e=R.indexOf(t);e>=0&&R.splice(e,1)}static get(t,e){if(t[pe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[pe];const r=new b(t,{engine:"unknown",...e});return t[pe]=r,r}renderer;context;projectionScreenMatrix=new Le;get plugins(){return R}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new me(this.#s,{...t});this._newPromiseGroups.push(r);const o=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Se()&&performance.measure("LODsManager:awaitLoading",{start:o,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#n=new Fe;#s=0;#o=0;#i=0;#r=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;ee(this.renderer),this.renderer.render=function(r,o){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,o),e.onAfterRender(r,o,n)}}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,r){if(this.pause)return;const o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ht||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.renderer.renderLists.get(t,0),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of o){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")){B&&(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}if(B==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,w=new We({color:f});a.object.material=w}const u=a.object;(u instanceof j||u.isMesh)&&this.updateLODs(t,e,u,s)}const n=r.transparent;for(const a of n){const u=a.object;(u instanceof j||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof j||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,o){r.userData||(r.userData={});let s=r[xe];if(s||(s=new gt,r[xe]=s),s.frames++<2)return;for(const l of R)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=this.overrideLodLevel!==void 0?this.overrideLodLevel:W;n>=0?(M.mesh_lod=n,M.texture_lod=n):(this.calculateLodLevel(e,r,s,o,M),M.mesh_lod=Math.round(M.mesh_lod),M.texture_lod=Math.round(M.texture_lod)),M.mesh_lod>=0&&this.loadProgressiveMeshes(r,M.mesh_lod),r.material&&M.texture_lod>=0&&this.loadProgressiveTextures(r.material,M.texture_lod,n),h&&r.material&&!r.isGizmo&&Pe(r.material);for(const l of R)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,M);s.lastLodLevel_Mesh=M.mesh_lod,s.lastLodLevel_Texture=M.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let o=!1;if((t[G]===void 0||e<t[G])&&(o=!0),r!==void 0&&r>=0&&(o=t[G]!=r,e=r),o){t[G]=e;const s=g.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});me.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[G]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(r=t[G]!=o,e=o),r){t[G]=e;const s=t.geometry,n=g.assignMeshLOD(t,e).then(l=>(l&&t[G]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return me.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new ve;_tempBox=new ie;_tempBox2=new ie;tempMatrix=new Le;_tempWorldPosition=new I;_tempBoxSize=new I;_tempBox2Size=new I;static corner0=new I;static corner1=new I;static corner2=new I;static corner3=new I;static _tempPtInside=new I;static isInside(t,e){const r=t.min,o=t.max,s=(r.x+o.x)*.5,n=(r.y+o.y)*.5;return this._tempPtInside.set(s,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,o,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let n=10+1,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),f=a&&a.length>0,w=g.getMaterialMinMaxLODsCount(e.material),v=w.min_count!==1/0&&w.min_count>=0&&w.max_count>=0;if(!f&&!v){s.mesh_lod=0,s.texture_lod=0;return}f||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let p=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;if(!m.boundingBox)m.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!m[b.$skinnedMeshBoundsOffset]){const A=b.skinnedMeshBoundsFrameOffsetCounter++;m[b.$skinnedMeshBoundsOffset]=A}const y=m[b.$skinnedMeshBoundsOffset];if((r.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const A=z(m),_=m.geometry;A&&(m.geometry=A),m.computeBoundingBox(),m.geometry=_}}p=m.boundingBox}if(p){const m=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 d=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(d)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(p),this._tempBox.applyMatrix4(e.matrixWorld),m.isPerspectiveCamera&&b.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.isPerspectiveCamera&&m.fov>70){const d=this._tempBox.min,c=this._tempBox.max;let L=d.x,O=d.y,S=c.x,q=c.y;const Q=2,oe=1.5,J=(d.x+c.x)*.5,Z=(d.y+c.y)*.5;L=(L-J)*Q+J,O=(O-Z)*Q+Z,S=(S-J)*Q+J,q=(q-Z)*Q+Z;const Ce=L<0&&S>0?0:Math.min(Math.abs(d.x),Math.abs(c.x)),Be=O<0&&q>0?0:Math.min(Math.abs(d.y),Math.abs(c.y)),ne=Math.max(Ce,Be);r.lastCentrality=(oe-ne)*(oe-ne)*(oe-ne)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&T>0&&y.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const A=t.matrixWorldInverse,_=this._tempBox2;_.copy(p),_.applyMatrix4(e.matrixWorld),_.applyMatrix4(A);const E=_.getSize(this._tempBox2Size),D=Math.max(E.x,E.y);if(Math.max(y.x,y.y)!=0&&D!=0&&(y.z=E.z/Math.max(E.x,E.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,B&&b.debugDrawLine){const d=this.tempMatrix.copy(this.projectionScreenMatrix);d.invert();const c=b.corner0,L=b.corner1,O=b.corner2,S=b.corner3;c.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=c.x,O.copy(this._tempBox.max),O.y=c.y,S.copy(this._tempBox.max);const q=(c.z+S.z)*.5;c.z=L.z=O.z=S.z=q,c.applyMatrix4(d),L.applyMatrix4(d),O.applyMatrix4(d),S.applyMatrix4(d),b.debugDrawLine(c,L,255),b.debugDrawLine(c,O,255),b.debugDrawLine(L,S,255),b.debugDrawLine(O,S,255)}let x=999;if(a&&r.lastScreenCoverage>0)for(let d=0;d<a.length;d++){const c=a[d],L=(c.densities?.[u]||c.density||1e-5)/r.lastScreenCoverage;if(u>0&&Se()&&!c.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.")),L<o){x=d;break}}x<n&&(n=x,l=!0)}if(l?s.mesh_lod=n:s.mesh_lod=r.lastLodLevel_Mesh,B&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=a?.[s.mesh_lod];m&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${m.densities?.[u].toFixed(0)}) | ${e.name}`)}if(v){const m="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=w.max_count-1,B){const y=w.lods[w.max_count-1];B&&console.log(`First Texture LOD ${s.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let A=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(A*=1.5);const _=T/window.devicePixelRatio*A;let E=!1;for(let D=w.lods.length-1;D>=0;D--){const x=w.lods[D];if(!(m&&x.max_height>=2048)&&!(Oe()&&x.max_height>4096)&&(x.max_height>_||!E&&D===0)){if(E=!0,s.texture_lod=D,B&&s.texture_lod<r.lastLodLevel_Texture){const d=x.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${d}px
6
- Screensize: ${_.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
7
- ${e.name}`)}break}}}}else s.texture_lod=0}};class gt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new I;lastCentrality=0}const Te=Symbol("NEEDLE_mesh_lod"),se=Symbol("NEEDLE_texture_lod");let we=null;function Ae(){const i=ft();i&&(i.mapURLs(function(t){return Ie(),t}),Ie(),we?.disconnect(),we=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&ke(r)})})}),we.observe(document,{childList:!0,subtree:!0}))}function ft(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ae()}),null)}function Ie(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{ke(i)})}const Ee=new WeakSet;let mt=0;function ke(i){if(!i||Ee.has(i))return null;Ee.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++mt+`
8
- `,i.getAttribute("src"));let t=null,e=null,r=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let o=function(){if(r){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=Y.get(t,{engine:"model-viewer"});return Y.addPlugin(new pt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{s.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}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[se]==!0)return;e[se]=!0;const r=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),s=this.getUrl(o);if(s&&r&&e.material){let n=function(a){if(a[se]==!0)return;a[se]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const w=u[f],v=a[w];if(v?.isTexture===!0){const T=v.userData?.associations?.textures;if(T==null)continue;const p=r.parser.json.textures[T];if(!p){console.warn("Texture data not found for texture index "+T);continue}if(p?.extensions?.[$]){const m=p.extensions[$];m&&s&&g.registerTexture(s,v,m.lods.length,T,m)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Te]==!0)return;e[Te]=!0;const r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(!o)return;const s=e.userData?.gltfExtensions?.[$];if(s&&o){const n=e.uuid;g.registerMesh(o,n,e,0,s.lods.length,s)}}}function xt(...i){let t,e,r,o;switch(i.length){case 2:[r,e]=i,o={};break;case 3:[r,e,o]=i;break;case 4:[t,e,r,o]=i;break;default:throw new Error("Invalid arguments")}ee(e),le(r),ce(r,{progressive:!0,...o?.hints}),r.register(n=>new g(n));const s=Y.get(e);return o?.enableLODsManager!==!1&&s.enable(),s}if(Ae(),!tt){const i={gltfProgressive:{useNeedleProgressive:xt,LODsManager:Y,configureLoader:ce,getRaycastMesh:z,useRaycastMeshes:st}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{Y as LODsManager,g as NEEDLE_progressive,le as addDracoAndKTX2Loaders,ce as configureLoader,ee as createLoaders,z as getRaycastMesh,be as setDracoDecoderLocation,De as setKTX2TranscoderLocation};