@mml-io/3d-web-client-core 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/camera/CameraManager.ts
2
2
  import { PerspectiveCamera, Raycaster, Vector3 as Vector32 } from "three";
3
+ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
3
4
 
4
5
  // src/helpers/math-helpers.ts
5
6
  import { Quaternion, Vector3, Vector4 } from "three";
@@ -82,159 +83,6 @@ var EventHandlerCollection = class _EventHandlerCollection {
82
83
  }
83
84
  };
84
85
 
85
- // src/input/VirtualJoystick.ts
86
- var VirtualJoystick = class _VirtualJoystick {
87
- constructor(holderElement, attrs) {
88
- this.holderElement = holderElement;
89
- this.left = false;
90
- this.right = false;
91
- this.up = false;
92
- this.down = false;
93
- this.hasDirection = false;
94
- this.clearFlags = () => {
95
- this.left = false;
96
- this.right = false;
97
- this.up = false;
98
- this.down = false;
99
- this.hasDirection = false;
100
- this.control.style.left = `${this.width / 2 - this.inner_radius}px`;
101
- this.control.style.top = `${this.height / 2 - this.inner_radius}px`;
102
- };
103
- this.radius = attrs.radius || 50;
104
- this.inner_radius = attrs.inner_radius || this.radius / 2;
105
- this.anchor = attrs.anchor || "left";
106
- this.x = attrs.x || 0;
107
- this.y = attrs.y || 0;
108
- this.width = attrs.width || this.radius * 2 + this.inner_radius * 2;
109
- this.height = attrs.height || this.radius * 2 + this.inner_radius * 2;
110
- this.mouse_support = this.checkTouch() || attrs.mouse_support === true;
111
- this.div = document.createElement("div");
112
- const divStyle = this.div.style;
113
- divStyle.display = this.checkTouch() || this.mouse_support ? "visible" : "none";
114
- divStyle.position = "fixed";
115
- if (this.anchor === "left") {
116
- divStyle.left = `${this.x}px`;
117
- } else {
118
- divStyle.right = `${this.x}px`;
119
- }
120
- divStyle.bottom = `${this.y}px`;
121
- divStyle.width = `${this.width}px`;
122
- divStyle.height = `${this.height}px`;
123
- divStyle.zIndex = "10000";
124
- divStyle.overflow = "hidden";
125
- this.holderElement.appendChild(this.div);
126
- this.setupBaseAndControl();
127
- this.bindEvents();
128
- }
129
- static checkForTouch() {
130
- try {
131
- document.createEvent("TouchEvent");
132
- return true;
133
- } catch (e) {
134
- return false;
135
- }
136
- }
137
- checkTouch() {
138
- return _VirtualJoystick.checkForTouch();
139
- }
140
- setupBaseAndControl() {
141
- this.base = document.createElement("span");
142
- let divStyle = this.base.style;
143
- divStyle.width = `${this.radius * 2}px`;
144
- divStyle.height = `${this.radius * 2}px`;
145
- divStyle.position = "absolute";
146
- divStyle.left = `${this.width / 2 - this.radius}px`;
147
- divStyle.bottom = `${this.height / 2 - this.radius}px`;
148
- divStyle.borderRadius = "50%";
149
- divStyle.borderColor = "rgba(200,200,200,0.5)";
150
- divStyle.borderWidth = "2px";
151
- divStyle.borderStyle = "solid";
152
- this.div.appendChild(this.base);
153
- this.control = document.createElement("span");
154
- divStyle = this.control.style;
155
- divStyle.width = `${this.inner_radius * 2}px`;
156
- divStyle.height = `${this.inner_radius * 2}px`;
157
- divStyle.position = "absolute";
158
- divStyle.left = `${this.width / 2 - this.inner_radius}px`;
159
- divStyle.bottom = `${this.height / 2 - this.inner_radius}px`;
160
- divStyle.borderRadius = "50%";
161
- divStyle.backgroundColor = "rgba(200,200,200,0.3)";
162
- divStyle.borderWidth = "1px";
163
- divStyle.borderColor = "rgba(200,200,200,0.8)";
164
- divStyle.borderStyle = "solid";
165
- this.div.appendChild(this.control);
166
- }
167
- bindEvents() {
168
- this.div.addEventListener("touchstart", this.handleTouchStart.bind(this), false);
169
- this.div.addEventListener("touchmove", this.handleTouchMove.bind(this), false);
170
- this.div.addEventListener("touchend", this.clearFlags.bind(this), false);
171
- if (this.mouse_support) {
172
- this.div.addEventListener("mousedown", this.handleMouseDown.bind(this));
173
- this.div.addEventListener("mousemove", this.handleMouseMove.bind(this));
174
- this.div.addEventListener("mouseup", this.handleMouseUp.bind(this));
175
- }
176
- }
177
- handleTouchStart(evt) {
178
- evt.preventDefault();
179
- evt.stopPropagation();
180
- if (evt.touches) {
181
- const touch = evt.touches[0];
182
- this.updateControlAndDirection(touch);
183
- }
184
- }
185
- handleTouchMove(evt) {
186
- evt.preventDefault();
187
- evt.stopPropagation();
188
- if (evt.touches.length > 0) {
189
- const touch = evt.touches[0];
190
- this.updateControlAndDirection(touch);
191
- }
192
- }
193
- handleMouseDown(evt) {
194
- evt.preventDefault();
195
- evt.stopPropagation();
196
- this.updateControlAndDirection(evt);
197
- }
198
- handleMouseMove(evt) {
199
- if (evt.buttons === 1) {
200
- evt.preventDefault();
201
- evt.stopPropagation();
202
- this.updateControlAndDirection(evt);
203
- }
204
- }
205
- handleMouseUp(evt) {
206
- this.clearFlags();
207
- }
208
- updateControlAndDirection(input) {
209
- const rect = this.div.getBoundingClientRect();
210
- const dx = input.clientX - (rect.left + this.div.offsetWidth / 2);
211
- const dy = input.clientY - (rect.top + this.div.offsetHeight / 2);
212
- const distance = Math.min(Math.sqrt(dx * dx + dy * dy), this.radius);
213
- const angle = Math.atan2(dy, dx);
214
- const constrainedX = distance * Math.cos(angle);
215
- const constrainedY = distance * Math.sin(angle);
216
- this.control.style.left = `${constrainedX + this.width / 2 - this.inner_radius}px`;
217
- this.control.style.top = `${constrainedY + this.height / 2 - this.inner_radius}px`;
218
- this.up = this.isUp(dx, dy);
219
- this.down = this.isDown(dx, dy);
220
- this.left = this.isLeft(dx, dy);
221
- this.right = this.isRight(dx, dy);
222
- this.hasDirection = this.up || this.down || this.left || this.right;
223
- }
224
- isUp(dx, dy) {
225
- return dy < 0 && Math.abs(dx) <= 2 * Math.abs(dy);
226
- }
227
- isDown(dx, dy) {
228
- return dy > 0 && Math.abs(dx) <= 2 * Math.abs(dy);
229
- }
230
- isLeft(dx, dy) {
231
- return dx < 0 && Math.abs(dy) <= 2 * Math.abs(dx);
232
- }
233
- isRight(dx, dy) {
234
- return dx > 0 && Math.abs(dy) <= 2 * Math.abs(dx);
235
- }
236
- };
237
-
238
86
  // src/tweakpane/blades/cameraFolder.ts
