@needle-tools/engine 4.8.9 → 4.9.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/gltf-progressive-DWiyqrwB.umd.cjs +8 -0
  3. package/dist/gltf-progressive-DhE1A6hX.min.js +8 -0
  4. package/dist/{gltf-progressive-BcHT3Nyo.js → gltf-progressive-egsMzRdv.js} +384 -369
  5. package/dist/{needle-engine.bundle-BdgNRB4B.js → needle-engine.bundle-BmZiljES.js} +7879 -7642
  6. package/dist/{needle-engine.bundle-TQyyn3pE.min.js → needle-engine.bundle-D_zlE1Pj.min.js} +142 -142
  7. package/dist/{needle-engine.bundle-DGca1cic.umd.cjs → needle-engine.bundle-n9XV_9nh.umd.cjs} +138 -138
  8. package/dist/needle-engine.js +305 -301
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/dist/{postprocessing-Ywv5oKkX.min.js → postprocessing-B_FzkwDz.min.js} +1 -1
  12. package/dist/{postprocessing-ORx-0eCx.js → postprocessing-DP1U_BpT.js} +2 -2
  13. package/dist/{postprocessing-CVb_x9YY.umd.cjs → postprocessing-DTX5VXrj.umd.cjs} +1 -1
  14. package/dist/rapier-BJaux8TQ.js +5217 -0
  15. package/dist/rapier-Bd0qRV1r.umd.cjs +1 -0
  16. package/dist/rapier-CnHGx3sO.min.js +1 -0
  17. package/dist/{three-Dceyffus.umd.cjs → three-BK56xWDs.umd.cjs} +24 -24
  18. package/dist/{three-BRSLmpyi.js → three-CsHK73Zc.js} +1 -0
  19. package/dist/{three-CsmWHVn7.min.js → three-TNFQHSFa.min.js} +25 -25
  20. package/dist/{three-examples-BX_Sktc9.min.js → three-examples-Bph291U2.min.js} +1 -1
  21. package/dist/{three-examples-CNexix3E.js → three-examples-BvMpKSun.js} +1 -1
  22. package/dist/{three-examples-DWxXVnws.umd.cjs → three-examples-C9WfZu-X.umd.cjs} +1 -1
  23. package/dist/{three-mesh-ui-gqAXlGNB.js → three-mesh-ui-CN6aRT7i.js} +1 -1
  24. package/dist/{three-mesh-ui-Cdh2iW8b.umd.cjs → three-mesh-ui-DnxkZWNA.umd.cjs} +1 -1
  25. package/dist/{three-mesh-ui-Bwy12Qvg.min.js → three-mesh-ui-n_qS2BM-.min.js} +1 -1
  26. package/dist/{vendor-xfQ8tKF3.umd.cjs → vendor-8le8g4MS.umd.cjs} +1 -1
  27. package/dist/{vendor-Z4SPrTcP.js → vendor-Msb9AgYE.js} +1 -1
  28. package/dist/{vendor-C43vobGc.min.js → vendor-yDFiCnCw.min.js} +1 -1
  29. package/lib/engine/codegen/register_types.js +4 -0
  30. package/lib/engine/codegen/register_types.js.map +1 -1
  31. package/lib/engine/engine_physics_rapier.js +3 -2
  32. package/lib/engine/engine_physics_rapier.js.map +1 -1
  33. package/lib/engine/engine_three_utils.js +4 -2
  34. package/lib/engine/engine_three_utils.js.map +1 -1
  35. package/lib/engine/xr/NeedleXRController.js +30 -9
  36. package/lib/engine/xr/NeedleXRController.js.map +1 -1
  37. package/lib/engine/xr/NeedleXRSession.js +18 -13
  38. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  39. package/lib/engine-components/api.d.ts +1 -0
  40. package/lib/engine-components/api.js +1 -0
  41. package/lib/engine-components/api.js.map +1 -1
  42. package/lib/engine-components/codegen/components.d.ts +3 -0
  43. package/lib/engine-components/codegen/components.js +3 -0
  44. package/lib/engine-components/codegen/components.js.map +1 -1
  45. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +1 -1
  46. package/lib/engine-components/splines/Spline.d.ts +58 -0
  47. package/lib/engine-components/splines/Spline.js +270 -0
  48. package/lib/engine-components/splines/Spline.js.map +1 -0
  49. package/lib/engine-components/splines/SplineUtils.d.ts +12 -0
  50. package/lib/engine-components/splines/SplineUtils.js +33 -0
  51. package/lib/engine-components/splines/SplineUtils.js.map +1 -0
  52. package/lib/engine-components/splines/SplineWalker.d.ts +47 -0
  53. package/lib/engine-components/splines/SplineWalker.js +113 -0
  54. package/lib/engine-components/splines/SplineWalker.js.map +1 -0
  55. package/lib/engine-components/splines/index.d.ts +3 -0
  56. package/lib/engine-components/splines/index.js +4 -0
  57. package/lib/engine-components/splines/index.js.map +1 -0
  58. package/lib/engine-components/webxr/controllers/XRControllerModel.js +2 -2
  59. package/lib/engine-components/webxr/controllers/XRControllerModel.js.map +1 -1
  60. package/lib/engine-components/webxr/controllers/XRControllerMovement.js +4 -0
  61. package/lib/engine-components/webxr/controllers/XRControllerMovement.js.map +1 -1
  62. package/package.json +3 -3
  63. package/plugins/vite/alias.js +3 -2
  64. package/src/engine/codegen/register_types.ts +4 -0
  65. package/src/engine/engine_physics_rapier.ts +3 -2
  66. package/src/engine/engine_three_utils.ts +5 -2
  67. package/src/engine/xr/NeedleXRController.ts +39 -15
  68. package/src/engine/xr/NeedleXRSession.ts +18 -13
  69. package/src/engine-components/api.ts +1 -0
  70. package/src/engine-components/codegen/components.ts +3 -0
  71. package/src/engine-components/splines/Spline.ts +284 -0
  72. package/src/engine-components/splines/SplineUtils.ts +32 -0
  73. package/src/engine-components/splines/SplineWalker.ts +106 -0
  74. package/src/engine-components/splines/index.ts +3 -0
  75. package/src/engine-components/webxr/controllers/XRControllerModel.ts +3 -2
  76. package/src/engine-components/webxr/controllers/XRControllerMovement.ts +5 -1
  77. package/dist/gltf-progressive-CH3Q4H06.umd.cjs +0 -8
  78. package/dist/gltf-progressive-DR6HqF_h.min.js +0 -8
  79. package/dist/rapier--oeYP_h7.umd.cjs +0 -1
  80. package/dist/rapier-B3xpyPtq.js +0 -5142
  81. package/dist/rapier-CyWhltHY.min.js +0 -1