239
87
  var camValues = {
240
88
  initialDistance: 3.3,
@@ -244,10 +92,9 @@ var camValues = {
244
92
  maxFOV: 70,
245
93
  minFOV: 60,
246
94
  invertFOVMapping: false,
247
- damping: 0.15,
248
- dampingScale: 5e-3,
95
+ damping: 0.25,
249
96
  zoomScale: 0.088,
250
- zoomDamping: 0.16
97
+ zoomDamping: 0.4
251
98
  };
252
99
  var camOptions = {
253
100
  initialDistance: { min: 1, max: 5, step: 0.1 },
@@ -256,8 +103,7 @@ var camOptions = {
256
103
  initialFOV: { min: 60, max: 85, step: 1 },
257
104
  maxFOV: { min: 50, max: 100, step: 1 },
258
105
  minFOV: { min: 50, max: 100, step: 1 },
259
- damping: { min: 0.01, max: 0.15, step: 0.01 },
260
- dampingScale: { min: 1e-3, max: 0.02, step: 1e-3 },
106
+ damping: { min: 0.01, max: 1, step: 1e-3 },
261
107
  zoomScale: { min: 5e-3, max: 0.3, step: 1e-3 },
262
108
  zoomDamping: { min: 0, max: 2, step: 0.01 }
263
109
  };
@@ -277,7 +123,6 @@ var CameraFolder = class {
277
123
  this.folder.addBinding(camValues, "maxFOV", camOptions.maxFOV);
278
124
  this.folder.addBinding({ invertFOVMapping: camValues.invertFOVMapping }, "invertFOVMapping");
279
125
  this.folder.addBinding(camValues, "damping", camOptions.damping);
280
- this.folder.addBinding(camValues, "dampingScale", camOptions.dampingScale);
281
126
  this.folder.addBinding(camValues, "zoomScale", camOptions.zoomScale);
282
127
  this.folder.addBinding(camValues, "zoomDamping", camOptions.zoomDamping);
283
128
  }
@@ -326,20 +171,16 @@ var CameraFolder = class {
326
171
  cameraManager.recomputeFoV();
327
172
  break;
328
173
  }
329
- case "invertFOVMapping":
174
+ case "invertFOVMapping": {
330
175
  const boolValue = e.value;
331
176
  cameraManager.invertFOVMapping = boolValue;
332
177
  break;
178
+ }
333
179
  case "damping": {
334
180
  const value = e.value;
335
181
  cameraManager.damping = value;
336
182
  break;
337
183
  }
338
- case "dampingScale": {
339
- const value = e.value;
340
- cameraManager.dampingScale = value;
341
- break;
342
- }
343
184
  case "zoomScale": {
344
185
  const value = e.value;
345
186
  cameraManager.zoomScale = value;
@@ -371,124 +212,132 @@ function getTweakpaneActive() {
371
212
  }
372
213
 
373
214
  // src/camera/CameraManager.ts
215
+ var cameraPanSensitivity = 20;
216
+ var scrollZoomSensitivity = 0.1;
217
+ var pinchZoomSensitivity = 0.025;
374
218
  var CameraManager = class {
375
219
  constructor(targetElement, collisionsManager, initialPhi = Math.PI / 2, initialTheta = -Math.PI / 2) {
220
+ this.targetElement = targetElement;
376
221
  this.collisionsManager = collisionsManager;
222
+ this.isMainCameraActive = true;
377
223
  this.initialDistance = camValues.initialDistance;
378
224
  this.minDistance = camValues.minDistance;
379
225
  this.maxDistance = camValues.maxDistance;
380
- this.initialFOV = camValues.initialFOV;
381
- this.maxFOV = camValues.maxFOV;
382
- this.minFOV = camValues.minFOV;
383
226
  this.damping = camValues.damping;
384
- this.dampingScale = 0.01;
385
227
  this.zoomScale = camValues.zoomScale;
386
228
  this.zoomDamping = camValues.zoomDamping;
229
+ this.initialFOV = camValues.initialFOV;
230
+ this.maxFOV = camValues.maxFOV;
231
+ this.minFOV = camValues.minFOV;
387
232
  this.invertFOVMapping = camValues.invertFOVMapping;
388
233
  this.fov = this.initialFOV;
389
234
  this.targetFOV = this.initialFOV;
390
235
  this.minPolarAngle = Math.PI * 0.25;
391
236
  this.maxPolarAngle = Math.PI * 0.95;
392
- this.targetDistance = this.initialDistance;
393
237
  this.distance = this.initialDistance;
238
+ this.targetDistance = this.initialDistance;
394
239
  this.desiredDistance = this.initialDistance;
395
- this.phi = Math.PI / 2;
396
- this.theta = Math.PI / 2;
397
- this.dragging = false;
398
240
  this.target = new Vector32(0, 1.55, 0);
399
241
  this.hadTarget = false;
400
- this.isLerping = false;
401
242
  this.finalTarget = new Vector32();
243
+ this.isLerping = false;
402
244
  this.lerpTarget = new Vector32();
403
245
  this.lerpFactor = 0;
404
246
  this.lerpDuration = 2.1;
405
- this.hasTouchControl = false;
406
- this.lastTouchX = 0;
407
- this.lastTouchY = 0;
247
+ this.activePointers = /* @__PURE__ */ new Map();
248
+ this.targetElement.style.touchAction = "pinch-zoom";
408
249
  this.phi = initialPhi;
409
- this.targetPhi = initialPhi;
250
+ this.targetPhi = this.phi;
410
251
  this.theta = initialTheta;
411
- this.targetTheta = initialTheta;
412
- this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
252
+ this.targetTheta = this.theta;
253
+ const aspect = window.innerWidth / window.innerHeight;
254
+ this.camera = new PerspectiveCamera(this.fov, aspect, 0.1, 400);
413
255
  this.camera.position.set(0, 1.4, -this.initialDistance);
256
+ this.camera.name = "MainCamera";
257
+ this.flyCamera = new PerspectiveCamera(this.initialFOV, aspect, 0.1, 400);
258
+ this.flyCamera.name = "FlyCamera";
259
+ this.flyCamera.position.copy(this.camera.position);
260
+ this.flyCamera.name = "FlyCamera";
261
+ this.orbitControls = new OrbitControls(this.flyCamera, this.targetElement);
262
+ this.orbitControls.enableDamping = true;
263
+ this.orbitControls.dampingFactor = 0.05;
264
+ this.orbitControls.enablePan = true;
265
+ this.orbitControls.enabled = false;
414
266
  this.rayCaster = new Raycaster();
415
- this.hasTouchControl = VirtualJoystick.checkForTouch();
267
+ this.createEventHandlers();
268
+ }
269
+ createEventHandlers() {
416
270
  this.eventHandlerCollection = EventHandlerCollection.create([
417
- [targetElement, "mousedown", this.onMouseDown.bind(this)],
418
- [document, "mouseup", this.onMouseUp.bind(this)],
419
- [document, "mousemove", this.onMouseMove.bind(this)],
420
- [targetElement, "wheel", this.onMouseWheel.bind(this)],
421
- [targetElement, "contextmenu", this.onContextMenu.bind(this)]
271
+ [this.targetElement, "pointerdown", this.onPointerDown.bind(this)],
272
+ [this.targetElement, "gesturestart", this.preventDefaultAndStopPropagation.bind(this)],
273
+ [this.targetElement, "wheel", this.onMouseWheel.bind(this)],
274
+ [this.targetElement, "contextmenu", this.onContextMenu.bind(this)],
275
+ [document, "pointerup", this.onPointerUp.bind(this)],
276
+ [document, "pointercancel", this.onPointerUp.bind(this)],
277
+ [document, "pointermove", this.onPointerMove.bind(this)]
422
278
  ]);
423
- if (this.hasTouchControl) {
424
- this.eventHandlerCollection.add(targetElement, "touchstart", this.onTouchStart.bind(this), {
425
- passive: false
426
- });
427
- this.eventHandlerCollection.add(document, "touchmove", this.onTouchMove.bind(this), {
428
- passive: false
429
- });
430
- this.eventHandlerCollection.add(document, "touchend", this.onTouchEnd.bind(this), {
431
- passive: false
432
- });
433
- }
434
279
  }
435
- setupTweakPane(tweakPane) {
436
- tweakPane.setupCamPane(this);
437
- }
438
- onTouchStart(evt) {
439
- Array.from(evt.touches).forEach((touch) => {
440
- this.dragging = true;
441
- this.lastTouchX = touch.clientX;
442
- this.lastTouchY = touch.clientY;
443
- });
280
+ disposeEventHandlers() {
281
+ this.eventHandlerCollection.clear();
444
282
  }
445
- onTouchMove(evt) {
446
- if (!this.dragging || getTweakpaneActive()) {
447
- return;
448
- }
283
+ preventDefaultAndStopPropagation(evt) {
449
284
  evt.preventDefault();
450
- const touch = Array.from(evt.touches).find((t) => true);
451
- if (touch) {
452
- const dx = touch.clientX - this.lastTouchX;
453
- const dy = touch.clientY - this.lastTouchY;
454
- this.lastTouchX = touch.clientX;
455
- this.lastTouchY = touch.clientY;
456
- if (this.targetTheta !== null && this.targetPhi !== null) {
457
- this.targetTheta += dx * this.dampingScale;
458
- this.targetPhi -= dy * this.dampingScale;
459
- this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
460
- }
461
- }
285
+ evt.stopPropagation();
462
286
  }
463
- onTouchEnd(evt) {
464
- if (this.dragging) {
465
- const touchEnded = Array.from(evt.changedTouches).some((t) => true);
466
- if (touchEnded) {
467
- this.dragging = false;
468
- }
469
- }
287
+ setupTweakPane(tweakPane) {
288
+ tweakPane.setupCamPane(this);
470
289
  }
471
- onMouseDown(event) {
290
+ onPointerDown(event) {
472
291
  if (event.button === 0 || event.button === 2) {
473
- this.dragging = true;
292
+ const pointerInfo = { x: event.clientX, y: event.clientY };
293
+ this.activePointers.set(event.pointerId, pointerInfo);
474
294
  document.body.style.cursor = "none";
475
295
  }
476
296
  }
477
- onMouseUp(event) {
478
- if (event.button === 0 || event.button === 2) {
479
- this.dragging = false;
480
- document.body.style.cursor = "default";
297
+ onPointerUp(event) {
298
+ const existingPointer = this.activePointers.get(event.pointerId);
299
+ if (existingPointer) {
300
+ this.activePointers.delete(event.pointerId);
301
+ if (this.activePointers.size === 0) {
302
+ document.body.style.cursor = "default";
303
+ }
481
304
  }
482
305
  }
483
- onMouseMove(event) {
306
+ getAveragePointerPositionAndSpread() {
307
+ const existingSum = { x: 0, y: 0 };
308
+ this.activePointers.forEach((p) => {
309
+ existingSum.x += p.x;
310
+ existingSum.y += p.y;
311
+ });
312
+ const aX = existingSum.x / this.activePointers.size;
313
+ const aY = existingSum.y / this.activePointers.size;
314
+ let sumOfDistances = 0;
315
+ this.activePointers.forEach((p) => {
316
+ const distance = Math.sqrt((p.x - aX) ** 2 + (p.y - aY) ** 2);
317
+ sumOfDistances += distance;
318
+ });
319
+ return { pos: { x: aX, y: aY }, spread: sumOfDistances / this.activePointers.size };
320
+ }
321
+ onPointerMove(event) {
484
322
  if (getTweakpaneActive()) {
485
323
  return;
486
324
  }
487
- if (this.dragging) {
488
- if (this.targetTheta === null || this.targetPhi === null)
489
- return;
490
- this.targetTheta += event.movementX * this.dampingScale;
491
- this.targetPhi -= event.movementY * this.dampingScale;
325
+ const existingPointer = this.activePointers.get(event.pointerId);
326
+ if (existingPointer) {
327
+ const previous = this.getAveragePointerPositionAndSpread();
328
+ existingPointer.x = event.clientX;
329
+ existingPointer.y = event.clientY;
330
+ const latest = this.getAveragePointerPositionAndSpread();
331
+ const sX = latest.pos.x - previous.pos.x;
332
+ const sY = latest.pos.y - previous.pos.y;
333
+ const dx = sX / this.targetElement.clientWidth * cameraPanSensitivity;
334
+ const dy = sY / this.targetElement.clientHeight * cameraPanSensitivity;
335
+ if (this.activePointers.size > 1) {
336
+ const zoomDelta = latest.spread - previous.spread;
337
+ this.zoom(-zoomDelta * pinchZoomSensitivity);
338
+ }
339
+ this.targetTheta += dx;
340
+ this.targetPhi -= dy;
492
341
  this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
493
342
  event.preventDefault();
494
343
  }
@@ -497,14 +346,17 @@ var CameraManager = class {
497
346
  if (getTweakpaneActive()) {
498
347
  return;
499
348
  }
500
- const scrollAmount = event.deltaY * this.zoomScale * 0.1;
501
- this.targetDistance += scrollAmount;
349
+ event.preventDefault();
350
+ const scrollAmount = event.deltaY * this.zoomScale * scrollZoomSensitivity;
351
+ this.zoom(scrollAmount);
352
+ }
353
+ zoom(delta) {
354
+ this.targetDistance += delta;
502
355
  this.targetDistance = Math.max(
503
356
  this.minDistance,
504
357
  Math.min(this.maxDistance, this.targetDistance)
505
358
  );
506
359
  this.desiredDistance = this.targetDistance;
507
- event.preventDefault();
508
360
  }
509
361
  onContextMenu(event) {
510
362
  event.preventDefault();
@@ -533,12 +385,12 @@ var CameraManager = class {
533
385
  const dy = this.camera.position.y - this.target.y;
534
386
  const dz = this.camera.position.z - this.target.z;
535
387
  this.targetDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
536
- this.targetTheta = Math.atan2(dz, dx);
537
- this.targetPhi = Math.acos(dy / this.targetDistance);
538
- this.phi = this.targetPhi;
539
- this.theta = this.targetTheta;
540
388
  this.distance = this.targetDistance;
541
389
  this.desiredDistance = this.targetDistance;
390
+ this.theta = Math.atan2(dz, dx);
391
+ this.targetTheta = this.theta;
392
+ this.phi = Math.acos(dy / this.targetDistance);
393
+ this.targetPhi = this.phi;
542
394
  this.recomputeFoV(true);
543
395
  }
544
396
  adjustCameraPosition() {
@@ -546,25 +398,28 @@ var CameraManager = class {
546
398
  const offset = new Vector32(0, 0, offsetDistance);
547
399
  offset.applyEuler(this.camera.rotation);
548
400
  const rayOrigin = this.camera.position.clone().add(offset);
549
- const rayDirection = this.target.clone().sub(rayOrigin).normalize();
550
- this.rayCaster.set(rayOrigin, rayDirection);
401
+ const rayDirection = rayOrigin.sub(this.target.clone()).normalize();
402
+ this.rayCaster.set(this.target.clone(), rayDirection);
551
403
  const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
552
- const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
553
- if (firstRaycastHit !== null && firstRaycastHit[0] <= cameraToPlayerDistance) {
554
- this.targetDistance = cameraToPlayerDistance - firstRaycastHit[0];
555
- this.distance = this.targetDistance;
404
+ if (firstRaycastHit !== null && firstRaycastHit[0] <= this.desiredDistance) {
405
+ const distanceToCollision = firstRaycastHit[0] - 0.1;
406
+ this.targetDistance = distanceToCollision;
407
+ this.distance = distanceToCollision;
556
408
  } else {
557
- this.targetDistance += (this.desiredDistance - this.targetDistance) * this.damping * 4;
409
+ this.targetDistance = this.desiredDistance;
558
410
  }
559
411
  }
560
412
  dispose() {
561
- this.eventHandlerCollection.clear();
413
+ this.disposeEventHandlers();
414
+ this.orbitControls.dispose();
415
+ document.body.style.cursor = "";
562
416
  }
563
417
  easeOutExpo(x) {
564
418
  return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
565
419
  }
566
420
  updateAspect(aspect) {
567
421
  this.camera.aspect = aspect;
422
+ this.flyCamera.aspect = aspect;
568
423
  }
569
424
  recomputeFoV(immediately = false) {
570
425
  this.targetFOV = remap(
@@ -578,7 +433,31 @@ var CameraManager = class {
578
433
  this.fov = this.targetFOV;
579
434
  }
580
435
  }
436
+ toggleFlyCamera() {
437
+ this.isMainCameraActive = !this.isMainCameraActive;
438
+ this.orbitControls.enabled = !this.isMainCameraActive;
439
+ if (!this.isMainCameraActive) {
440
+ this.updateAspect(window.innerWidth / window.innerHeight);
441
+ this.flyCamera.position.copy(this.camera.position);
442
+ this.flyCamera.rotation.copy(this.camera.rotation);
443
+ const target = new Vector32();
444
+ this.camera.getWorldDirection(target);
445
+ target.multiplyScalar(this.targetDistance).add(this.camera.position);
446
+ this.orbitControls.target.copy(target);
447
+ this.orbitControls.update();
448
+ this.disposeEventHandlers();
449
+ } else {
450
+ this.createEventHandlers();
451
+ }
452
+ }
453
+ get activeCamera() {
454
+ return this.isMainCameraActive ? this.camera : this.flyCamera;
455
+ }
581
456
  update() {
457
+ if (!this.isMainCameraActive) {
458
+ this.orbitControls.update();
459
+ return;
460
+ }
582
461
  if (this.isLerping && this.lerpFactor < 1) {
583
462
  this.lerpFactor += 0.01 / this.lerpDuration;
584
463
  this.lerpFactor = Math.min(1, this.lerpFactor);
@@ -586,24 +465,25 @@ var CameraManager = class {
586
465
  } else {
587
466
  this.adjustCameraPosition();
588
467
  }
589
- if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
590
- this.distance += (this.targetDistance - this.distance) * this.damping * (0.21 + this.zoomDamping);
591
- this.phi += (this.targetPhi - this.phi) * this.damping;
592
- this.theta += (this.targetTheta - this.theta) * this.damping;
593
- const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
594
- const y = this.target.y + this.distance * Math.cos(this.phi);
595
- const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
596
- this.recomputeFoV();
597
- this.fov += (this.targetFOV - this.fov) * this.damping;
598
- this.camera.fov = this.fov;
599
- this.camera.updateProjectionMatrix();
600
- this.camera.position.set(x, y, z);
601
- this.camera.lookAt(this.target);
602
- if (this.isLerping && this.lerpFactor >= 1) {
603
- this.isLerping = false;
604
- }
468
+ this.distance += (this.targetDistance - this.distance) * this.zoomDamping;
469
+ this.theta += (this.targetTheta - this.theta) * this.damping;
470
+ this.phi += (this.targetPhi - this.phi) * this.damping;
471
+ const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
472
+ const y = this.target.y + this.distance * Math.cos(this.phi);
473
+ const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
474
+ this.recomputeFoV();
475
+ this.fov += (this.targetFOV - this.fov) * this.zoomDamping;
476
+ this.camera.fov = this.fov;
477
+ this.camera.updateProjectionMatrix();
478
+ this.camera.position.set(x, y, z);
479
+ this.camera.lookAt(this.target);
480
+ if (this.isLerping && this.lerpFactor >= 1) {
481
+ this.isLerping = false;
605
482
  }
606
483
  }
484
+ hasActiveInput() {
485
+ return this.activePointers.size > 0;
486
+ }
607
487
  };
608
488
 
609
489
  // src/character/Character.ts
@@ -1480,6 +1360,9 @@ var Character = class extends Group {
1480
1360
  }
1481
1361
  async load() {
1482
1362
  const previousModel = this.model;
1363
+ if (previousModel && previousModel.mesh) {
1364
+ this.remove(previousModel.mesh);
1365
+ }
1483
1366
  this.model = new CharacterModel({
1484
1367
  characterDescription: this.config.characterDescription,
1485
1368
  animationConfig: this.config.animationConfig,
@@ -1489,9 +1372,6 @@ var Character = class extends Group {
1489
1372
  isLocal: this.config.isLocal
1490
1373
  });
1491
1374
  await this.model.init();
1492
- if (previousModel && previousModel.mesh) {
1493
- this.remove(previousModel.mesh);
1494
- }
1495
1375
  this.add(this.model.mesh);
1496
1376
  if (this.speakingIndicator === null) {
1497
1377
  this.speakingIndicator = new CharacterSpeakingIndicator(this.config.composer.postPostScene);
@@ -1769,6 +1649,7 @@ var LocalController = class {
1769
1649
  this.jumpPressed = false;
1770
1650
  // Tracks if the jump button is pressed
1771
1651
  this.jumpReleased = true;
1652
+ this.controlState = null;
1772
1653
  this.networkState = {
1773
1654
  id: this.config.id,
1774
1655
  position: { x: 0, y: 0, z: 0 },
@@ -1776,35 +1657,22 @@ var LocalController = class {
1776
1657
  state: 0 /* idle */
1777
1658
  };
1778
1659
  }
1779
- updateControllerState() {
1780
- var _a, _b, _c, _d, _e;
1781
- this.forward = this.config.keyInputManager.forward || ((_a = this.config.virtualJoystick) == null ? void 0 : _a.up) || false;
1782
- this.backward = this.config.keyInputManager.backward || ((_b = this.config.virtualJoystick) == null ? void 0 : _b.down) || false;
1783
- this.left = this.config.keyInputManager.left || ((_c = this.config.virtualJoystick) == null ? void 0 : _c.left) || false;
1784
- this.right = this.config.keyInputManager.right || ((_d = this.config.virtualJoystick) == null ? void 0 : _d.right) || false;
1785
- this.run = this.config.keyInputManager.run;
1786
- this.jump = this.config.keyInputManager.jump;
1787
- this.anyDirection = this.config.keyInputManager.anyDirection || ((_e = this.config.virtualJoystick) == null ? void 0 : _e.hasDirection) || false;
1788
- this.conflictingDirections = this.config.keyInputManager.conflictingDirection;
1789
- if (!this.jump) {
1790
- this.jumpReleased = true;
1791
- }
1792
- }
1793
1660
  update() {
1794
- this.updateControllerState();
1661
+ var _a, _b;
1662
+ this.controlState = this.config.keyInputManager.getOutput() || ((_a = this.config.virtualJoystick) == null ? void 0 : _a.getOutput()) || null;
1795
1663
  this.rayCaster.set(this.config.character.position, this.vectorDown);
1796
1664
  const firstRaycastHit = this.config.collisionsManager.raycastFirst(this.rayCaster.ray);
1797
1665
  if (firstRaycastHit !== null) {
1798
1666
  this.currentHeight = firstRaycastHit[0];
1799
1667
  this.currentSurfaceAngle.copy(firstRaycastHit[1]);
1800
1668
  }
1801
- if (this.anyDirection || !this.characterOnGround) {
1669
+ if (((_b = this.controlState) == null ? void 0 : _b.direction) !== null || !this.characterOnGround) {
1802
1670
  const targetAnimation = this.getTargetAnimation();
1803
1671
  this.config.character.updateAnimation(targetAnimation);
1804
1672
  } else {
1805
1673
  this.config.character.updateAnimation(0 /* idle */);
1806
1674
  }
1807
- if (this.anyDirection) {
1675
+ if (this.controlState) {
1808
1676
  this.updateRotation();
1809
1677
  }
1810
1678
  for (let i = 0; i < this.collisionDetectionSteps; i++) {
@@ -1829,44 +1697,31 @@ var LocalController = class {
1829
1697
  }
1830
1698
  return 4 /* air */;
1831
1699
  }
1832
- if (this.conflictingDirections) {
1700
+ if (!this.controlState) {
1833
1701
  return 0 /* idle */;
1834
1702
  }
1835
- return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
1703
+ if (this.controlState.isSprinting) {
1704
+ return 2 /* running */;
1705
+ }
1706
+ return 1 /* walking */;
1836
1707
  }
1837
1708
  updateRotationOffset() {
1838
- if (this.conflictingDirections)
1839
- return;
1840
- if (this.forward) {
1841
- this.rotationOffset = Math.PI;
1842
- if (this.left)
1843
- this.rotationOffset = Math.PI + Math.PI / 4;
1844
- if (this.right)
1845
- this.rotationOffset = Math.PI - Math.PI / 4;
1846
- } else if (this.backward) {
1847
- this.rotationOffset = Math.PI * 2;
1848
- if (this.left)
1849
- this.rotationOffset = -Math.PI * 2 - Math.PI / 4;
1850
- if (this.right)
1851
- this.rotationOffset = Math.PI * 2 + Math.PI / 4;
1852
- } else if (this.left) {
1853
- this.rotationOffset = Math.PI * -0.5;
1854
- } else if (this.right) {
1855
- this.rotationOffset = Math.PI * 0.5;
1709
+ if (this.controlState && this.controlState.direction !== null) {
1710
+ this.rotationOffset = this.controlState.direction;
1856
1711
  }
1857
1712
  }
1858
1713
  updateAzimuthalAngle() {
1859
- const camToModelDistance = this.config.cameraManager.camera.position.distanceTo(
1714
+ const camToModelDistance = this.config.cameraManager.activeCamera.position.distanceTo(
1860
1715
  this.config.character.position
1861
1716
  );
1862
1717
  const isCameraFirstPerson = camToModelDistance < 2;
1863
1718
  if (isCameraFirstPerson) {
1864
- const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.config.cameraManager.camera.quaternion);
1719
+ const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.config.cameraManager.activeCamera.quaternion);
1865
1720
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1866
1721
  } else {
1867
1722
  this.azimuthalAngle = Math.atan2(
1868
- this.config.cameraManager.camera.position.x - this.config.character.position.x,
1869
- this.config.cameraManager.camera.position.z - this.config.character.position.z
1723
+ this.config.cameraManager.activeCamera.position.x - this.config.character.position.x,
1724
+ this.config.cameraManager.activeCamera.position.z - this.config.character.position.z
1870
1725
  );
1871
1726
  }
1872
1727
  }
@@ -1887,17 +1742,19 @@ var LocalController = class {
1887
1742
  this.config.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1888
1743
  }
1889
1744
  processJump(currentAcceleration, deltaTime) {
1745
+ var _a;
1746
+ const jump = (_a = this.controlState) == null ? void 0 : _a.jump;
1890
1747
  if (this.characterOnGround) {
1891
1748
  this.coyoteJumped = false;
1892
1749
  this.canDoubleJump = false;
1893
1750
  this.doubleJumpUsed = false;
1894
1751
  this.jumpCounter = 0;
1895
- if (!this.jump) {
1752
+ if (!jump) {
1896
1753
  this.canDoubleJump = !this.doubleJumpUsed && this.jumpReleased && this.jumpCounter === 1;
1897
1754
  this.canJump = true;
1898
1755
  this.jumpReleased = true;
1899
1756
  }
1900
- if (this.jump && this.canJump && this.jumpReleased) {
1757
+ if (jump && this.canJump && this.jumpReleased) {
1901
1758
  currentAcceleration.y += this.jumpForce / deltaTime;
1902
1759
  this.canJump = false;
1903
1760
  this.jumpReleased = false;
@@ -1908,13 +1765,13 @@ var LocalController = class {
1908
1765
  }
1909
1766
  }
1910
1767
  } else {
1911
- if (this.jump && !this.coyoteJumped && this.coyoteTime) {
1768
+ if (jump && !this.coyoteJumped && this.coyoteTime) {
1912
1769
  this.coyoteJumped = true;
1913
1770
  currentAcceleration.y += this.jumpForce / deltaTime;
1914
1771
  this.canJump = false;
1915
1772
  this.jumpReleased = false;
1916
1773
  this.jumpCounter++;
1917
- } else if (this.jump && this.canDoubleJump) {
1774
+ } else if (jump && this.canDoubleJump) {
1918
1775
  currentAcceleration.y += this.doubleJumpForce / deltaTime;
1919
1776
  this.doubleJumpUsed = true;
1920
1777
  this.jumpReleased = false;
@@ -1924,7 +1781,7 @@ var LocalController = class {
1924
1781
  this.canJump = false;
1925
1782
  }
1926
1783
  }
1927
- if (!this.jump) {
1784
+ if (!jump) {
1928
1785
  this.jumpReleased = true;
1929
1786
  if (!this.characterOnGround) {
1930
1787
  currentAcceleration.y += this.gravity;
@@ -1932,31 +1789,19 @@ var LocalController = class {
1932
1789
  }
1933
1790
  }
1934
1791
  applyControls(deltaTime) {
1792
+ var _a;
1935
1793
  const resistance = this.characterOnGround ? this.groundResistance : this.airResistance;
1936
1794
  const speedFactor = Math.pow(1 - resistance, deltaTime);
1937
1795
  this.characterVelocity.multiplyScalar(speedFactor);
1938
1796
  const acceleration = this.tempVector.set(0, 0, 0);
1939
1797
  this.canDoubleJump = !this.doubleJumpUsed && this.jumpReleased && this.jumpCounter === 1;
1940
1798
  this.processJump(acceleration, deltaTime);
1941
- const control = (this.characterOnGround ? this.run ? this.groundRunControl : this.groundWalkControl : this.airControlModifier) * this.baseControl;
1799
+ const control = (this.characterOnGround ? ((_a = this.controlState) == null ? void 0 : _a.isSprinting) ? this.groundRunControl : this.groundWalkControl : this.airControlModifier) * this.baseControl;
1942
1800
  const controlAcceleration = this.tempVector2.set(0, 0, 0);
1943
- if (!this.conflictingDirections) {
1944
- if (this.forward) {
1945
- const forward = this.tempVector3.set(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1946
- controlAcceleration.add(forward);
1947
- }
1948
- if (this.backward) {
1949
- const backward = this.tempVector3.set(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1950
- controlAcceleration.add(backward);
1951
- }
1952
- if (this.left) {
1953
- const left = this.tempVector3.set(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1954
- controlAcceleration.add(left);
1955
- }
1956
- if (this.right) {
1957
- const right = this.tempVector3.set(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1958
- controlAcceleration.add(right);
1959
- }
1801
+ if (this.controlState && this.controlState.direction !== null) {
1802
+ const heading = this.controlState.direction;
1803
+ const headingVector = this.tempVector3.set(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle + heading);
1804
+ controlAcceleration.add(headingVector);
1960
1805
  }
1961
1806
  if (controlAcceleration.length() > 0) {
1962
1807
  controlAcceleration.normalize();
@@ -1967,6 +1812,7 @@ var LocalController = class {
1967
1812
  this.config.character.position.addScaledVector(this.characterVelocity, deltaTime);
1968
1813
  }
1969
1814
  updatePosition(deltaTime, stepDeltaTime, iter) {
1815
+ var _a;
1970
1816
  this.applyControls(stepDeltaTime);
1971
1817
  if (iter === 0) {
1972
1818
  const lastMovement = this.getMovementFromSurfaces(this.config.character.position, deltaTime);
@@ -1998,7 +1844,7 @@ var LocalController = class {
1998
1844
  if (this.characterWasOnGround && !this.characterOnGround) {
1999
1845
  this.characterAirborneSince = Date.now();
2000
1846
  }
2001
- if (!this.jump) {
1847
+ if (!((_a = this.controlState) == null ? void 0 : _a.jump)) {
2002
1848
  this.jumpReleased = true;
2003
1849
  }
2004
1850
  this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
@@ -2320,7 +2166,11 @@ var CharacterManager = class {
2320
2166
  this.remoteCharacterControllers.delete(id);
2321
2167
  }
2322
2168
  }
2323
- if (this.config.updateURLLocation && this.config.timeManager.frame % 60 === 0 && document.hasFocus()) {
2169
+ if (this.config.updateURLLocation && this.config.timeManager.frame % 60 === 0 && document.hasFocus() && /*
2170
+ Don't update the URL if the camera is being controlled as some browsers (e.g. Chrome) cause a hitch to Pointer
2171
+ events when the url is updated
2172
+ */
2173
+ !this.config.cameraManager.hasActiveInput()) {
2324
2174
  const hash = encodeCharacterAndCamera(
2325
2175
  this.localCharacter,
2326
2176
  this.config.cameraManager.camera
@@ -2416,11 +2266,22 @@ var CharacterModelLoader = class {
2416
2266
  };
2417
2267
 
2418
2268
  // src/input/KeyInputManager.ts
2269
+ var Key = /* @__PURE__ */ ((Key2) => {
2270
+ Key2["W"] = "w";
2271
+ Key2["A"] = "a";
2272
+ Key2["S"] = "s";
2273
+ Key2["D"] = "d";
2274
+ Key2["SHIFT"] = "shift";
2275
+ Key2["SPACE"] = " ";
2276
+ Key2["C"] = "c";
2277
+ return Key2;
2278
+ })(Key || {});
2419
2279
  var KeyInputManager = class {
2420
2280
  constructor(shouldCaptureKeyPress = () => true) {
2421
2281
  this.shouldCaptureKeyPress = shouldCaptureKeyPress;
2422
2282
  this.keys = /* @__PURE__ */ new Map();
2423
2283
  this.eventHandlerCollection = new EventHandlerCollection();
2284
+ this.bindings = /* @__PURE__ */ new Map();
2424
2285
  this.eventHandlerCollection.add(document, "keydown", this.onKeyDown.bind(this));
2425
2286
  this.eventHandlerCollection.add(document, "keyup", this.onKeyUp.bind(this));
2426
2287
  this.eventHandlerCollection.add(window, "blur", this.handleUnfocus.bind(this));
@@ -2442,49 +2303,262 @@ var KeyInputManager = class {
2442
2303
  }
2443
2304
  onKeyUp(event) {
2444
2305
  this.keys.set(event.key.toLowerCase(), false);
2306
+ if (this.bindings.has(event.key.toLowerCase())) {
2307
+ this.bindings.get(event.key.toLowerCase())();
2308
+ }
2445
2309
  }
2446
2310
  isKeyPressed(key) {
2447
2311
  return this.keys.get(key) || false;
2448
2312
  }
2313
+ createKeyBinding(key, callback) {
2314
+ this.bindings.set(key, callback);
2315
+ }
2449
2316
  isMovementKeyPressed() {
2450
2317
  return ["w" /* W */, "a" /* A */, "s" /* S */, "d" /* D */].some((key) => this.isKeyPressed(key));
2451
2318
  }
2452
- get forward() {
2319
+ getForward() {
2453
2320
  return this.isKeyPressed("w" /* W */);
2454
2321
  }
2455
- get backward() {
2322
+ getBackward() {
2456
2323
  return this.isKeyPressed("s" /* S */);
2457
2324
  }
2458
- get left() {
2325
+ getLeft() {
2459
2326
  return this.isKeyPressed("a" /* A */);
2460
2327
  }
2461
- get right() {
2328
+ getRight() {
2462
2329
  return this.isKeyPressed("d" /* D */);
2463
2330
  }
2464
- get run() {
2331
+ getRun() {
2465
2332
  return this.isKeyPressed("shift" /* SHIFT */);
2466
2333
  }
2467
- get jump() {
2334
+ getJump() {
2468
2335
  return this.isKeyPressed(" " /* SPACE */);
2469
2336
  }
2470
- get anyDirection() {
2471
- return this.isMovementKeyPressed();
2472
- }
2473
- get conflictingDirection() {
2474
- return this.isKeyPressed("w" /* W */) && this.isKeyPressed("s" /* S */) || this.isKeyPressed("a" /* A */) && this.isKeyPressed("d" /* D */);
2337
+ getOutput() {
2338
+ const dx = (this.getRight() ? 1 : 0) - (this.getLeft() ? 1 : 0);
2339
+ const dy = (this.getBackward() ? 1 : 0) - (this.getForward() ? 1 : 0);
2340
+ const jump = this.getJump();
2341
+ if (dx === 0 && dy === 0) {
2342
+ if (this.getJump()) {
2343
+ return { direction: null, isSprinting: false, jump };
2344
+ }
2345
+ return null;
2346
+ }
2347
+ const direction = Math.atan2(dx, dy);
2348
+ return { direction, isSprinting: this.getRun(), jump };
2475
2349
  }
2476
2350
  dispose() {
2477
2351
  this.eventHandlerCollection.clear();
2352
+ this.bindings.clear();
2353
+ }
2354
+ };
2355
+
2356
+ // src/input/VirtualJoystick.ts
2357
+ var sprintingThreshold = 0.6;
2358
+ var VirtualJoystick = class _VirtualJoystick {
2359
+ constructor(holderElement, config) {
2360
+ this.holderElement = holderElement;
2361
+ this.config = config;
2362
+ this.joystickPointerId = null;
2363
+ this.joystickOutput = null;
2364
+ this.jumpPointerId = null;
2365
+ this.clearJoystickState = () => {
2366
+ this.joystickOutput = null;
2367
+ this.joystickCenterElement.style.left = `${this.radius - this.innerRadius}px`;
2368
+ this.joystickCenterElement.style.top = `${this.radius - this.innerRadius}px`;
2369
+ };
2370
+ this.radius = config.radius || 50;
2371
+ this.innerRadius = config.innerRadius || this.radius / 2;
2372
+ this.mouseSupport = this.checkTouch() || config.mouseSupport === true;
2373
+ this.element = document.createElement("div");
2374
+ const style = this.element.style;
2375
+ style.display = this.mouseSupport ? "flex" : "none";
2376
+ style.position = "absolute";
2377
+ style.width = `100%`;
2378
+ style.height = `200px`;
2379
+ style.bottom = "50px";
2380
+ style.zIndex = "10000";
2381
+ style.alignItems = "center";
2382
+ style.justifyContent = "space-between";
2383
+ style.pointerEvents = "none";
2384
+ style.padding = "20px";
2385
+ style.boxSizing = "border-box";
2386
+ style.userSelect = "none";
2387
+ this.holderElement.appendChild(this.element);
2388
+ this.joystickBaseElement = this.createBase();
2389
+ this.element.appendChild(this.joystickBaseElement);
2390
+ this.joystickCenterElement = this.createCenter();
2391
+ this.joystickBaseElement.appendChild(this.joystickCenterElement);
2392
+ this.jumpButton = this.createJumpButton();
2393
+ this.element.appendChild(this.jumpButton);
2394
+ this.bindEvents();
2395
+ this.clearJoystickState();
2396
+ }
2397
+ static checkForTouch() {
2398
+ try {
2399
+ document.createEvent("TouchEvent");
2400
+ return true;
2401
+ } catch (e) {
2402
+ return false;
2403
+ }
2404
+ }
2405
+ checkTouch() {
2406
+ return _VirtualJoystick.checkForTouch();
2407
+ }
2408
+ createBase() {
2409
+ const base = document.createElement("span");
2410
+ const style = base.style;
2411
+ style.touchAction = "pinch-zoom";
2412
+ style.width = `${this.radius * 2}px`;
2413
+ style.height = `${this.radius * 2}px`;
2414
+ style.position = "relative";
2415
+ style.display = "block";
2416
+ style.borderRadius = "50%";
2417
+ style.borderColor = "rgba(200,200,200,0.5)";
2418
+ style.borderWidth = "2px";
2419
+ style.borderStyle = "solid";
2420
+ style.pointerEvents = "auto";
2421
+ style.userSelect = "none";
2422
+ return base;
2423
+ }
2424
+ createCenter() {
2425
+ const center = document.createElement("div");
2426
+ const style = center.style;
2427
+ style.width = `${this.innerRadius * 2}px`;
2428
+ style.height = `${this.innerRadius * 2}px`;
2429
+ style.position = "absolute";
2430
+ style.borderRadius = "50%";
2431
+ style.backgroundColor = "rgba(200,200,200,0.3)";
2432
+ style.borderWidth = "1px";
2433
+ style.borderColor = "rgba(200,200,200,0.8)";
2434
+ style.borderStyle = "solid";
2435
+ style.userSelect = "none";
2436
+ return center;
2437
+ }
2438
+ createJumpButton() {
2439
+ const button = document.createElement("button");
2440
+ button.textContent = "JUMP";
2441
+ const style = button.style;
2442
+ style.touchAction = "pinch-zoom";
2443
+ style.width = `100px`;
2444
+ style.height = `100px`;
2445
+ style.borderRadius = "20px";
2446
+ style.color = "white";
2447
+ style.font = "Helvetica, sans-serif";
2448
+ style.fontSize = "16px";
2449
+ style.backgroundColor = "rgba(200,200,200,0.3)";
2450
+ style.color = "rgba(220,220,220,1)";
2451
+ style.borderWidth = "1px";
2452
+ style.borderColor = "rgba(200,200,200,0.8)";
2453
+ style.borderStyle = "solid";
2454
+ style.pointerEvents = "auto";
2455
+ style.userSelect = "none";
2456
+ return button;
2457
+ }
2458
+ bindEvents() {
2459
+ this.joystickBaseElement.addEventListener("pointerdown", this.onJoystickPointerDown.bind(this));
2460
+ this.joystickBaseElement.addEventListener(
2461
+ "contextmenu",
2462
+ this.preventDefaultAndStopPropagation.bind(this)
2463
+ );
2464
+ this.joystickBaseElement.addEventListener(
2465
+ "touchstart",
2466
+ this.preventDefaultAndStopPropagation.bind(this)
2467
+ );
2468
+ this.jumpButton.addEventListener("pointerdown", this.onJumpPointerDown.bind(this));
2469
+ this.jumpButton.addEventListener(
2470
+ "contextmenu",
2471
+ this.preventDefaultAndStopPropagation.bind(this)
2472
+ );
2473
+ this.jumpButton.addEventListener(
2474
+ "touchstart",
2475
+ this.preventDefaultAndStopPropagation.bind(this)
2476
+ );
2477
+ document.addEventListener("pointermove", this.onPointerMove.bind(this));
2478
+ document.addEventListener("pointerup", this.onPointerUp.bind(this));
2479
+ document.addEventListener("pointercancel", this.onPointerUp.bind(this));
2480
+ }
2481
+ preventDefaultAndStopPropagation(evt) {
2482
+ evt.preventDefault();
2483
+ evt.stopPropagation();
2484
+ }
2485
+ onJumpPointerDown(evt) {
2486
+ if (this.jumpPointerId === null) {
2487
+ this.jumpPointerId = evt.pointerId;
2488
+ }
2489
+ }
2490
+ onJoystickPointerDown(evt) {
2491
+ evt.preventDefault();
2492
+ evt.stopPropagation();
2493
+ if (evt.buttons !== 1) {
2494
+ return;
2495
+ }
2496
+ if (this.joystickPointerId === null) {
2497
+ this.joystickPointerId = evt.pointerId;
2498
+ this.updateControlAndDirection(evt);
2499
+ }
2500
+ }
2501
+ onPointerMove(evt) {
2502
+ evt.preventDefault();
2503
+ evt.stopPropagation();
2504
+ if (evt.pointerId !== this.joystickPointerId) {
2505
+ return;
2506
+ }
2507
+ this.updateControlAndDirection(evt);
2508
+ }
2509
+ onPointerUp(evt) {
2510
+ evt.preventDefault();
2511
+ evt.stopPropagation();
2512
+ if (evt.pointerId === this.jumpPointerId) {
2513
+ this.jumpPointerId = null;
2514
+ }
2515
+ if (evt.pointerId === this.joystickPointerId) {
2516
+ this.joystickPointerId = null;
2517
+ this.clearJoystickState();
2518
+ }
2519
+ }
2520
+ updateControlAndDirection(input) {
2521
+ const rect = this.joystickBaseElement.getBoundingClientRect();
2522
+ const dx = input.clientX - (rect.left + this.radius);
2523
+ const dy = input.clientY - (rect.top + this.radius);
2524
+ const distance = Math.min(Math.sqrt(dx * dx + dy * dy), this.radius);
2525
+ const angle = Math.atan2(dy, dx);
2526
+ const constrainedX = distance * Math.cos(angle);
2527
+ const constrainedY = distance * Math.sin(angle);
2528
+ this.joystickCenterElement.style.left = `${constrainedX + this.radius - this.innerRadius}px`;
2529
+ this.joystickCenterElement.style.top = `${constrainedY + this.radius - this.innerRadius}px`;
2530
+ const direction = Math.atan2(dx, dy);
2531
+ const speed = distance / this.radius;
2532
+ const isSprinting = speed > sprintingThreshold;
2533
+ this.joystickOutput = { direction, isSprinting };
2534
+ }
2535
+ getOutput() {
2536
+ const jump = this.jumpPointerId !== null;
2537
+ if (!this.joystickOutput) {
2538
+ if (jump) {
2539
+ return { direction: null, isSprinting: false, jump };
2540
+ }
2541
+ return null;
2542
+ }
2543
+ return {
2544
+ ...this.joystickOutput,
2545
+ jump
2546
+ };
2478
2547
  }
2479
2548
  };
2480
2549
 
2481
2550
  // src/mml/MMLCompositionScene.ts
2482
2551
  import {
2483
2552
  InteractionManager,
2484
- MMLClickTrigger,
2485
- PromptManager,
2486
- LoadingProgressManager
2487
- } from "mml-web";
2553
+ LoadingProgressManager,
2554
+ MMLDocumentTimeManager,
2555
+ PromptManager
2556
+ } from "@mml-io/mml-web";
2557
+ import {
2558
+ ThreeJSClickTrigger,
2559
+ ThreeJSGraphicsInterface,
2560
+ ThreeJSInteractionAdapter
2561
+ } from "@mml-io/mml-web-threejs";
2488
2562
  import { Group as Group3 } from "three";
2489
2563
  var MMLCompositionScene = class {
2490
2564
  constructor(config) {
@@ -2492,20 +2566,55 @@ var MMLCompositionScene = class {
2492
2566
  this.chatProbes = /* @__PURE__ */ new Set();
2493
2567
  this.group = new Group3();
2494
2568
  this.promptManager = PromptManager.init(this.config.targetElement);
2569
+ const graphicsAdapter = {
2570
+ collisionType: null,
2571
+ containerType: null,
2572
+ getGraphicsAdapterFactory: () => {
2573
+ return ThreeJSGraphicsInterface;
2574
+ },
2575
+ getRootContainer: () => {
2576
+ return this.group;
2577
+ },
2578
+ interactionShouldShowDistance(interaction) {
2579
+ return ThreeJSInteractionAdapter.interactionShouldShowDistance(
2580
+ interaction,
2581
+ this.config.camera,
2582
+ this.config.scene
2583
+ );
2584
+ },
2585
+ dispose() {
2586
+ },
2587
+ getAudioListener: () => {
2588
+ return config.audioListener;
2589
+ },
2590
+ getCamera: () => {
2591
+ return config.camera;
2592
+ },
2593
+ getThreeScene: () => {
2594
+ return config.scene;
2595
+ },
2596
+ getUserPositionAndRotation: () => {
2597
+ return this.config.getUserPositionAndRotation();
2598
+ }
2599
+ };
2495
2600
  const { interactionListener, interactionManager } = InteractionManager.init(
2496
2601
  this.config.targetElement,
2497
- this.config.camera,
2498
- this.config.scene
2602
+ (interaction) => {
2603
+ return graphicsAdapter.interactionShouldShowDistance(interaction);
2604
+ }
2499
2605
  );
2500
2606
  this.interactionManager = interactionManager;
2501
2607
  this.interactionListener = interactionListener;
2502
2608
  this.loadingProgressManager = new LoadingProgressManager();
2609
+ this.documentTimeManager = new MMLDocumentTimeManager();
2503
2610
  this.mmlScene = {
2504
- getAudioListener: () => this.config.audioListener,
2505
- getRenderer: () => this.config.renderer,
2506
- getThreeScene: () => this.config.scene,
2611
+ getGraphicsAdapter() {
2612
+ return graphicsAdapter;
2613
+ },
2614
+ hasGraphicsAdapter() {
2615
+ return true;
2616
+ },
2507
2617
  getRootContainer: () => this.group,
2508
- getCamera: () => this.config.camera,
2509
2618
  addCollider: (object, mElement) => {
2510
2619
  this.config.collisionsManager.addMeshesGroup(object, mElement);
2511
2620
  },
@@ -2533,14 +2642,21 @@ var MMLCompositionScene = class {
2533
2642
  removeChatProbe: (chatProbe) => {
2534
2643
  this.chatProbes.delete(chatProbe);
2535
2644
  },
2536
- prompt: (promptProps, callback) => {
2537
- this.promptManager.prompt(promptProps, callback);
2645
+ prompt: (promptProps, abortSignal, callback) => {
2646
+ this.promptManager.prompt(promptProps, abortSignal, callback);
2647
+ },
2648
+ link: (linkProps, abortSignal, windowCallback) => {
2649
+ this.promptManager.link(linkProps, abortSignal, windowCallback);
2538
2650
  },
2539
2651
  getLoadingProgressManager: () => {
2540
2652
  return this.loadingProgressManager;
2541
2653
  }
2542
2654
  };
2543
- this.clickTrigger = MMLClickTrigger.init(this.config.targetElement, this.mmlScene);
2655
+ this.clickTrigger = ThreeJSClickTrigger.init(
2656
+ this.config.targetElement,
2657
+ this.group,
2658
+ this.config.camera
2659
+ );
2544
2660
  }
2545
2661
  onChatMessage(message) {
2546
2662
  for (const chatProbe of this.chatProbes) {
@@ -3436,24 +3552,27 @@ var TweakPane = class {
3436
3552
  this.characterControls = new CharacterControlsFolder(this.gui, false);
3437
3553
  this.toneMappingFolder.folder.hidden = rendererValues.toneMapping === 5 ? false : true;
3438
3554
  this.export = this.gui.addFolder({ title: "import / export", expanded: false });
3439
- this.setupGUIListeners();
3440
- window.addEventListener("keydown", (e) => {
3441
- this.processKey(e);
3442
- });
3443
- }
3444
- setupGUIListeners() {
3555
+ this.eventHandlerCollection = new EventHandlerCollection();
3445
3556
  const gui = this.gui;
3446
3557
  const paneElement = gui.containerElem_;
3447
3558
  paneElement.style.right = this.guiVisible ? "0px" : "-450px";
3448
- this.gui.element.addEventListener("mouseenter", () => setTweakpaneActive(true));
3449
- this.gui.element.addEventListener("mousemove", () => setTweakpaneActive(true));
3450
- this.gui.element.addEventListener("mousedown", () => setTweakpaneActive(true));
3451
- this.gui.element.addEventListener("mouseleave", () => setTweakpaneActive(false));
3452
- this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
3559
+ this.eventHandlerCollection.add(this.gui.element, "mouseenter", () => setTweakpaneActive(true));
3560
+ this.eventHandlerCollection.add(this.gui.element, "mousemove", () => setTweakpaneActive(true));
3561
+ this.eventHandlerCollection.add(this.gui.element, "mousedown", () => setTweakpaneActive(true));
3562
+ this.eventHandlerCollection.add(
3563
+ this.gui.element,
3564
+ "mouseleave",
3565
+ () => setTweakpaneActive(false)
3566
+ );
3567
+ this.eventHandlerCollection.add(this.gui.element, "mouseup", () => setTweakpaneActive(false));
3568
+ this.eventHandlerCollection.add(window, "keydown", (e) => {
3569
+ this.processKey(e);
3570
+ });
3453
3571
  }
3454
3572
  processKey(e) {
3455
- if (e.key === "p")
3573
+ if (e.key === "p") {
3456
3574
  this.toggleGUI();
3575
+ }
3457
3576
  }
3458
3577
  setupRenderPane(composer, normalPass, ppssaoEffect, ppssaoPass, n8aopass, toneMappingEffect, toneMappingPass, brightnessContrastSaturation, bloomEffect, gaussGrainEffect, hasLighting, sun, setHDR, setSkyboxAzimuthalAngle, setSkyboxPolarAngle, setAmbientLight, setFog) {
3459
3578
  this.rendererFolder.setupChangeEvent(
@@ -3487,6 +3606,7 @@ var TweakPane = class {
3487
3606
  });
3488
3607
  }
3489
3608
  dispose() {
3609
+ this.eventHandlerCollection.clear();
3490
3610
  this.gui.dispose();
3491
3611
  this.tweakPaneWrapper.remove();
3492
3612
  }
@@ -5137,22 +5257,22 @@ var N8SSAOPass = class extends Pass {
5137
5257
  var Composer = class {
5138
5258
  constructor({
5139
5259
  scene,
5140
- camera,
5260
+ cameraManager,
5141
5261
  spawnSun = false,
5142
5262
  environmentConfiguration
5143
5263
  }) {
5144
5264
  this.width = 1;
5145
5265
  this.height = 1;
5146
5266
  this.resolution = new Vector27(this.width, this.height);
5147
- this.isEnvHDRI = false;
5148
5267
  this.bcs = BrightnessContrastSaturation;
5149
5268
  this.gaussGrainEffect = GaussGrainEffect;
5150
5269
  this.ambientLight = null;
5270
+ this.skyboxState = { src: {}, latestPromise: null };
5151
5271
  this.sun = null;
5152
- var _a;
5272
+ var _a, _b;
5153
5273
  this.scene = scene;
5274
+ this.cameraManager = cameraManager;
5154
5275
  this.postPostScene = new Scene4();
5155
- this.camera = camera;
5156
5276
  this.spawnSun = spawnSun;
5157
5277
  this.renderer = new WebGLRenderer4({
5158
5278
  powerPreference: "high-performance",
@@ -5172,14 +5292,14 @@ var Composer = class {
5172
5292
  this.effectComposer = new EffectComposer2(this.renderer, {
5173
5293
  frameBufferType: HalfFloatType2
5174
5294
  });
5175
- this.renderPass = new RenderPass(this.scene, this.camera);
5176
- this.normalPass = new NormalPass2(this.scene, this.camera);
5295
+ this.renderPass = new RenderPass(this.scene, this.cameraManager.activeCamera);
5296
+ this.normalPass = new NormalPass2(this.scene, this.cameraManager.activeCamera);
5177
5297
  this.normalPass.enabled = ppssaoValues.enabled;
5178
5298
  this.normalTextureEffect = new TextureEffect({
5179
5299
  blendFunction: BlendFunction2.SKIP,
5180
5300
  texture: this.normalPass.texture
5181
5301
  });
5182
- this.ppssaoEffect = new SSAOEffect2(this.camera, this.normalPass.texture, {
5302
+ this.ppssaoEffect = new SSAOEffect2(this.cameraManager.activeCamera, this.normalPass.texture, {
5183
5303
  blendFunction: ppssaoValues.blendFunction,
5184
5304
  distanceScaling: ppssaoValues.distanceScaling,
5185
5305
  depthAwareUpsampling: ppssaoValues.depthAwareUpsampling,
@@ -5197,7 +5317,11 @@ var Composer = class {
5197
5317
  worldProximityThreshold: ppssaoValues.worldProximityThreshold,
5198
5318
  worldProximityFalloff: ppssaoValues.worldProximityFalloff
5199
5319
  });
5200
- this.ppssaoPass = new EffectPass2(this.camera, this.ppssaoEffect, this.normalTextureEffect);
5320
+ this.ppssaoPass = new EffectPass2(
5321
+ this.cameraManager.activeCamera,
5322
+ this.ppssaoEffect,
5323
+ this.normalTextureEffect
5324
+ );
5201
5325
  this.ppssaoPass.enabled = ppssaoValues.enabled;
5202
5326
  this.fxaaEffect = new FXAAEffect();
5203
5327
  if ((_a = environmentConfiguration == null ? void 0 : environmentConfiguration.postProcessing) == null ? void 0 : _a.bloomIntensity) {
@@ -5206,7 +5330,12 @@ var Composer = class {
5206
5330
  this.bloomEffect = new BloomEffect({
5207
5331
  intensity: extrasValues.bloom
5208
5332
  });
5209
- this.n8aopass = new N8SSAOPass(this.scene, this.camera, this.width, this.height);
5333
+ this.n8aopass = new N8SSAOPass(
5334
+ this.scene,
5335
+ this.cameraManager.activeCamera,
5336
+ this.width,
5337
+ this.height
5338
+ );
5210
5339
  this.n8aopass.configuration.aoRadius = n8ssaoValues.aoRadius;
5211
5340
  this.n8aopass.configuration.distanceFalloff = n8ssaoValues.distanceFalloff;
5212
5341
  this.n8aopass.configuration.intensity = n8ssaoValues.intensity;
@@ -5219,8 +5348,8 @@ var Composer = class {
5219
5348
  this.n8aopass.configuration.denoiseSamples = n8ssaoValues.denoiseSamples;
5220
5349
  this.n8aopass.configuration.denoiseRadius = n8ssaoValues.denoiseRadius;
5221
5350
  this.n8aopass.enabled = n8ssaoValues.enabled;
5222
- this.fxaaPass = new EffectPass2(this.camera, this.fxaaEffect);
5223
- this.bloomPass = new EffectPass2(this.camera, this.bloomEffect);
5351
+ this.fxaaPass = new EffectPass2(this.cameraManager.activeCamera, this.fxaaEffect);
5352
+ this.bloomPass = new EffectPass2(this.cameraManager.activeCamera, this.bloomEffect);
5224
5353
  this.toneMappingEffect = new ToneMappingEffect({
5225
5354
  mode: toneMappingValues.mode,
5226
5355
  resolution: toneMappingValues.resolution,
@@ -5235,7 +5364,7 @@ var Composer = class {
5235
5364
  edgeDetectionMode: EdgeDetectionMode.COLOR,
5236
5365
  predicationMode: PredicationMode.DEPTH
5237
5366
  });
5238
- this.toneMappingPass = new EffectPass2(this.camera, this.toneMappingEffect);
5367
+ this.toneMappingPass = new EffectPass2(this.cameraManager.activeCamera, this.toneMappingEffect);
5239
5368
  this.toneMappingPass.enabled = rendererValues.toneMapping === 5 || rendererValues.toneMapping === 0 ? true : false;
5240
5369
  this.bcsPass = new ShaderPass(this.bcs, "tDiffuse");
5241
5370
  this.bcs.uniforms.brightness.value = bcsValues.brightness;
@@ -5244,7 +5373,7 @@ var Composer = class {
5244
5373
  this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
5245
5374
  this.gaussGrainEffect.uniforms.amount.value = extrasValues.grain;
5246
5375
  this.gaussGrainEffect.uniforms.alpha.value = 1;
5247
- this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
5376
+ this.smaaPass = new EffectPass2(this.cameraManager.activeCamera, this.smaaEffect);
5248
5377
  this.effectComposer.addPass(this.renderPass);
5249
5378
  if (ppssaoValues.enabled) {
5250
5379
  this.effectComposer.addPass(this.normalPass);
@@ -5262,6 +5391,13 @@ var Composer = class {
5262
5391
  this.sun = new Sun();
5263
5392
  this.scene.add(this.sun);
5264
5393
  }
5394
+ if ((_b = this.environmentConfiguration) == null ? void 0 : _b.skybox) {
5395
+ if ("hdrJpgUrl" in this.environmentConfiguration.skybox) {
5396
+ this.useHDRJPG(this.environmentConfiguration.skybox.hdrJpgUrl);
5397
+ } else if ("hdrUrl" in this.environmentConfiguration.skybox) {
5398
+ this.useHDRI(this.environmentConfiguration.skybox.hdrUrl);
5399
+ }
5400
+ }
5265
5401
  this.updateSunValues();
5266
5402
  this.resizeListener = () => {
5267
5403
  this.fitContainer();
@@ -5269,6 +5405,19 @@ var Composer = class {
5269
5405
  window.addEventListener("resize", this.resizeListener, false);
5270
5406
  this.fitContainer();
5271
5407
  }
5408
+ updateEnvironmentConfiguration(environmentConfiguration) {
5409
+ this.environmentConfiguration = environmentConfiguration;
5410
+ if (environmentConfiguration.skybox) {
5411
+ if ("hdrJpgUrl" in environmentConfiguration.skybox) {
5412
+ this.useHDRJPG(environmentConfiguration.skybox.hdrJpgUrl);
5413
+ } else if ("hdrUrl" in environmentConfiguration.skybox) {
5414
+ this.useHDRI(environmentConfiguration.skybox.hdrUrl);
5415
+ }
5416
+ }
5417
+ this.updateSkyboxAndEnvValues();
5418
+ this.updateAmbientLightValues();
5419
+ this.updateSunValues();
5420
+ }
5272
5421
  setupTweakPane(tweakPane) {
5273
5422
  tweakPane.setupRenderPane(
5274
5423
  this.effectComposer,
@@ -5311,8 +5460,8 @@ var Composer = class {
5311
5460
  }
5312
5461
  this.width = parentElement.clientWidth;
5313
5462
  this.height = parentElement.clientHeight;
5314
- this.camera.aspect = this.width / this.height;
5315
- this.camera.updateProjectionMatrix();
5463
+ this.cameraManager.activeCamera.aspect = this.width / this.height;
5464
+ this.cameraManager.activeCamera.updateProjectionMatrix();
5316
5465
  this.renderer.setPixelRatio(window.devicePixelRatio);
5317
5466
  this.resolution.set(
5318
5467
  this.width * window.devicePixelRatio,
@@ -5341,11 +5490,12 @@ var Composer = class {
5341
5490
  }
5342
5491
  render(timeManager) {
5343
5492
  this.renderer.info.reset();
5493
+ this.renderPass.mainCamera = this.cameraManager.activeCamera;
5344
5494
  this.normalPass.texture.needsUpdate = true;
5345
5495
  this.gaussGrainEffect.uniforms.time.value = timeManager.time;
5346
5496
  this.effectComposer.render();
5347
5497
  this.renderer.clearDepth();
5348
- this.renderer.render(this.postPostScene, this.camera);
5498
+ this.renderer.render(this.postPostScene, this.cameraManager.activeCamera);
5349
5499
  }
5350
5500
  updateSkyboxRotation() {
5351
5501
  this.scene.backgroundRotation = new Euler3(
@@ -5359,71 +5509,71 @@ var Composer = class {
5359
5509
  0
5360
5510
  );
5361
5511
  }
5362
- useHDRJPG(url, fromFile = false) {
5363
- const pmremGenerator = new PMREMGenerator(this.renderer);
5364
- const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
5365
- const hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;
5366
- hdrJpgEquirectangularMap.mapping = EquirectangularReflectionMapping;
5367
- hdrJpgEquirectangularMap.needsUpdate = true;
5368
- const envMap = pmremGenerator.fromEquirectangular(hdrJpgEquirectangularMap).texture;
5369
- if (envMap) {
5370
- envMap.colorSpace = LinearSRGBColorSpace;
5371
- envMap.needsUpdate = true;
5372
- this.scene.environment = envMap;
5373
- this.scene.environmentIntensity = envValues.envMapIntensity;
5374
- this.scene.environmentRotation = new Euler3(
5375
- MathUtils.degToRad(envValues.skyboxPolarAngle),
5376
- MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5377
- 0
5378
- );
5379
- this.scene.background = envMap;
5380
- this.scene.backgroundIntensity = envValues.skyboxIntensity;
5381
- this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
5382
- this.scene.backgroundRotation = new Euler3(
5383
- MathUtils.degToRad(envValues.skyboxPolarAngle),
5384
- MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5385
- 0
5386
- );
5387
- this.isEnvHDRI = true;
5512
+ async loadHDRJPG(url) {
5513
+ return new Promise((resolve, reject) => {
5514
+ const pmremGenerator = new PMREMGenerator(this.renderer);
5515
+ const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
5516
+ const hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;
5517
+ hdrJpgEquirectangularMap.mapping = EquirectangularReflectionMapping;
5518
+ hdrJpgEquirectangularMap.needsUpdate = true;
5519
+ const envMap = pmremGenerator.fromEquirectangular(hdrJpgEquirectangularMap).texture;
5388
5520
  hdrJpgEquirectangularMap.dispose();
5389
5521
  pmremGenerator.dispose();
5390
- }
5391
- hdrJpg.dispose();
5522
+ hdrJpg.dispose();
5523
+ if (envMap) {
5524
+ envMap.colorSpace = LinearSRGBColorSpace;
5525
+ envMap.needsUpdate = true;
5526
+ resolve(envMap);
5527
+ } else {
5528
+ reject("Failed to generate environment map");
5529
+ }
5530
+ });
5392
5531
  });
5393
5532
  }
5394
- useHDRI(url, fromFile = false) {
5395
- if (this.isEnvHDRI && fromFile === false || !this.renderer) {
5396
- return;
5397
- }
5398
- const pmremGenerator = new PMREMGenerator(this.renderer);
5399
- new RGBELoader(new LoadingManager()).load(
5400
- url,
5401
- (texture) => {
5533
+ async loadHDRi(url) {
5534
+ return new Promise((resolve, reject) => {
5535
+ const pmremGenerator = new PMREMGenerator(this.renderer);
5536
+ new RGBELoader(new LoadingManager()).load(url, (texture) => {
5402
5537
  const envMap = pmremGenerator.fromEquirectangular(texture).texture;
5538
+ texture.dispose();
5539
+ pmremGenerator.dispose();
5403
5540
  if (envMap) {
5404
5541
  envMap.colorSpace = LinearSRGBColorSpace;
5405
5542
  envMap.needsUpdate = true;
5406
- this.scene.environment = envMap;
5407
- this.scene.environmentIntensity = envValues.envMapIntensity;
5408
- this.scene.environmentRotation = new Euler3(
5409
- MathUtils.degToRad(envValues.skyboxPolarAngle),
5410
- MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5411
- 0
5412
- );
5413
- this.scene.background = envMap;
5414
- this.scene.backgroundIntensity = envValues.skyboxIntensity;
5415
- this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
5416
- this.isEnvHDRI = true;
5417
- texture.dispose();
5418
- pmremGenerator.dispose();
5543
+ resolve(envMap);
5544
+ } else {
5545
+ reject("Failed to generate environment map");
5419
5546
  }
5420
- },
5421
- () => {
5422
- },
5423
- (error) => {
5424
- console.error(`Can't load ${url}: ${JSON.stringify(error)}`);
5547
+ });
5548
+ });
5549
+ }
5550
+ useHDRJPG(url, fromFile = false) {
5551
+ if (this.skyboxState.src.hdrJpgUrl === url) {
5552
+ return;
5553
+ }
5554
+ const hdrJPGPromise = this.loadHDRJPG(url);
5555
+ this.skyboxState.src = { hdrJpgUrl: url };
5556
+ this.skyboxState.latestPromise = hdrJPGPromise;
5557
+ hdrJPGPromise.then((envMap) => {
5558
+ if (this.skyboxState.latestPromise !== hdrJPGPromise) {
5559
+ return;
5425
5560
  }
5426
- );
5561
+ this.applyEnvMap(envMap);
5562
+ });
5563
+ }
5564
+ useHDRI(url) {
5565
+ if (this.skyboxState.src.hdrUrl === url) {
5566
+ return;
5567
+ }
5568
+ const hdrPromise = this.loadHDRi(url);
5569
+ this.skyboxState.src = { hdrUrl: url };
5570
+ this.skyboxState.latestPromise = hdrPromise;
5571
+ hdrPromise.then((envMap) => {
5572
+ if (this.skyboxState.latestPromise !== hdrPromise) {
5573
+ return;
5574
+ }
5575
+ this.applyEnvMap(envMap);
5576
+ });
5427
5577
  }
5428
5578
  setHDRIFromFile() {
5429
5579
  if (!this.renderer)
@@ -5442,7 +5592,7 @@ var Composer = class {
5442
5592
  const fileURL = URL.createObjectURL(file);
5443
5593
  if (fileURL) {
5444
5594
  if (extension === "hdr") {
5445
- this.useHDRI(fileURL, true);
5595
+ this.useHDRI(fileURL);
5446
5596
  } else if (extension === "jpg") {
5447
5597
  this.useHDRJPG(fileURL);
5448
5598
  } else {
@@ -5487,11 +5637,13 @@ var Composer = class {
5487
5637
  }
5488
5638
  if (typeof ((_e = (_d = this.environmentConfiguration) == null ? void 0 : _d.sun) == null ? void 0 : _e.azimuthalAngle) === "number") {
5489
5639
  sunValues.sunPosition.sunAzimuthalAngle = this.environmentConfiguration.sun.azimuthalAngle;
5490
- (_f = this.sun) == null ? void 0 : _f.setAzimuthalAngle(this.environmentConfiguration.sun.azimuthalAngle);
5640
+ (_f = this.sun) == null ? void 0 : _f.setAzimuthalAngle(
5641
+ this.environmentConfiguration.sun.azimuthalAngle * (Math.PI / 180)
5642
+ );
5491
5643
  }
5492
5644
  if (typeof ((_h = (_g = this.environmentConfiguration) == null ? void 0 : _g.sun) == null ? void 0 : _h.polarAngle) === "number") {
5493
5645
  sunValues.sunPosition.sunPolarAngle = this.environmentConfiguration.sun.polarAngle;
5494
- (_i = this.sun) == null ? void 0 : _i.setPolarAngle(this.environmentConfiguration.sun.polarAngle);
5646
+ (_i = this.sun) == null ? void 0 : _i.setPolarAngle(this.environmentConfiguration.sun.polarAngle * (Math.PI / 180));
5495
5647
  }
5496
5648
  }
5497
5649
  updateSkyboxAndEnvValues() {
@@ -5524,6 +5676,23 @@ var Composer = class {
5524
5676
  }
5525
5677
  this.setAmbientLight();
5526
5678
  }
5679
+ applyEnvMap(envMap) {
5680
+ this.scene.environment = envMap;
5681
+ this.scene.environmentIntensity = envValues.envMapIntensity;
5682
+ this.scene.environmentRotation = new Euler3(
5683
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5684
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5685
+ 0
5686
+ );
5687
+ this.scene.background = envMap;
5688
+ this.scene.backgroundIntensity = envValues.skyboxIntensity;
5689
+ this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
5690
+ this.scene.backgroundRotation = new Euler3(
5691
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5692
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5693
+ 0
5694
+ );
5695
+ }
5527
5696
  };
5528
5697
 
5529
5698
  // src/time/TimeManager.ts
@@ -5573,39 +5742,73 @@ var TimeManager = class {
5573
5742
  };
5574
5743
 
5575
5744
  // src/collisions/CollisionsManager.ts
5576
- import {
5577
- getRelativePositionAndRotationRelativeToObject,
5578
- MMLCollisionTrigger
5579
- } from "mml-web";
5745
+ import { MMLCollisionTrigger } from "@mml-io/mml-web";
5580
5746
  import {
5581
5747
  Box3,
5582
5748
  Color as Color8,
5583
5749
  DoubleSide,
5584
- Euler as Euler4,
5750
+ Euler as Euler5,
5585
5751
  Group as Group5,
5586
5752
  Line3 as Line32,
5587
- Matrix4 as Matrix46,
5753
+ Matrix4 as Matrix47,
5588
5754
  Mesh as Mesh5,
5589
5755
  MeshBasicMaterial as MeshBasicMaterial3,
5590
- Quaternion as Quaternion6,
5756
+ Quaternion as Quaternion7,
5591
5757
  Ray as Ray2,
5592
- Vector3 as Vector314
5758
+ Vector3 as Vector315
5593
5759
  } from "three";
5594
5760
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
5595
5761
  import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
5596
5762
  import { MeshBVH, MeshBVHHelper } from "three-mesh-bvh";
5763
+
5764
+ // src/collisions/getRelativePositionAndRotationRelativeToObject.ts
5765
+ import { Euler as Euler4, Matrix4 as Matrix46, Quaternion as Quaternion6, Vector3 as Vector314 } from "three";
5766
+ var tempContainerMatrix = new Matrix46();
5767
+ var tempTargetMatrix = new Matrix46();
5768
+ var tempPositionVector = new Vector314();
5769
+ var tempRotationEuler = new Euler4();
5770
+ var tempRotationQuaternion = new Quaternion6();
5771
+ var tempScaleVector = new Vector314();
5772
+ function getRelativePositionAndRotationRelativeToObject(positionAndRotation, container) {
5773
+ const { x, y, z } = positionAndRotation.position;
5774
+ const { x: rx, y: ry, z: rz } = positionAndRotation.rotation;
5775
+ container.updateWorldMatrix(true, false);
5776
+ tempContainerMatrix.copy(container.matrixWorld).invert();
5777
+ tempPositionVector.set(x, y, z);
5778
+ tempRotationEuler.set(rx, ry, rz);
5779
+ tempRotationQuaternion.setFromEuler(tempRotationEuler);
5780
+ tempScaleVector.set(1, 1, 1);
5781
+ tempTargetMatrix.compose(tempPositionVector, tempRotationQuaternion, tempScaleVector);
5782
+ tempTargetMatrix.premultiply(tempContainerMatrix);
5783
+ tempTargetMatrix.decompose(tempPositionVector, tempRotationQuaternion, tempScaleVector);
5784
+ tempRotationEuler.setFromQuaternion(tempRotationQuaternion);
5785
+ return {
5786
+ position: {
5787
+ x: tempPositionVector.x,
5788
+ y: tempPositionVector.y,
5789
+ z: tempPositionVector.z
5790
+ },
5791
+ rotation: {
5792
+ x: tempRotationEuler.x,
5793
+ y: tempRotationEuler.y,
5794
+ z: tempRotationEuler.z
5795
+ }
5796
+ };
5797
+ }
5798
+
5799
+ // src/collisions/CollisionsManager.ts
5597
5800
  var CollisionsManager = class {
5598
5801
  constructor(scene) {
5599
5802
  this.debug = false;
5600
- this.tempVector = new Vector314();
5601
- this.tempVector2 = new Vector314();
5602
- this.tempVector3 = new Vector314();
5603
- this.tempQuaternion = new Quaternion6();
5803
+ this.tempVector = new Vector315();
5804
+ this.tempVector2 = new Vector315();
5805
+ this.tempVector3 = new Vector315();
5806
+ this.tempQuaternion = new Quaternion7();
5604
5807
  this.tempRay = new Ray2();
5605
- this.tempMatrix = new Matrix46();
5606
- this.tempMatrix2 = new Matrix46();
5808
+ this.tempMatrix = new Matrix47();
5809
+ this.tempMatrix2 = new Matrix47();
5607
5810
  this.tempBox = new Box3();
5608
- this.tempEuler = new Euler4();
5811
+ this.tempEuler = new Euler5();
5609
5812
  this.tempSegment = new Line32();
5610
5813
  this.tempSegment2 = new Line32();
5611
5814
  this.collisionMeshState = /* @__PURE__ */ new Map();
@@ -5615,7 +5818,7 @@ var CollisionsManager = class {
5615
5818
  raycastFirst(ray) {
5616
5819
  let minimumDistance = null;
5617
5820
  let minimumHit = null;
5618
- let minimumNormal = new Vector314();
5821
+ let minimumNormal = new Vector315();
5619
5822
  for (const [, collisionMeshState] of this.collisionMeshState) {
5620
5823
  this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(collisionMeshState.matrix).invert());
5621
5824
  const hit = collisionMeshState.meshBVH.raycastFirst(this.tempRay, DoubleSide);
@@ -5756,7 +5959,7 @@ var CollisionsManager = class {
5756
5959
  const realDistance = intersectionSegment.distance();
5757
5960
  if (realDistance < capsuleRadius) {
5758
5961
  if (!collisionPosition) {
5759
- collisionPosition = new Vector314().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
5962
+ collisionPosition = new Vector315().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
5760
5963
  }
5761
5964
  const ratio = realDistance / modelReferenceDistance;
5762
5965
  const realDepth = capsuleRadius - realDistance;
@@ -5858,59 +6061,134 @@ var GroundPlane = class extends Group6 {
5858
6061
  };
5859
6062
 
5860
6063
  // src/loading-screen/LoadingScreen.ts
5861
- import { LoadingProgressManager as LoadingProgressManager2 } from "mml-web";
6064
+ import { LoadingProgressManager as LoadingProgressManager2 } from "@mml-io/mml-web";
5862
6065
  var LoadingScreen = class {
5863
- constructor(loadingProgressManager) {
6066
+ constructor(loadingProgressManager, config) {
5864
6067
  this.loadingProgressManager = loadingProgressManager;
6068
+ this.config = config;
6069
+ this.overlayLayers = [];
5865
6070
  this.hasCompleted = false;
5866
6071
  this.disposed = false;
6072
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6073
+ const defaultBackground = "linear-gradient(45deg, #28284B 0%, #303056 100%)";
5867
6074
  this.element = document.createElement("div");
6075
+ this.element.id = "loading-screen";
5868
6076
  this.element.style.position = "absolute";
5869
6077
  this.element.style.top = "0";
5870
6078
  this.element.style.left = "0";
5871
6079
  this.element.style.width = "100%";
5872
6080
  this.element.style.height = "100%";
5873
- this.element.style.background = "linear-gradient(45deg, #28284B 0%, #303056 100%)";
5874
- this.element.style.color = "white";
5875
- this.element.addEventListener("click", (event) => {
5876
- event.stopPropagation();
5877
- });
5878
- this.element.addEventListener("mousedown", (event) => {
5879
- event.stopPropagation();
5880
- });
5881
- this.element.addEventListener("mousemove", (event) => {
5882
- event.stopPropagation();
5883
- });
5884
- this.element.addEventListener("mouseup", (event) => {
5885
- event.stopPropagation();
5886
- });
5887
- this.loadingBannerText = document.createElement("div");
5888
- this.loadingBannerText.textContent = "Loading...";
5889
- this.loadingBannerText.style.position = "absolute";
5890
- this.loadingBannerText.style.display = "flex";
5891
- this.loadingBannerText.style.top = "0";
5892
- this.loadingBannerText.style.left = "0";
5893
- this.loadingBannerText.style.width = "100%";
5894
- this.loadingBannerText.style.height = "100%";
5895
- this.loadingBannerText.style.color = "white";
5896
- this.loadingBannerText.style.fontSize = "80px";
5897
- this.loadingBannerText.style.fontWeight = "bold";
5898
- this.loadingBannerText.style.fontFamily = "sans-serif";
5899
- this.loadingBannerText.style.alignItems = "center";
5900
- this.loadingBannerText.style.justifyContent = "center";
5901
- this.element.append(this.loadingBannerText);
6081
+ this.element.style.backgroundColor = ((_a = this.config) == null ? void 0 : _a.background) || defaultBackground;
6082
+ this.element.style.background = ((_b = this.config) == null ? void 0 : _b.background) || defaultBackground;
6083
+ this.element.style.zIndex = "10001";
6084
+ this.backgroundBlur = document.createElement("div");
6085
+ this.backgroundBlur.id = "loading-screen-blur";
6086
+ this.backgroundBlur.style.position = "absolute";
6087
+ this.backgroundBlur.style.top = "0";
6088
+ this.backgroundBlur.style.left = "0";
6089
+ this.backgroundBlur.style.width = "100%";
6090
+ this.backgroundBlur.style.height = "100%";
6091
+ this.backgroundBlur.style.display = "flex";
6092
+ if ((_c = this.config) == null ? void 0 : _c.backgroundBlurAmount) {
6093
+ this.backgroundBlur.style.backdropFilter = `blur(${this.config.backgroundBlurAmount}px)`;
6094
+ }
6095
+ this.element.append(this.backgroundBlur);
6096
+ if ((_d = this.config) == null ? void 0 : _d.backgroundImageUrl) {
6097
+ this.element.style.backgroundImage = `url(${this.config.backgroundImageUrl})`;
6098
+ this.element.style.backgroundPosition = "center";
6099
+ this.element.style.backgroundSize = "cover";
6100
+ }
6101
+ if ((_e = this.config) == null ? void 0 : _e.overlayLayers) {
6102
+ const logLoadError = (imageUrl) => {
6103
+ console.error(`Failed to load overlay image: ${imageUrl}`);
6104
+ };
6105
+ for (const layer of this.config.overlayLayers) {
6106
+ const overlayLayer = document.createElement("div");
6107
+ overlayLayer.style.position = "absolute";
6108
+ overlayLayer.style.background = `url(${layer.overlayImageUrl}) no-repeat`;
6109
+ overlayLayer.style.backgroundSize = "contain";
6110
+ const anchor = layer.overlayAnchor;
6111
+ const offsetX = ((_f = layer.overlayOffset) == null ? void 0 : _f.x) || 0;
6112
+ const offsetY = ((_g = layer.overlayOffset) == null ? void 0 : _g.y) || 0;
6113
+ if (anchor.includes("top")) {
6114
+ overlayLayer.style.top = `${offsetY}px`;
6115
+ } else if (anchor.includes("bottom")) {
6116
+ overlayLayer.style.bottom = `${offsetY}px`;
6117
+ }
6118
+ if (anchor.includes("left")) {
6119
+ overlayLayer.style.left = `${offsetX}px`;
6120
+ } else if (anchor.includes("right")) {
6121
+ overlayLayer.style.right = `${offsetX}px`;
6122
+ }
6123
+ const image = new Image();
6124
+ image.src = layer.overlayImageUrl;
6125
+ image.onload = () => {
6126
+ const naturalWidth = image.naturalWidth;
6127
+ const naturalHeight = image.naturalHeight;
6128
+ overlayLayer.style.width = `${naturalWidth}px`;
6129
+ overlayLayer.style.height = `${naturalHeight}px`;
6130
+ };
6131
+ image.onerror = () => logLoadError(layer.overlayImageUrl);
6132
+ this.overlayLayers.push(overlayLayer);
6133
+ this.backgroundBlur.append(overlayLayer);
6134
+ }
6135
+ }
6136
+ this.element.style.color = ((_h = this.config) == null ? void 0 : _h.color) || "white";
6137
+ this.loadingBanner = document.createElement("div");
6138
+ this.loadingBanner.style.position = "absolute";
6139
+ this.loadingBanner.style.display = "flex";
6140
+ this.loadingBanner.style.flexDirection = "column";
6141
+ this.loadingBanner.style.left = "0";
6142
+ this.loadingBanner.style.bottom = "0";
6143
+ this.loadingBanner.style.padding = "0";
6144
+ this.loadingBanner.style.width = "100%";
6145
+ this.loadingBanner.style.justifyContent = "flex-end";
6146
+ this.backgroundBlur.append(this.loadingBanner);
6147
+ if ((_i = this.config) == null ? void 0 : _i.title) {
6148
+ this.loadingBannerTitle = document.createElement("div");
6149
+ this.loadingBannerTitle.textContent = this.config.title;
6150
+ this.loadingBannerTitle.style.color = ((_j = this.config) == null ? void 0 : _j.color) || "white";
6151
+ this.loadingBannerTitle.style.paddingLeft = "40px";
6152
+ this.loadingBannerTitle.style.paddingRight = "40px";
6153
+ this.loadingBannerTitle.style.fontSize = "42px";
6154
+ this.loadingBannerTitle.style.fontWeight = "bold";
6155
+ this.loadingBannerTitle.style.fontFamily = "sans-serif";
6156
+ if ((_k = this.config) == null ? void 0 : _k.background) {
6157
+ this.loadingBannerTitle.style.textShadow = `0px 0px 80px ${this.config.background}`;
6158
+ }
6159
+ this.loadingBanner.append(this.loadingBannerTitle);
6160
+ }
6161
+ if ((_l = this.config) == null ? void 0 : _l.subtitle) {
6162
+ this.loadingBannerSubtitle = document.createElement("div");
6163
+ this.loadingBannerSubtitle.style.color = ((_m = this.config) == null ? void 0 : _m.color) || "white";
6164
+ this.loadingBannerSubtitle.style.paddingLeft = "40px";
6165
+ this.loadingBannerSubtitle.style.paddingRight = "40px";
6166
+ this.loadingBannerSubtitle.style.fontSize = "16px";
6167
+ this.loadingBannerSubtitle.style.fontWeight = "400";
6168
+ this.loadingBannerSubtitle.style.fontFamily = "sans-serif";
6169
+ this.loadingBannerSubtitle.style.marginTop = "12px";
6170
+ if ((_n = this.config) == null ? void 0 : _n.background) {
6171
+ this.loadingBannerSubtitle.style.textShadow = `0px 0px 40px ${this.config.background}`;
6172
+ }
6173
+ this.loadingBannerSubtitle.textContent = this.config.subtitle;
6174
+ this.loadingBanner.append(this.loadingBannerSubtitle);
6175
+ }
5902
6176
  this.progressDebugViewHolder = document.createElement("div");
5903
- this.progressDebugViewHolder.style.display = "flex";
6177
+ this.progressDebugViewHolder.style.display = "none";
5904
6178
  this.progressDebugViewHolder.style.position = "absolute";
5905
- this.progressDebugViewHolder.style.maxHeight = "calc(100% - 74px)";
5906
- this.progressDebugViewHolder.style.left = "0";
5907
- this.progressDebugViewHolder.style.bottom = "74px";
5908
- this.progressDebugViewHolder.style.width = "100%";
6179
+ this.progressDebugViewHolder.style.width = "calc(100% - 80px)";
6180
+ this.progressDebugViewHolder.style.maxHeight = "calc(100% - 120px)";
6181
+ this.progressDebugViewHolder.style.left = "40px";
6182
+ this.progressDebugViewHolder.style.bottom = "60px";
6183
+ this.progressDebugViewHolder.style.alignItems = "center";
5909
6184
  this.progressDebugViewHolder.style.justifyContent = "center";
6185
+ this.progressDebugViewHolder.style.zIndex = "10003";
5910
6186
  this.element.append(this.progressDebugViewHolder);
5911
6187
  this.progressDebugView = document.createElement("div");
5912
- this.progressDebugView.style.backgroundColor = "rgba(128, 128, 128, 0.25)";
6188
+ this.progressDebugView.style.backgroundColor = "rgba(128, 128, 128, 0.5)";
5913
6189
  this.progressDebugView.style.border = "1px solid black";
6190
+ this.progressDebugView.style.borderRadius = "7px";
6191
+ this.progressDebugView.style.width = "100%";
5914
6192
  this.progressDebugView.style.maxWidth = "100%";
5915
6193
  this.progressDebugView.style.overflow = "auto";
5916
6194
  this.progressDebugViewHolder.append(this.progressDebugView);
@@ -5919,6 +6197,8 @@ var LoadingScreen = class {
5919
6197
  this.debugCheckbox.checked = false;
5920
6198
  this.debugCheckbox.addEventListener("change", () => {
5921
6199
  this.progressDebugElement.style.display = this.debugCheckbox.checked ? "block" : "none";
6200
+ this.loadingBannerTitle.style.display = this.debugCheckbox.checked ? "none" : "flex";
6201
+ this.loadingBannerSubtitle.style.display = this.debugCheckbox.checked ? "none" : "flex";
5922
6202
  if (this.hasCompleted) {
5923
6203
  this.dispose();
5924
6204
  }
@@ -5937,23 +6217,36 @@ var LoadingScreen = class {
5937
6217
  this.progressDebugView.append(this.progressDebugElement);
5938
6218
  this.progressBarHolder = document.createElement("div");
5939
6219
  this.progressBarHolder.style.display = "flex";
5940
- this.progressBarHolder.style.alignItems = "center";
5941
- this.progressBarHolder.style.justifyContent = "center";
5942
- this.progressBarHolder.style.position = "absolute";
5943
- this.progressBarHolder.style.bottom = "20px";
5944
- this.progressBarHolder.style.left = "0";
6220
+ this.progressBarHolder.style.alignItems = "start";
6221
+ this.progressBarHolder.style.justifyContent = "flex-start";
5945
6222
  this.progressBarHolder.style.width = "100%";
5946
- this.element.append(this.progressBarHolder);
6223
+ this.progressBarHolder.style.marginLeft = "40px";
6224
+ this.progressBarHolder.style.marginBottom = "40px";
6225
+ this.progressBarHolder.style.cursor = "pointer";
6226
+ this.progressBarHolder.style.marginTop = "24px";
6227
+ this.loadingBanner.append(this.progressBarHolder);
5947
6228
  this.progressBarBackground = document.createElement("div");
5948
6229
  this.progressBarBackground.style.position = "relative";
5949
- this.progressBarBackground.style.width = "500px";
5950
- this.progressBarBackground.style.maxWidth = "80%";
5951
- this.progressBarBackground.style.backgroundColor = "gray";
5952
- this.progressBarBackground.style.height = "50px";
5953
- this.progressBarBackground.style.lineHeight = "50px";
5954
- this.progressBarBackground.style.borderRadius = "25px";
5955
- this.progressBarBackground.style.border = "2px solid white";
6230
+ this.progressBarBackground.style.width = "80%";
6231
+ this.progressBarBackground.style.maxWidth = "400px";
6232
+ this.progressBarBackground.style.minWidth = "240px";
6233
+ this.progressBarBackground.style.backgroundColor = "rgba(32,32,32, 0.25)";
6234
+ this.progressBarBackground.style.backdropFilter = "blur(4px)";
6235
+ this.progressBarBackground.style.height = "16px";
6236
+ this.progressBarBackground.style.lineHeight = "16px";
6237
+ this.progressBarBackground.style.borderRadius = "16px";
5956
6238
  this.progressBarBackground.style.overflow = "hidden";
6239
+ this.progressBarBackground.addEventListener("click", () => {
6240
+ const display = this.progressDebugViewHolder.style.display;
6241
+ if (display === "none") {
6242
+ this.progressDebugViewHolder.style.display = "flex";
6243
+ } else {
6244
+ this.progressDebugViewHolder.style.display = "none";
6245
+ this.debugCheckbox.checked = false;
6246
+ this.progressDebugElement.style.display = this.debugCheckbox.checked ? "block" : "none";
6247
+ this.loadingBannerTitle.style.display = this.debugCheckbox.checked ? "none" : "flex";
6248
+ }
6249
+ });
5957
6250
  this.progressBarHolder.append(this.progressBarBackground);
5958
6251
  this.progressBar = document.createElement("div");
5959
6252
  this.progressBar.style.position = "absolute";
@@ -5961,7 +6254,8 @@ var LoadingScreen = class {
5961
6254
  this.progressBar.style.left = "0";
5962
6255
  this.progressBar.style.width = "0";
5963
6256
  this.progressBar.style.height = "100%";
5964
- this.progressBar.style.backgroundColor = "#0050a4";
6257
+ this.progressBar.style.pointerEvents = "none";
6258
+ this.progressBar.style.backgroundColor = ((_o = this.config) == null ? void 0 : _o.color) || "#0050a4";
5965
6259
  this.progressBarBackground.append(this.progressBar);
5966
6260
  this.loadingStatusText = document.createElement("div");
5967
6261
  this.loadingStatusText.style.position = "absolute";
@@ -5969,11 +6263,14 @@ var LoadingScreen = class {
5969
6263
  this.loadingStatusText.style.left = "0";
5970
6264
  this.loadingStatusText.style.width = "100%";
5971
6265
  this.loadingStatusText.style.height = "100%";
5972
- this.loadingStatusText.style.color = "white";
6266
+ this.loadingStatusText.style.color = "rgba(200,200,200,0.9)";
6267
+ this.loadingStatusText.style.fontSize = "10px";
5973
6268
  this.loadingStatusText.style.textAlign = "center";
5974
6269
  this.loadingStatusText.style.verticalAlign = "middle";
6270
+ this.loadingStatusText.style.mixBlendMode = "difference";
5975
6271
  this.loadingStatusText.style.fontFamily = "sans-serif";
5976
6272
  this.loadingStatusText.style.fontWeight = "bold";
6273
+ this.loadingStatusText.style.userSelect = "none";
5977
6274
  this.loadingStatusText.textContent = "Loading...";
5978
6275
  this.progressBarBackground.append(this.loadingStatusText);
5979
6276
  this.loadingCallback = () => {
@@ -5988,7 +6285,7 @@ var LoadingScreen = class {
5988
6285
  this.loadingStatusText.textContent = "Completed";
5989
6286
  this.progressBar.style.width = "100%";
5990
6287
  } else {
5991
- this.loadingStatusText.textContent = `Loading... ${(loadingRatio * 100).toFixed(2)}%`;
6288
+ this.loadingStatusText.textContent = `${(loadingRatio * 100).toFixed(2)}%`;
5992
6289
  this.progressBar.style.width = `${loadingRatio * 100}%`;
5993
6290
  }
5994
6291
  this.progressDebugElement.textContent = LoadingProgressManager2.LoadingProgressSummaryToString(
@@ -6048,6 +6345,7 @@ export {
6048
6345
  Composer,
6049
6346
  ErrorScreen,
6050
6347
  GroundPlane,
6348
+ Key,
6051
6349
  KeyInputManager,
6052
6350
  LoadingScreen,
6053
6351
  MMLCompositionScene,