@@ -0,0 +1,284 @@
1
+
2
+ import { BufferGeometry, CatmullRomCurve3, CubicBezierCurve3, Curve, Line, LineBasicMaterial,LineCurve3, Object3D, Quaternion, Vector3 } from "three";
3
+
4
+ import { Mathf } from "../../engine/engine_math.js";
5
+ import { serializeable } from "../../engine/engine_serialization.js";
6
+ import { getParam } from "../../engine/engine_utils.js";
7
+ import { Behaviour } from "../Component.js";
8
+
9
+ const debug = getParam("debugsplines");
10
+
11
+ export class SplineData {
12
+ @serializeable(Vector3)
13
+ position: Vector3 = new Vector3();
14
+
15
+ @serializeable(Quaternion)
16
+ rotation: Quaternion = new Quaternion();
17
+
18
+ @serializeable(Vector3)
19
+ tangentIn: Vector3 = new Vector3();
20
+
21
+ @serializeable(Vector3)
22
+ tangentOut: Vector3 = new Vector3();
23
+ }
24
+
25
+ // enum SplineTypeEnum {
26
+ // CatmullRom = 0,
27
+ // Bezier = 1,
28
+ // Linear = 2
29
+ // }
30
+ // type SplineType = "CatmullRom" | "Bezier" | "Linear";
31
+
32
+
33
+ //@dont-generate-component
34
+ /**
35
+ * Holds spline data and generates a spline curve. Use with SplineWalker to move objects along the spline or call getPointAt to sample points on the spline.
36
+ * The spline is defined by an array of knots (SplineData) which define position, rotation and tangents.
37
+ *
38
+ * You can create a SplineContainer from an array of points using the static method 'createFromPoints'.
39
+ */
40
+ export class SplineContainer extends Behaviour {
41
+
42
+ /**
43
+ * Adds a knot to the end of the spline.
44
+ */
45
+ addKnot(knot: SplineData | { position: Vector3 }): SplineContainer {
46
+ if (knot instanceof SplineData) {
47
+ this.spline.push(knot);
48
+ this._isDirty = true;
49
+ }
50
+ else {
51
+ const k = new SplineData();
52
+ k.position.copy(knot.position);
53
+ this.spline.push(k);
54
+ this._isDirty = true;
55
+ }
56
+ return this;
57
+ }
58
+
59
+ /**
60
+ * Removes a knot by index or by reference.
61
+ */
62
+ removeKnot(index: number | SplineData): SplineContainer {
63
+ if (typeof index === "number") {
64
+ this.spline.splice(index, 1);
65
+ this._isDirty = true;
66
+ } else {
67
+ const i = this.spline.indexOf(index);
68
+ if (i !== -1) {
69
+ this.spline.splice(i, 1);
70
+ this._isDirty = true;
71
+ }
72
+ }
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * Gets a point on the spline in world space.
78
+ */
79
+ getPointAt(t: number, target?: Vector3): Vector3 {
80
+ if (!this.curve) return new Vector3();
81
+ const pos = this.curve.getPointAt(Mathf.clamp01(t), target);
82
+ const worldMatrix = this.gameObject.matrixWorld ?? undefined;
83
+ if (worldMatrix) {
84
+ pos.applyMatrix4(worldMatrix);
85
+ }
86
+ return pos;
87
+ }
88
+
89
+ /**
90
+ * Marks the spline as dirty, causing it to be rebuilt on the next update.
91
+ */
92
+ markDirty() {
93
+ this._isDirty = true;
94
+ }
95
+
96
+ /**
97
+ * Gets the tangent vector on the spline in world space.
98
+ */
99
+ getTangentAt(t: number, target?: Vector3): Vector3 {
100
+ if (!this.curve) return target ?? new Vector3();
101
+ const wr = this.gameObject.worldQuaternion;
102
+ return this.curve.getTangentAt(Mathf.clamp01(t), target).applyQuaternion(wr);
103
+ }
104
+
105
+ @serializeable()
106
+ set closed(value: boolean) {
107
+ this._closed = value;
108
+ this._isDirty = true;
109
+ }
110
+ get closed() { return this._closed; }
111
+ private _closed: boolean = false;
112
+
113
+
114
+ /** Spline data. Call 'markDirty' if modified */
115
+ @serializeable(SplineData)
116
+ spline: SplineData[] = [];
117
+
118
+ /** Enable to render the spline curve for debugging */
119
+ set debug(debug: boolean) {
120
+ if (debug && !this._builtCurve) this.buildCurve();
121
+ if (!this._debugLine) return;
122
+ this._debugLine.visible = debug;
123
+ }
124
+
125
+ /** Gets the spline curve generated from the 'spline' data */
126
+ get curve(): Curve<Vector3> | null {
127
+ return this._curve;
128
+ }
129
+
130
+ get isDirty() { return this._isDirty; }
131
+
132
+ private _isDirty: boolean = false;
133
+
134
+ private _curve: Curve<Vector3> | null = null;
135
+ private _builtCurve: boolean = false;
136
+ private _debugLine: Object3D | null = null;
137
+
138
+ /** @internal */
139
+ awake() {
140
+ if (debug) {
141
+ console.log(`[Spline] ${this.name}`, this);
142
+ this.buildCurve();
143
+ }
144
+ }
145
+
146
+ /** @internal */
147
+ update() {
148
+ if (this._isDirty) {
149
+ this.buildCurve(true);
150
+ }
151
+ if (this._debugLine && this._debugLine.parent !== this.gameObject) this.gameObject.add(this._debugLine);
152
+ }
153
+
154
+ private buildCurve(force: boolean = false) {
155
+ if (this._builtCurve && !force) return;
156
+ this._builtCurve = true;
157
+
158
+ if (!this.spline) {
159
+ console.error("[Spline] Can not build curve, no spline data", this.name);
160
+ return;
161
+ }
162
+ this._isDirty = false;
163
+ this._curve = createCatmullRomCurve(this.spline, this.closed);
164
+ this.buildDebugCurve();
165
+ // TODO: Unity supports spline interpolation type per knot which we don't support right now. Additionally EditType is deprecated. For simplicity we're just supporting CatmullRom for now.
166
+ // switch (this.editType) {
167
+ // case SplineType.CatmullRom:
168
+ // this.createCatmullRomCurve();
169
+ // break;
170
+ // case SplineType.Bezier:
171
+ // console.warn("Bezier spline not implemented yet", this.name);
172
+ // this.createCatmullRomCurve();
173
+ // // this.createBezierCurve();
174
+ // break;
175
+ // case SplineType.Linear:
176
+ // this.createLinearCurve();
177
+ // break;
178
+ // }
179
+ }
180
+
181
+ private buildDebugCurve() {
182
+ if (debug && this.spline && this._curve) {
183
+ this._debugLine?.removeFromParent();
184
+ this._debugLine = null;
185
+
186
+ const material = new LineBasicMaterial({
187
+ color: 0x6600ff,
188
+ });
189
+ const res = this.spline.length * 10;
190
+ const splinePoints = this._curve.getPoints(res);
191
+ const geometry = new BufferGeometry().setFromPoints(splinePoints);
192
+ this._debugLine = new Line(geometry, material);
193
+ this.gameObject?.add(this._debugLine);
194
+ }
195
+ }
196
+ }
197
+
198
+
199
+ function createCatmullRomCurve(data: SplineData[], closed: boolean): CatmullRomCurve3 {
200
+ const points = data.map(knot => new Vector3(-knot.position.x, knot.position.y, knot.position.z));
201
+ if (points.length === 1) points.push(points[0]);
202
+ const averageTension = data.reduce((acc, knot) => acc + Math.abs(knot.tangentOut.x) + Math.abs(knot.tangentOut.y) + Math.abs(knot.tangentOut.z), 0) / data.length;
203
+ const tension = Mathf.remap(averageTension, 0, 0.3, 0, .5);
204
+ return new CatmullRomCurve3(points, closed, "catmullrom", tension);
205
+ }
206
+
207
+ function createLinearCurve(data: SplineData[], closed: boolean): LineCurve3 | null {
208
+ if (!data || data.length < 2) return null;
209
+ const points = data.map(knot => new Vector3(-knot.position.x, knot.position.y, knot.position.z));
210
+ if (closed) points.push(points[0]);
211
+ return new LineCurve3(points.at(0), points.at(1));
212
+ }
213
+
214
+
215
+ // function createBezierCurve(data: SplineData[], closed: boolean): CubicBezierCurve3 | null {
216
+ // if (!data || data.length < 2) return null;
217
+
218
+ // for (let k = 0; k < data.length; k++) {
219
+ // const k0 = data[k];
220
+ // let nextIndex = k + 1;
221
+ // if (nextIndex >= data.length) {
222
+ // if (!closed) break;
223
+ // nextIndex = 0;
224
+ // }
225
+ // const k1 = data[nextIndex];
226
+ // // points
227
+ // const p0 = new Vector3(-k0.position.x, k0.position.y, k0.position.z);
228
+ // const p1 = new Vector3(-k1.position.x, k1.position.y, k1.position.z);
229
+ // // tangents
230
+ // const t0 = new Vector3(-k0.tangentOut.x, k0.tangentOut.y, k0.tangentOut.z);
231
+ // const t1 = new Vector3(-k1.tangentIn.x, k1.tangentIn.y, k1.tangentIn.z);
232
+ // // rotations
233
+ // // const q0 = k0.rotation;// new Quaternion(k0.rotation.value.x, k0.rotation.value.y, k0.rotation.value.z, k0.rotation.value.w);
234
+ // // const q1 = k1.rotation;// new Quaternion(k1.rotation.value.x, k1.rotation.value.y, k1.rotation.value.z, k1.rotation.value.w);
235
+ // // const a = new Vector3(0,1,0);
236
+ // // const angle = Math.PI*.5;
237
+ // // t0.sub(p0).applyQuaternion(q0).add(p0);
238
+ // // t1.sub(p1).applyQuaternion(q1).add(p1);
239
+ // t0.add(p0);
240
+ // // t0.applyQuaternion(q0);
241
+ // t1.add(p1);
242
+ // const curve = new CubicBezierCurve3(p0, t0, t1, p1);
243
+ // return curve;
244
+ // }
245
+ // return null;
246
+ // }
247
+
248
+ // class SplineCurve {
249
+
250
+ // private spline: Spline;
251
+
252
+ // constructor(spline: Spline) {
253
+ // this.spline = spline;
254
+ // }
255
+
256
+ // getPoints(num: number): Vector3[] {
257
+ // const points: Vector3[] = [];
258
+ // const samplePerKnot = num / this.spline.length;
259
+ // for (let k = 1; k < this.spline.length; k++) {
260
+ // const cur = this.spline[k];
261
+ // const prev = this.spline[k - 1];
262
+
263
+ // for (let i = 0; i < samplePerKnot; i++) {
264
+ // const t = i / (samplePerKnot - 1);
265
+ // console.log(CurveUtils);
266
+ // const x = this.interpolate(-prev.Position.x, -cur.Position.x, -prev.tangentOut.x, -cur.TangentIn.x, t);
267
+ // const y = this.interpolate(prev.Position.y, cur.Position.y, prev.tangentOut.y, cur.TangentIn.y, t);
268
+ // const z = this.interpolate(prev.Position.z, cur.Position.z, prev.tangentOut.z, cur.TangentIn.z, t);
269
+ // points.push(new Vector3(x, y, z));
270
+ // }
271
+ // }
272
+
273
+ // return points;
274
+ // }
275
+
276
+ // interpolate(p0, p1, p2, p3, t) {
277
+
278
+ // var v0 = (p2 - p0) * 0.5;
279
+ // var v1 = (p3 - p1) * 0.5;
280
+ // var t2 = t * t;
281
+ // var t3 = t * t2;
282
+ // return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
283
+ // }
284
+ // }
@@ -0,0 +1,32 @@
1
+ import { Vector3 } from "three";
2
+
3
+ import { Mathf } from "../../engine/engine_math.js";
4
+ import { SplineContainer, SplineData } from "./Spline.js";
5
+
6
+
7
+ export namespace SplineUtils {
8
+
9
+ /**
10
+ * Creates a SplineContainer from an array of points.
11
+ * @param positions The positions of the knots.
12
+ * @param closed Whether the spline is closed (the last knot connects to the first).
13
+ * @param tension The tension of the spline. 0 is no tension, 1 is high tension (straight lines between knots). Default is 0.75.
14
+ * @return The created SplineContainer component - add it to an Object3D to use it.
15
+ */
16
+ export function createFromPoints(positions: Vector3[], closed: boolean = false, tension: number = .75): SplineContainer {
17
+ const spline = new SplineContainer();
18
+ const tangentFactor = 1 - Mathf.clamp(tension, 0, 1);
19
+ positions.forEach((pos, index) => {
20
+ const tangent = new Vector3();
21
+ if (index < positions.length - 1) tangent.subVectors(positions[index + 1], pos).normalize().multiplyScalar(tangentFactor);
22
+ else if (closed && positions.length > 1) tangent.subVectors(positions[0], pos).normalize().multiplyScalar(tangentFactor);
23
+ const knot = new SplineData();
24
+ knot.position.copy(pos);
25
+ knot.tangentIn.copy(tangent);
26
+ knot.tangentOut.copy(tangent);
27
+ spline.addKnot(knot);
28
+ });
29
+ spline.closed = closed;
30
+ return spline;
31
+ }
32
+ }
@@ -0,0 +1,106 @@
1
+
2
+ import { Object3D } from "three"
3
+
4
+ import { Mathf } from "../../engine/engine_math.js";
5
+ import { serializeable } from "../../engine/engine_serialization_decorator.js";
6
+ import { Behaviour } from "../Component.js";
7
+ import { SplineContainer } from "./Spline.js";
8
+
9
+ /**
10
+ * Moves an object along a spline. Use this with a SplineContainer component.
11
+ */
12
+ export class SplineWalker extends Behaviour {
13
+
14
+ /**
15
+ * The spline to use/walk along. Add a SplineContainer component to an object and assign it here.
16
+ */
17
+ @serializeable(SplineContainer)
18
+ spline: SplineContainer | null = null;
19
+
20
+ /** The object to move along the spline. If object is undefined then the spline walker will use the gameObject the component has been added to
21
+ * @default undefined
22
+ */
23
+ @serializeable(Object3D)
24
+ object?: Object3D | null = undefined;
25
+
26
+ /** The object to look at while moving along the spline
27
+ * @default null
28
+ */
29
+ @serializeable(Object3D)
30
+ lookAt: Object3D | null = null;
31
+
32
+ /**
33
+ * When clamp is set to true, the position01 value will be clamped between 0 and 1 and the object will not loop the spline.
34
+ * @default false
35
+ */
36
+ @serializeable()
37
+ clamp: boolean = false;
38
+
39
+ /** The current position on the spline. The value ranges from 0 (start of the spline curve) to 1 (end of the spline curve)
40
+ * @default 0
41
+ */
42
+ // @type float
43
+ @serializeable()
44
+ get position01(): number {
45
+ return this._position01;
46
+ }
47
+ set position01(v: number) {
48
+ this._position01 = v;
49
+ this.updateFromPosition();
50
+ }
51
+
52
+ /** Resets the position to 0 */
53
+ reset() {
54
+ this._position01 = 0;
55
+ }
56
+
57
+ /**
58
+ * If true the SplineWalker will automatically move along the spline
59
+ * @default true
60
+ */
61
+ @serializeable()
62
+ autoRun: boolean = true;
63
+
64
+ /**
65
+ * The duration in seconds it takes to complete the whole spline when autoWalk is enabled.
66
+ * @default 10
67
+ */
68
+ @serializeable()
69
+ duration: number = 10;
70
+
71
+
72
+ // #region internal
73
+
74
+ private _position01: number = 0;
75
+
76
+ /** @internal */
77
+ start() {
78
+ if(this.object === undefined) this.object = this.gameObject;
79
+ this.updateFromPosition();
80
+ }
81
+
82
+ update() {
83
+ if (this.autoRun) {
84
+ this._position01 += this.context.time.deltaTime / this.duration;
85
+ this.updateFromPosition();
86
+ }
87
+ }
88
+
89
+
90
+ private updateFromPosition() {
91
+ if (!this.spline || !this.spline.curve) return;
92
+ if (!this.object) return;
93
+
94
+ if (this.clamp) this._position01 = Mathf.clamp01(this._position01);
95
+ else this._position01 = this._position01 % 1;
96
+
97
+ const t = this._position01 >= 1 ? 1 : this._position01 % 1;
98
+ const pt = this.spline.getPointAt(t);
99
+ this.object.worldPosition = pt;
100
+ if (!this.lookAt) {
101
+ const tan = this.spline.getTangentAt(t);
102
+ this.object.lookAt(pt.add(tan));
103
+ }
104
+ else this.object.lookAt(this.lookAt.worldPosition);
105
+ }
106
+ }
@@ -0,0 +1,3 @@
1
+ export { SplineContainer } from "./Spline.js";
2
+ export { SplineUtils } from "./SplineUtils.js";
3
+ export { SplineWalker } from "./SplineWalker.js";
@@ -63,9 +63,10 @@ export class XRControllerModel extends Behaviour {
63
63
  const isSupportedSession = args.xr.isVR || args.xr.isPassThrough;
64
64
  if (!isSupportedSession) return;
65
65
 
66
- const { controller } = args;
66
+ console.debug("XR Controller Added", args.controller.side, args.controller.index);
67
+ // return;
67
68
 
68
- if (debug) console.warn("Add Controller Model for", controller.side, controller.index)
69
+ const { controller } = args;
69
70
 
70
71
  if (this.createControllerModel || this.createHandModel) {
71
72
  if (controller.hand) {
@@ -8,10 +8,11 @@ import { Gizmos } from "../../../engine/engine_gizmos.js";
8
8
  import { Mathf } from "../../../engine/engine_math.js";
9
9
  import type { RaycastTestObjectCallback } from "../../../engine/engine_physics.js";
10
10
  import { serializable } from "../../../engine/engine_serialization.js"
11
- import { getTempQuaternion, getTempVector, getWorldPosition, getWorldQuaternion, getWorldScale } from "../../../engine/engine_three_utils.js";
11
+ import { getTempVector, getWorldPosition, getWorldQuaternion, getWorldScale } from "../../../engine/engine_three_utils.js";
12
12
  import type { IGameObject } from "../../../engine/engine_types.js";
13
13
  import { getParam } from "../../../engine/engine_utils.js";
14
14
  import { NeedleXRController, type NeedleXREventArgs, NeedleXRSession } from "../../../engine/engine_xr.js";
15
+ import { isDevEnvironment } from "../../../engine/debug/index.js";
15
16
  import { Behaviour, GameObject } from "../../Component.js"
16
17
  import { hasPointerEventComponent } from "../../ui/PointerEvents.js";
17
18
  import { TeleportTarget } from "../TeleportTarget.js";
@@ -131,6 +132,9 @@ export class XRControllerMovement extends Behaviour implements XRMovementBehavio
131
132
  vec.applyQuaternion(controller.xr.poseOrientation);
132
133
  vec.y = 0;
133
134
  vec.applyQuaternion(rig.worldQuaternion);
135
+ if (isDevEnvironment() && Number.isNaN(vec.x)) {
136
+ console.error("Stick movement resulted in NaN", { stick, vec });
137
+ }
134
138
  rig.position.add(vec);
135
139
  // if we dont do this here the XRControllerModel will be frame-delayed
136
140
  // maybe we need to introduce a priority order for XR components
@@ -1,8 +0,0 @@
1
- "use strict";const d=require("./three-Dceyffus.umd.cjs"),q=require("./three-examples-DWxXVnws.umd.cjs");var ae=typeof document<"u"?document.currentScript:null;const be="3.3.2";globalThis.GLTF_PROGRESSIVE_VERSION=be;console.debug(`[gltf-progressive] version ${be}`);let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",N="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Be=k,$e=N,ve=new URL(k+"draco_decoder.js");ve.searchParams.append("range","true");fetch(ve,{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&&Oe("./include/draco/"),N===$e&&Se("./include/ktx2/")}).finally(()=>{Te()});const Ge=()=>({dracoDecoderPath:k,ktx2TranscoderPath:N});function Oe(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 Se(i){N=i,R&&R.transcoderPath!=N?(console.debug("Updating KTX2 transcoder path to "+i),R.setTranscoderPath(N),R.init()):console.debug("Setting KTX2 transcoder path to "+i)}function ne(i){return Te(),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 Te(){A||(A=new q.DRACOLoader,A[ge]=k,A.setDecoderPath(k),A.setDecoderConfig({type:"js"}),A.preload()),R||(R=new q.KTX2Loader,R.setTranscoderPath(N),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;V("debugprogressive");function V(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 Ee(i,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||i===void 0)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}let j;function Pe(){return j!==void 0||(j=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),V("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",j)),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 We{maxConcurrent;_running=new Map;_queue=[];debug=!1;constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}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 ze=typeof window>"u"&&typeof document>"u",me=Symbol("needle:raycast-mesh");function X(i){return i?.[me]instanceof d.BufferGeometry?i[me]:null}function Ne(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!X(i)){const r=qe(t);r.userData={isRaycastMesh:!0},i[me]=r}}function Ve(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 E=new Array,p=V("debugprogressive");let re,z=-1;if(p){let t=function(){z+=1,z>=i&&(z=-1),console.log(`Toggle LOD level [${z}]`)},i=6;window.addEventListener("keyup",e=>{e.key==="p"&&t(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(z=r,console.log(`Set LOD level to [${z}]`))})}function Ae(i){if(p)if(Array.isArray(i))for(const t of i)Ae(t);else i&&"wireframe"in i&&(i.wireframe=re===!0)}const H=new Array;let Xe=0;const Ke=Pe()?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{worker;static async createWorker(t){const e=new Worker(new URL("/loader.worker-CiTwpNPW.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:ae&&ae.tagName.toUpperCase()==="SCRIPT"&&ae.src||new URL("gltf-progressive-CH3Q4H06.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-Dceyffus.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;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"})}}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=V("gltf-progressive-worker"),Qe=V("gltf-progressive-reduce-mipmaps"),ue=Symbol("needle-progressive-texture"),F="NEEDLE_progressive";class x{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 m=0;m<c.lods.length;m++){const _=c.lods[m];_.width&&(a.lods[m]=a.lods[m]||{min_height:1/0,max_height:0},a.lods[m].min_height=Math.min(a.lods[m].min_height,_.height),a.lods[m].max_height=Math.max(a.lods[m].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 E)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,x.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):x.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&&x.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,x.registerTexture(this.url,o,n.lods?.length,r,n));s||this.parser.getDependency("texture",r).then(o=>{o&&x.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&&x.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(`> 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;x.assignLODInformation(t,e,o,r,n),x.lodInfos.set(o,s),x.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),x.assignLODInformation(t,l,e,n,s),x.lodInfos.set(e,o);let a=x.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],x.lowresCache.set(e,a),n>0&&!X(r)&&Ne(r,l);for(const u of E)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=x.lodInfos.get(s)),o){if(e>0){let c=!1;const m=Array.isArray(o.lods);if(m&&e>=o.lods.length?c=!0:m||(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=Ee(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,m=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(!m.use)return p&&console.log(`LOD ${e} was aborted: ${u}`),null;const M=o,C=new Promise(async(f,w)=>{if(He){const y=await(await je({})).load(u);if(y.textures.length>0)for(const h of y.textures){let g=h.texture;return x.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(y.geometries.length>0){const h=new Array;for(const g of y.geometries){const b=g.geometry;x.assignLODInformation(n.url,b,s,e,g.primitiveIndex),h.push(b)}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 D=await S.loadAsync(B).catch(L=>(console.error(`Error loading LOD ${e} from ${u}
3
- `,L),f(null)));if(!D)return f(null);const W=D.parser;r&&console.log("Loading finished "+u,M.guid);let T=0;if(D.parser.json.textures){let L=!1;for(const y of D.parser.json.textures){if(y?.extensions){const h=y?.extensions[F];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){let y=await W.getDependency("texture",T);return y&&x.assignLODInformation(n.url,y,s,e,void 0),r&&console.log('change "'+t.name+'" → "'+y.name+'"',u,T,y,c),t instanceof d.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=M.guid),f(y)}else p&&console.warn("Could not find texture with guid",M.guid,D.parser.json)}if(T=0,D.parser.json.meshes){let L=!1;for(const y of D.parser.json.meshes){if(y?.extensions){const h=y?.extensions[F];if(h?.guid&&h.guid===M.guid){L=!0;break}}T++}if(L){const y=await W.getDependency("mesh",T);if(r&&console.log(`Loaded Mesh "${y.name}"`,u,T,y,c),y.isMesh===!0){const h=y.geometry;return x.assignLODInformation(n.url,h,s,e,0),f(h)}else{const h=new Array;for(let g=0;g<y.children.length;g++){const b=y.children[g];if(b.isMesh===!0){const O=b.geometry;x.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,D.parser.json)}return f(null)});return this.previouslyLoaded.set(c,C),m.use(C),await C}else if(t instanceof d.Texture){r&&console.log("Load texture from uri: "+u);const m=await new d.TextureLoader().loadAsync(u);return m?(m.guid=o.guid,m.flipY=!1,m.needsUpdate=!0,m.colorSpace=t.colorSpace,r&&console.log(o,m)):p&&console.warn("failed loading",u),m}}else p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new We(x.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 I=V("debugprogressive"),Ze=V("noprogressive"),ce=Symbol("Needle:LODSManager"),fe=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};let Q=class v{static debugDrawLine;static getObjectLODState(t){return t[fe]}static addPlugin(t){E.push(t)}static removePlugin(t){const e=E.indexOf(t);e>=0&&E.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 v(t,{engine:"unknown",...e});return t[ce]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return E}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,I&&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,I&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,I&&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")){I&&(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(I==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const c=Math.random()*16777215,m=new d.MeshStandardMaterial({color:c});a.object.material=m}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 E)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const o=this.overrideLodLevel!==void 0?this.overrideLodLevel:z;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&&Ae(r.material);for(const l of E)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=x.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=x.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(I&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=x.getMeshLODExtension(e.geometry)?.lods,c=x.getPrimitiveIndex(e.geometry),m=u&&u.length>0,_=x.getMaterialMinMaxLODsCount(e.material),M=_.min_count!==1/0&&_.min_count>=0&&_.max_count>=0;if(!m&&!M){s.mesh_lod=0,s.texture_lod=0;return}m||(a=!0,l=0);const C=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[v.$skinnedMeshBoundsOffset]){const S=v.skinnedMeshBoundsFrameOffsetCounter++;f[v.$skinnedMeshBoundsOffset]=S}const w=f[v.$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&&v.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 b=h.x,O=h.y,$=g.x,K=g.y;const J=2,oe=1.5,Z=(h.x+g.x)*.5,ee=(h.y+g.y)*.5;b=(b-Z)*J+Z,O=(O-ee)*J+ee,$=($-Z)*J+Z,K=(K-ee)*J+ee;const ke=b<0&&$>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),Re=O<0&&K>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ie=Math.max(ke,Re);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&&C>0&&w.multiplyScalar(C/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 D=B.getSize(this._tempBox2Size),W=Math.max(D.x,D.y);if(Math.max(w.x,w.y)!=0&&W!=0&&(w.z=D.z/Math.max(D.x,D.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,I&&v.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=v.corner0,b=v.corner1,O=v.corner2,$=v.corner3;g.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=g.x,O.copy(this._tempBox.max),O.y=g.y,$.copy(this._tempBox.max);const K=(g.z+$.z)*.5;g.z=b.z=O.z=$.z=K,g.applyMatrix4(h),b.applyMatrix4(h),O.applyMatrix4(h),$.applyMatrix4(h),v.debugDrawLine(g,b,255),v.debugDrawLine(g,O,255),v.debugDrawLine(b,$,255),v.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,I&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.debug(`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,I){const w=_.lods[_.max_count-1];I&&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 D=C/window.devicePixelRatio*S;let W=!1;for(let T=_.lods.length-1;T>=0;T--){const L=_.lods[T];if(!(f&&L.max_height>=2048)&&!(Pe()&&L.max_height>4096)&&(L.max_height>D||!W&&T===0)){if(W=!0,s.texture_lod=T,I&&s.texture_lod<r.lastLodLevel_Texture){const y=L.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${y}px
6
- Screensize: ${D.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"),te=Symbol("NEEDLE_texture_lod");let he=null;function Ce(){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"),Ce()}),null)}function Me(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Ie(t)})}const De=new WeakSet;let rt=0;function Ie(i){if(!i||De.has(i))return null;De.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 s=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 n=Q.get(t,{engine:"model-viewer"});return Q.addPlugin(new st),n.enable(),n.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",o=>{o.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{s()}),()=>{n.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[te]==!0)return;e[te]=!0;const r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let l=function(a){if(a[te]==!0)return;a[te]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let c=0;c<u.length;c++){const m=u[c],_=a[m];if(_?.isTexture===!0){const M=_.userData?.associations?.textures;if(M==null)continue;const C=r.parser.json.textures[M];if(!C){console.warn("Texture data not found for texture index "+M);continue}if(C?.extensions?.[F]){const G=C.extensions[F];G&&s&&x.registerTexture(s,_,G.lods.length,M,G)}}}};const o=e.material;if(Array.isArray(o))for(const a of o)l(a);else l(o)}}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;x.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 x(o));const s=Q.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}Ce();if(!ze){const i={gltfProgressive:{useNeedleProgressive:nt,LODsManager:Q,configureLoader:xe,getRaycastMesh:X,useRaycastMeshes:Ve}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}exports.LODsManager=Q;exports.NEEDLE_progressive=x;exports.addDracoAndKTX2Loaders=ye;exports.configureLoader=xe;exports.createLoaders=ne;exports.getRaycastMesh=X;exports.setDracoDecoderLocation=Oe;exports.setKTX2TranscoderLocation=Se;