@mml-io/3d-web-client-core 0.15.0 → 0.17.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
@@ -83,8 +83,9 @@ var EventHandlerCollection = class _EventHandlerCollection {
83
83
  };
84
84
 
85
85
  // src/input/VirtualJoystick.ts
86
- var _VirtualJoystick = class _VirtualJoystick {
87
- constructor(attrs) {
86
+ var VirtualJoystick = class _VirtualJoystick {
87
+ constructor(holderElement, attrs) {
88
+ this.holderElement = holderElement;
88
89
  this.left = false;
89
90
  this.right = false;
90
91
  this.up = false;
@@ -107,7 +108,23 @@ var _VirtualJoystick = class _VirtualJoystick {
107
108
  this.width = attrs.width || this.radius * 2 + this.inner_radius * 2;
108
109
  this.height = attrs.height || this.radius * 2 + this.inner_radius * 2;
109
110
  this.mouse_support = this.checkTouch() || attrs.mouse_support === true;
110
- this.initializeJoystick();
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();
111
128
  }
112
129
  static checkForTouch() {
113
130
  try {
@@ -117,38 +134,9 @@ var _VirtualJoystick = class _VirtualJoystick {
117
134
  return false;
118
135
  }
119
136
  }
120
- static isTouchOnJoystick(touch) {
121
- if (!_VirtualJoystick.JOYSTICK_DIV) {
122
- return false;
123
- }
124
- const divRect = _VirtualJoystick.JOYSTICK_DIV.getBoundingClientRect();
125
- return touch.clientX >= divRect.left && touch.clientX <= divRect.right && touch.clientY >= divRect.top && touch.clientY <= divRect.bottom;
126
- }
127
137
  checkTouch() {
128
138
  return _VirtualJoystick.checkForTouch();
129
139
  }
130
- initializeJoystick() {
131
- if (!_VirtualJoystick.JOYSTICK_DIV) {
132
- this.div = document.createElement("div");
133
- const divStyle = this.div.style;
134
- divStyle.display = this.checkTouch() || this.mouse_support ? "visible" : "none";
135
- divStyle.position = "fixed";
136
- if (this.anchor === "left") {
137
- divStyle.left = `${this.x}px`;
138
- } else {
139
- divStyle.right = `${this.x}px`;
140
- }
141
- divStyle.bottom = `${this.y}px`;
142
- divStyle.width = `${this.width}px`;
143
- divStyle.height = `${this.height}px`;
144
- divStyle.zIndex = "10000";
145
- divStyle.overflow = "hidden";
146
- document.body.appendChild(this.div);
147
- _VirtualJoystick.JOYSTICK_DIV = this.div;
148
- }
149
- this.setupBaseAndControl();
150
- this.bindEvents();
151
- }
152
140
  setupBaseAndControl() {
153
141
  this.base = document.createElement("span");
154
142
  let divStyle = this.base.style;
@@ -188,6 +176,7 @@ var _VirtualJoystick = class _VirtualJoystick {
188
176
  }
189
177
  handleTouchStart(evt) {
190
178
  evt.preventDefault();
179
+ evt.stopPropagation();
191
180
  if (evt.touches) {
192
181
  const touch = evt.touches[0];
193
182
  this.updateControlAndDirection(touch);
@@ -195,6 +184,7 @@ var _VirtualJoystick = class _VirtualJoystick {
195
184
  }
196
185
  handleTouchMove(evt) {
197
186
  evt.preventDefault();
187
+ evt.stopPropagation();
198
188
  if (evt.touches.length > 0) {
199
189
  const touch = evt.touches[0];
200
190
  this.updateControlAndDirection(touch);
@@ -202,11 +192,13 @@ var _VirtualJoystick = class _VirtualJoystick {
202
192
  }
203
193
  handleMouseDown(evt) {
204
194
  evt.preventDefault();
195
+ evt.stopPropagation();
205
196
  this.updateControlAndDirection(evt);
206
197
  }
207
198
  handleMouseMove(evt) {
208
199
  if (evt.buttons === 1) {
209
200
  evt.preventDefault();
201
+ evt.stopPropagation();
210
202
  this.updateControlAndDirection(evt);
211
203
  }
212
204
  }
@@ -242,8 +234,132 @@ var _VirtualJoystick = class _VirtualJoystick {
242
234
  return dx > 0 && Math.abs(dy) <= 2 * Math.abs(dx);
243
235
  }
244
236
  };
245
- _VirtualJoystick.JOYSTICK_DIV = null;
246
- var VirtualJoystick = _VirtualJoystick;
237
+
238
+ // src/tweakpane/blades/cameraFolder.ts
239
+ var camValues = {
240
+ initialDistance: 3.3,
241
+ minDistance: 0.1,
242
+ maxDistance: 8,
243
+ initialFOV: 60,
244
+ maxFOV: 85,
245
+ minFOV: 60,
246
+ invertFOVMapping: false,
247
+ damping: 0.091,
248
+ dampingScale: 0.01,
249
+ zoomScale: 0.05,
250
+ zoomDamping: 0.3
251
+ };
252
+ var camOptions = {
253
+ initialDistance: { min: 1, max: 5, step: 0.1 },
254
+ minDistance: { min: 0.1, max: 2, step: 0.1 },
255
+ maxDistance: { min: 5, max: 20, step: 0.5 },
256
+ initialFOV: { min: 60, max: 85, step: 1 },
257
+ maxFOV: { min: 50, max: 100, step: 1 },
258
+ 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 },
261
+ zoomScale: { min: 5e-3, max: 0.3, step: 1e-3 },
262
+ zoomDamping: { min: 0, max: 2, step: 0.01 }
263
+ };
264
+ var CameraFolder = class {
265
+ constructor(parentFolder, expand = false) {
266
+ this.camData = {
267
+ distance: "0",
268
+ FoV: "0"
269
+ };
270
+ this.folder = parentFolder.addFolder({ title: "camera", expanded: expand });
271
+ this.folder.addBinding(this.camData, "distance", { readonly: true });
272
+ this.folder.addBinding(this.camData, "FoV", { readonly: true });
273
+ this.folder.addBinding(camValues, "initialDistance", camOptions.initialDistance);
274
+ this.folder.addBinding(camValues, "minDistance", camOptions.minDistance);
275
+ this.folder.addBinding(camValues, "maxDistance", camOptions.maxDistance);
276
+ this.folder.addBinding(camValues, "minFOV", camOptions.minFOV);
277
+ this.folder.addBinding(camValues, "maxFOV", camOptions.maxFOV);
278
+ this.folder.addBinding({ invertFOVMapping: camValues.invertFOVMapping }, "invertFOVMapping");
279
+ this.folder.addBinding(camValues, "damping", camOptions.damping);
280
+ this.folder.addBinding(camValues, "dampingScale", camOptions.dampingScale);
281
+ this.folder.addBinding(camValues, "zoomScale", camOptions.zoomScale);
282
+ this.folder.addBinding(camValues, "zoomDamping", camOptions.zoomDamping);
283
+ }
284
+ setupChangeEvent(cameraManager) {
285
+ this.folder.on("change", (e) => {
286
+ const target = e.target.key;
287
+ if (!target)
288
+ return;
289
+ switch (target) {
290
+ case "initialDistance": {
291
+ const value = e.value;
292
+ cameraManager.initialDistance = value;
293
+ cameraManager.distance = value;
294
+ cameraManager.targetDistance = value;
295
+ cameraManager.desiredDistance = value;
296
+ cameraManager.recomputeFoV();
297
+ break;
298
+ }
299
+ case "minDistance": {
300
+ const value = e.value;
301
+ cameraManager.minDistance = value;
302
+ cameraManager.distance = value;
303
+ cameraManager.targetDistance = value;
304
+ cameraManager.desiredDistance = value;
305
+ cameraManager.recomputeFoV();
306
+ break;
307
+ }
308
+ case "maxDistance": {
309
+ const value = e.value;
310
+ cameraManager.maxDistance = value;
311
+ cameraManager.distance = value;
312
+ cameraManager.targetDistance = value;
313
+ cameraManager.desiredDistance = value;
314
+ cameraManager.recomputeFoV();
315
+ break;
316
+ }
317
+ case "minFOV": {
318
+ const value = e.value;
319
+ cameraManager.minFOV = value;
320
+ cameraManager.recomputeFoV();
321
+ break;
322
+ }
323
+ case "maxFOV": {
324
+ const value = e.value;
325
+ cameraManager.maxFOV = value;
326
+ cameraManager.recomputeFoV();
327
+ break;
328
+ }
329
+ case "invertFOVMapping":
330
+ const boolValue = e.value;
331
+ cameraManager.invertFOVMapping = boolValue;
332
+ break;
333
+ case "damping": {
334
+ const value = e.value;
335
+ cameraManager.damping = value;
336
+ break;
337
+ }
338
+ case "dampingScale": {
339
+ const value = e.value;
340
+ cameraManager.dampingScale = value;
341
+ break;
342
+ }
343
+ case "zoomScale": {
344
+ const value = e.value;
345
+ cameraManager.zoomScale = value;
346
+ break;
347
+ }
348
+ case "zoomDamping": {
349
+ const value = e.value;
350
+ cameraManager.zoomDamping = value;
351
+ break;
352
+ }
353
+ default:
354
+ break;
355
+ }
356
+ });
357
+ }
358
+ update(cameraManager) {
359
+ this.camData.distance = cameraManager.distance.toFixed(2);
360
+ this.camData.FoV = cameraManager.fov.toFixed(2);
361
+ }
362
+ };
247
363
 
248
364
  // src/tweakpane/tweakPaneActivity.ts
249
365
  var isTweakpaneActive = false;
@@ -258,17 +374,21 @@ function getTweakpaneActive() {
258
374
  var CameraManager = class {
259
375
  constructor(targetElement, collisionsManager, initialPhi = Math.PI / 2, initialTheta = -Math.PI / 2) {
260
376
  this.collisionsManager = collisionsManager;
261
- this.initialDistance = 3.3;
262
- this.minDistance = 0.1;
263
- this.maxDistance = 8;
264
- this.initialFOV = 60;
377
+ this.initialDistance = camValues.initialDistance;
378
+ this.minDistance = camValues.minDistance;
379
+ this.maxDistance = camValues.maxDistance;
380
+ this.initialFOV = camValues.initialFOV;
381
+ this.maxFOV = camValues.maxFOV;
382
+ this.minFOV = camValues.minFOV;
383
+ this.damping = camValues.damping;
384
+ this.dampingScale = 0.01;
385
+ this.zoomScale = camValues.zoomScale;
386
+ this.zoomDamping = camValues.zoomDamping;
387
+ this.invertFOVMapping = camValues.invertFOVMapping;
265
388
  this.fov = this.initialFOV;
266
- this.minFOV = 85;
267
- this.maxFOV = 60;
268
389
  this.targetFOV = this.initialFOV;
269
390
  this.minPolarAngle = Math.PI * 0.25;
270
391
  this.maxPolarAngle = Math.PI * 0.95;
271
- this.dampingFactor = 0.091;
272
392
  this.targetDistance = this.initialDistance;
273
393
  this.distance = this.initialDistance;
274
394
  this.desiredDistance = this.initialDistance;
@@ -297,7 +417,8 @@ var CameraManager = class {
297
417
  [targetElement, "mousedown", this.onMouseDown.bind(this)],
298
418
  [document, "mouseup", this.onMouseUp.bind(this)],
299
419
  [document, "mousemove", this.onMouseMove.bind(this)],
300
- [targetElement, "wheel", this.onMouseWheel.bind(this)]
420
+ [targetElement, "wheel", this.onMouseWheel.bind(this)],
421
+ [targetElement, "contextmenu", this.onContextMenu.bind(this)]
301
422
  ]);
302
423
  if (this.hasTouchControl) {
303
424
  this.eventHandlerCollection.add(targetElement, "touchstart", this.onTouchStart.bind(this), {
@@ -311,13 +432,14 @@ var CameraManager = class {
311
432
  });
312
433
  }
313
434
  }
435
+ setupTweakPane(tweakPane) {
436
+ tweakPane.setupCamPane(this);
437
+ }
314
438
  onTouchStart(evt) {
315
439
  Array.from(evt.touches).forEach((touch) => {
316
- if (!VirtualJoystick.isTouchOnJoystick(touch)) {
317
- this.dragging = true;
318
- this.lastTouchX = touch.clientX;
319
- this.lastTouchY = touch.clientY;
320
- }
440
+ this.dragging = true;
441
+ this.lastTouchX = touch.clientX;
442
+ this.lastTouchY = touch.clientY;
321
443
  });
322
444
  }
323
445
  onTouchMove(evt) {
@@ -325,47 +447,53 @@ var CameraManager = class {
325
447
  return;
326
448
  }
327
449
  evt.preventDefault();
328
- const touch = Array.from(evt.touches).find((t) => !VirtualJoystick.isTouchOnJoystick(t));
450
+ const touch = Array.from(evt.touches).find((t) => true);
329
451
  if (touch) {
330
452
  const dx = touch.clientX - this.lastTouchX;
331
453
  const dy = touch.clientY - this.lastTouchY;
332
454
  this.lastTouchX = touch.clientX;
333
455
  this.lastTouchY = touch.clientY;
334
456
  if (this.targetTheta !== null && this.targetPhi !== null) {
335
- this.targetTheta += dx * 0.01;
336
- this.targetPhi -= dy * 0.01;
457
+ this.targetTheta += dx * this.dampingScale;
458
+ this.targetPhi -= dy * this.dampingScale;
337
459
  this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
338
460
  }
339
461
  }
340
462
  }
341
463
  onTouchEnd(evt) {
342
464
  if (this.dragging) {
343
- const touchEnded = Array.from(evt.changedTouches).some(
344
- (t) => !VirtualJoystick.isTouchOnJoystick(t)
345
- );
465
+ const touchEnded = Array.from(evt.changedTouches).some((t) => true);
346
466
  if (touchEnded) {
347
467
  this.dragging = false;
348
468
  }
349
469
  }
350
470
  }
351
- onMouseDown() {
352
- this.dragging = true;
471
+ onMouseDown(event) {
472
+ if (event.button === 0 || event.button === 2) {
473
+ this.dragging = true;
474
+ document.body.style.cursor = "none";
475
+ }
353
476
  }
354
- onMouseUp(_event) {
355
- this.dragging = false;
477
+ onMouseUp(event) {
478
+ if (event.button === 0 || event.button === 2) {
479
+ this.dragging = false;
480
+ document.body.style.cursor = "default";
481
+ }
356
482
  }
357
483
  onMouseMove(event) {
358
- if (!this.dragging || getTweakpaneActive())
359
- return;
360
- if (this.targetTheta === null || this.targetPhi === null)
484
+ if (getTweakpaneActive())
361
485
  return;
362
- this.targetTheta += event.movementX * 0.01;
363
- this.targetPhi -= event.movementY * 0.01;
364
- this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
365
- event.preventDefault();
486
+ if (this.dragging) {
487
+ if (this.targetTheta === null || this.targetPhi === null)
488
+ return;
489
+ this.targetTheta += event.movementX * this.dampingScale;
490
+ this.targetPhi -= event.movementY * this.dampingScale;
491
+ this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
492
+ event.preventDefault();
493
+ }
366
494
  }
367
495
  onMouseWheel(event) {
368
- const scrollAmount = event.deltaY * 1e-3;
496
+ const scrollAmount = event.deltaY * this.zoomScale * 0.1;
369
497
  this.targetDistance += scrollAmount;
370
498
  this.targetDistance = Math.max(
371
499
  this.minDistance,
@@ -374,6 +502,9 @@ var CameraManager = class {
374
502
  this.desiredDistance = this.targetDistance;
375
503
  event.preventDefault();
376
504
  }
505
+ onContextMenu(event) {
506
+ event.preventDefault();
507
+ }
377
508
  setTarget(target) {
378
509
  if (!this.isLerping) {
379
510
  this.target.copy(target);
@@ -404,14 +535,7 @@ var CameraManager = class {
404
535
  this.theta = this.targetTheta;
405
536
  this.distance = this.targetDistance;
406
537
  this.desiredDistance = this.targetDistance;
407
- this.targetFOV = remap(
408
- this.targetDistance,
409
- this.minDistance,
410
- this.maxDistance,
411
- this.minFOV,
412
- this.maxFOV
413
- );
414
- this.fov = this.targetFOV;
538
+ this.recomputeFoV(true);
415
539
  }
416
540
  adjustCameraPosition() {
417
541
  const offsetDistance = 0.5;
@@ -426,7 +550,7 @@ var CameraManager = class {
426
550
  this.targetDistance = cameraToPlayerDistance - firstRaycastHit[0];
427
551
  this.distance = this.targetDistance;
428
552
  } else {
429
- this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
553
+ this.targetDistance += (this.desiredDistance - this.targetDistance) * this.damping * 4;
430
554
  }
431
555
  }
432
556
  dispose() {
@@ -438,6 +562,18 @@ var CameraManager = class {
438
562
  updateAspect(aspect) {
439
563
  this.camera.aspect = aspect;
440
564
  }
565
+ recomputeFoV(immediately = false) {
566
+ this.targetFOV = remap(
567
+ this.targetDistance,
568
+ this.minDistance,
569
+ this.maxDistance,
570
+ this.invertFOVMapping ? this.minFOV : this.maxFOV,
571
+ this.invertFOVMapping ? this.maxFOV : this.minFOV
572
+ );
573
+ if (immediately) {
574
+ this.fov = this.targetFOV;
575
+ }
576
+ }
441
577
  update() {
442
578
  if (this.isLerping && this.lerpFactor < 1) {
443
579
  this.lerpFactor += 0.01 / this.lerpDuration;
@@ -447,20 +583,14 @@ var CameraManager = class {
447
583
  this.adjustCameraPosition();
448
584
  }
449
585
  if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
450
- this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
451
- this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
452
- this.theta += (this.targetTheta - this.theta) * this.dampingFactor;
586
+ this.distance += (this.targetDistance - this.distance) * this.damping * (0.21 + this.zoomDamping);
587
+ this.phi += (this.targetPhi - this.phi) * this.damping;
588
+ this.theta += (this.targetTheta - this.theta) * this.damping;
453
589
  const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
454
590
  const y = this.target.y + this.distance * Math.cos(this.phi);
455
591
  const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
456
- this.targetFOV = remap(
457
- this.targetDistance,
458
- this.minDistance,
459
- this.maxDistance,
460
- this.minFOV,
461
- this.maxFOV
462
- );
463
- this.fov += (this.targetFOV - this.fov) * this.dampingFactor;
592
+ this.recomputeFoV();
593
+ this.fov += (this.targetFOV - this.fov) * this.damping;
464
594
  this.camera.fov = this.fov;
465
595
  this.camera.updateProjectionMatrix();
466
596
  this.camera.position.set(x, y, z);
@@ -478,9 +608,9 @@ import { Color as Color3, Group, Vector3 as Vector34 } from "three";
478
608
  // src/character/CharacterModel.ts
479
609
  import {
480
610
  MMLCharacter,
481
- ModelLoader,
482
611
  parseMMLDescription
483
612
  } from "@mml-io/3d-web-avatar";
613
+ import { ModelLoader } from "@mml-io/model-loader";
484
614
  import {
485
615
  AnimationClip,
486
616
  AnimationMixer,
@@ -634,13 +764,9 @@ var CharacterFolder = class {
634
764
 
635
765
  // src/character/CharacterMaterial.ts
636
766
  var CharacterMaterial = class extends MeshStandardMaterial {
637
- constructor(isLocal, cameraManager, characterId, originalMaterial, colorOverride) {
767
+ constructor(config) {
638
768
  super();
639
- this.isLocal = isLocal;
640
- this.cameraManager = cameraManager;
641
- this.characterId = characterId;
642
- this.originalMaterial = originalMaterial;
643
- this.colorOverride = colorOverride;
769
+ this.config = config;
644
770
  this.uniforms = {
645
771
  discardAll: { value: 1 },
646
772
  diffuseColor: { value: new Color() },
@@ -649,12 +775,12 @@ var CharacterMaterial = class extends MeshStandardMaterial {
649
775
  this.colorsCube216 = [];
650
776
  this.targetAlpha = 1;
651
777
  this.currentAlpha = 1;
652
- this.copy(this.originalMaterial);
778
+ this.copy(this.config.originalMaterial);
653
779
  this.generateColorCube();
654
- this.color = this.colorOverride || this.colorsCube216[this.characterId];
780
+ this.color = this.config.colorOverride || this.colorsCube216[this.config.characterId];
655
781
  this.envMapIntensity = characterValues.envMapIntensity;
656
782
  this.transparent = true;
657
- this.side = this.originalMaterial.side;
783
+ this.side = this.config.originalMaterial.side;
658
784
  this.onBeforeCompile = (shader) => {
659
785
  this.uniforms = UniformsUtils.clone(shader.uniforms);
660
786
  this.uniforms.nearClip = { value: 0.01 };
@@ -690,7 +816,7 @@ var CharacterMaterial = class extends MeshStandardMaterial {
690
816
  );
691
817
  shader.fragmentShader = injectBefore(
692
818
  shader.fragmentShader,
693
- "#include <output_fragment>",
819
+ "#include <dithering_fragment>",
694
820
  /* glsl */
695
821
  `
696
822
  if (discardAll == 1) {
@@ -730,16 +856,16 @@ var CharacterMaterial = class extends MeshStandardMaterial {
730
856
  }
731
857
  }
732
858
  update() {
733
- if (this.isLocal) {
734
- this.targetAlpha = this.cameraManager.targetDistance < 0.4 ? 0 : 1;
859
+ if (this.config.isLocal) {
860
+ this.targetAlpha = this.config.cameraManager.distance < 0.4 ? 0 : 1;
735
861
  this.currentAlpha += ease(this.targetAlpha, this.currentAlpha, 0.07);
736
862
  if (this.currentAlpha > 0.999) {
737
863
  this.currentAlpha = 1;
738
- this.cameraManager.minPolarAngle = Math.PI * 0.25;
864
+ this.config.cameraManager.minPolarAngle = Math.PI * 0.25;
739
865
  }
740
866
  if (this.currentAlpha < 1e-3) {
741
867
  this.currentAlpha = 0;
742
- this.cameraManager.minPolarAngle = Math.PI * 0.35;
868
+ this.config.cameraManager.minPolarAngle = Math.PI * 0.35;
743
869
  }
744
870
  this.uniforms.discardAll.value = this.currentAlpha === 0 ? 1 : 0;
745
871
  if (this.currentAlpha !== this.opacity) {
@@ -766,61 +892,79 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
766
892
  AnimationState2[AnimationState2["jumpToAir"] = 3] = "jumpToAir";
767
893
  AnimationState2[AnimationState2["air"] = 4] = "air";
768
894
  AnimationState2[AnimationState2["airToGround"] = 5] = "airToGround";
895
+ AnimationState2[AnimationState2["doubleJump"] = 6] = "doubleJump";
769
896
  return AnimationState2;
770
897
  })(AnimationState || {});
771
898
 
772
899
  // src/character/CharacterModel.ts
773
- var CharacterModel = class {
774
- constructor(characterDescription, animationConfig, characterModelLoader, cameraManager, characterId, isLocal) {
775
- this.characterDescription = characterDescription;
776
- this.animationConfig = animationConfig;
777
- this.characterModelLoader = characterModelLoader;
778
- this.cameraManager = cameraManager;
779
- this.characterId = characterId;
780
- this.isLocal = isLocal;
900
+ var _CharacterModel = class _CharacterModel {
901
+ constructor(config) {
902
+ this.config = config;
781
903
  this.mesh = null;
782
904
  this.headBone = null;
783
905
  this.materials = /* @__PURE__ */ new Map();
784
906
  this.animations = {};
785
907
  this.animationMixer = null;
786
908
  this.currentAnimation = 0 /* idle */;
909
+ this.isPostDoubleJump = false;
787
910
  }
788
911
  async init() {
789
912
  await this.loadMainMesh();
790
- await this.setAnimationFromFile(this.animationConfig.idleAnimationFileUrl, 0 /* idle */);
791
913
  await this.setAnimationFromFile(
792
- this.animationConfig.jogAnimationFileUrl,
793
- 1 /* walking */
914
+ this.config.animationConfig.idleAnimationFileUrl,
915
+ 0 /* idle */,
916
+ true
917
+ );
918
+ await this.setAnimationFromFile(
919
+ this.config.animationConfig.jogAnimationFileUrl,
920
+ 1 /* walking */,
921
+ true
922
+ );
923
+ await this.setAnimationFromFile(
924
+ this.config.animationConfig.sprintAnimationFileUrl,
925
+ 2 /* running */,
926
+ true
927
+ );
928
+ await this.setAnimationFromFile(
929
+ this.config.animationConfig.airAnimationFileUrl,
930
+ 4 /* air */,
931
+ true
794
932
  );
795
933
  await this.setAnimationFromFile(
796
- this.animationConfig.sprintAnimationFileUrl,
797
- 2 /* running */
934
+ this.config.animationConfig.doubleJumpAnimationFileUrl,
935
+ 6 /* doubleJump */,
936
+ false,
937
+ 1.3
798
938
  );
799
- await this.setAnimationFromFile(this.animationConfig.airAnimationFileUrl, 4 /* air */);
800
939
  this.applyCustomMaterials();
801
940
  }
802
941
  applyCustomMaterials() {
803
942
  if (!this.mesh)
804
943
  return;
805
944
  this.mesh.traverse((child) => {
945
+ if (child.isBone) {
946
+ if (child.name === "head") {
947
+ this.headBone = child;
948
+ }
949
+ }
806
950
  if (child.isMesh || child.isSkinnedMesh) {
807
951
  const asMesh = child;
808
952
  const originalMaterial = asMesh.material;
809
953
  if (this.materials.has(originalMaterial.name)) {
810
954
  asMesh.material = this.materials.get(originalMaterial.name);
811
955
  } else {
812
- const material = originalMaterial.name === "body_replaceable_color" ? new CharacterMaterial(
813
- this.isLocal,
814
- this.cameraManager,
815
- this.characterId,
956
+ const material = originalMaterial.name === "body_replaceable_color" ? new CharacterMaterial({
957
+ isLocal: this.config.isLocal,
958
+ cameraManager: this.config.cameraManager,
959
+ characterId: this.config.characterId,
816
960
  originalMaterial
817
- ) : new CharacterMaterial(
818
- this.isLocal,
819
- this.cameraManager,
820
- this.characterId,
961
+ }) : new CharacterMaterial({
962
+ isLocal: this.config.isLocal,
963
+ cameraManager: this.config.cameraManager,
964
+ characterId: this.config.characterId,
821
965
  originalMaterial,
822
- originalMaterial.color
823
- );
966
+ colorOverride: originalMaterial.color
967
+ });
824
968
  this.materials.set(originalMaterial.name, material);
825
969
  asMesh.material = material;
826
970
  }
@@ -828,6 +972,13 @@ var CharacterModel = class {
828
972
  });
829
973
  }
830
974
  updateAnimation(targetAnimation) {
975
+ if (this.isPostDoubleJump) {
976
+ if (targetAnimation === 6 /* doubleJump */) {
977
+ targetAnimation = 4 /* air */;
978
+ } else {
979
+ this.isPostDoubleJump = false;
980
+ }
981
+ }
831
982
  if (this.currentAnimation !== targetAnimation) {
832
983
  this.transitionToAnimation(targetAnimation);
833
984
  }
@@ -847,7 +998,7 @@ var CharacterModel = class {
847
998
  var _a, _b;
848
999
  if (((_a = mmlCharacterDescription.base) == null ? void 0 : _a.url.length) === 0) {
849
1000
  throw new Error(
850
- "ERROR: An MML Character Description was provided but it's not a valid <m-character> string, or a valid URL"
1001
+ "ERROR: An MML Character Description was provided, but it's not a valid <m-character> string, or a valid URL"
851
1002
  );
852
1003
  }
853
1004
  let mergedCharacter = null;
@@ -855,27 +1006,30 @@ var CharacterModel = class {
855
1006
  const characterBase = ((_b = mmlCharacterDescription.base) == null ? void 0 : _b.url) || null;
856
1007
  if (characterBase) {
857
1008
  this.mmlCharacterDescription = mmlCharacterDescription;
858
- const mmlCharacter = new MMLCharacter(new ModelLoader());
1009
+ const mmlCharacter = new MMLCharacter(_CharacterModel.ModelLoader);
859
1010
  mergedCharacter = await mmlCharacter.mergeBodyParts(
860
1011
  characterBase,
861
1012
  mmlCharacterDescription.parts
862
1013
  );
863
1014
  if (mergedCharacter) {
864
- return mergedCharacter.children[0].children[0];
1015
+ return mergedCharacter;
865
1016
  }
866
1017
  }
867
1018
  }
868
1019
  }
869
1020
  async loadCharacterFromDescription() {
870
- if (this.characterDescription.meshFileUrl) {
871
- return await this.characterModelLoader.load(this.characterDescription.meshFileUrl, "model") || null;
1021
+ if (this.config.characterDescription.meshFileUrl) {
1022
+ return await this.config.characterModelLoader.load(
1023
+ this.config.characterDescription.meshFileUrl,
1024
+ "model"
1025
+ ) || null;
872
1026
  }
873
1027
  let mmlCharacterSource;
874
- if (this.characterDescription.mmlCharacterUrl) {
875
- const res = await fetch(this.characterDescription.mmlCharacterUrl);
1028
+ if (this.config.characterDescription.mmlCharacterUrl) {
1029
+ const res = await fetch(this.config.characterDescription.mmlCharacterUrl);
876
1030
  mmlCharacterSource = await res.text();
877
- } else if (this.characterDescription.mmlCharacterString) {
878
- mmlCharacterSource = this.characterDescription.mmlCharacterString;
1031
+ } else if (this.config.characterDescription.mmlCharacterString) {
1032
+ mmlCharacterSource = this.config.characterDescription.mmlCharacterString;
879
1033
  } else {
880
1034
  throw new Error(
881
1035
  "ERROR: No Character Description was provided. Specify one of meshFileUrl, mmlCharacterUrl or mmlCharacterString"
@@ -915,16 +1069,21 @@ var CharacterModel = class {
915
1069
  });
916
1070
  return animationClip;
917
1071
  }
918
- async setAnimationFromFile(animationFileUrl, animationType) {
1072
+ async setAnimationFromFile(animationFileUrl, animationType, loop = true, playbackSpeed = 1) {
919
1073
  return new Promise(async (resolve, reject) => {
920
- const animation = await this.characterModelLoader.load(animationFileUrl, "animation");
1074
+ const animation = await this.config.characterModelLoader.load(animationFileUrl, "animation");
921
1075
  const cleanAnimation = this.cleanAnimationClips(this.mesh, animation);
922
1076
  if (typeof animation !== "undefined" && cleanAnimation instanceof AnimationClip) {
923
1077
  this.animations[animationType] = this.animationMixer.clipAction(cleanAnimation);
924
1078
  this.animations[animationType].stop();
1079
+ this.animations[animationType].timeScale = playbackSpeed;
925
1080
  if (animationType === 0 /* idle */) {
926
1081
  this.animations[animationType].play();
927
1082
  }
1083
+ if (!loop) {
1084
+ this.animations[animationType].setLoop(LoopRepeat, 1);
1085
+ this.animations[animationType].clampWhenFinished = true;
1086
+ }
928
1087
  resolve();
929
1088
  } else {
930
1089
  reject(`failed to load ${animationType} from ${animationFileUrl}`);
@@ -932,20 +1091,30 @@ var CharacterModel = class {
932
1091
  });
933
1092
  }
934
1093
  transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
935
- if (!this.mesh)
1094
+ if (!this.mesh) {
936
1095
  return;
1096
+ }
937
1097
  const currentAction = this.animations[this.currentAnimation];
938
1098
  this.currentAnimation = targetAnimation;
939
1099
  const targetAction = this.animations[targetAnimation];
940
- if (!targetAction)
1100
+ if (!targetAction) {
941
1101
  return;
1102
+ }
942
1103
  if (currentAction) {
943
- currentAction.enabled = true;
944
1104
  currentAction.fadeOut(transitionDuration);
945
1105
  }
946
- if (!targetAction.isRunning())
1106
+ targetAction.reset();
1107
+ if (!targetAction.isRunning()) {
947
1108
  targetAction.play();
948
- targetAction.setLoop(LoopRepeat, Infinity);
1109
+ }
1110
+ if (targetAnimation === 6 /* doubleJump */) {
1111
+ targetAction.getMixer().addEventListener("finished", (_event) => {
1112
+ if (this.currentAnimation === 6 /* doubleJump */) {
1113
+ this.isPostDoubleJump = true;
1114
+ this.updateAnimation(6 /* doubleJump */);
1115
+ }
1116
+ });
1117
+ }
949
1118
  targetAction.enabled = true;
950
1119
  targetAction.fadeIn(transitionDuration);
951
1120
  }
@@ -956,6 +1125,8 @@ var CharacterModel = class {
956
1125
  }
957
1126
  }
958
1127
  };
1128
+ _CharacterModel.ModelLoader = new ModelLoader();
1129
+ var CharacterModel = _CharacterModel;
959
1130
 
960
1131
  // src/character/CharacterSpeakingIndicator.ts
961
1132
  import {
@@ -1031,7 +1202,7 @@ var CharacterSpeakingIndicator = class {
1031
1202
  this.scene.add(this.mesh);
1032
1203
  }
1033
1204
  setBillboarding(position, camera) {
1034
- this.mesh.position.set(position.x, position.y - 0.15, position.z);
1205
+ this.mesh.position.set(position.x, position.y, position.z);
1035
1206
  this.mesh.lookAt(camera.position);
1036
1207
  }
1037
1208
  setTime(value) {
@@ -1116,27 +1287,27 @@ function CanvasText(message, options) {
1116
1287
  const padding = options.paddingPx || 0;
1117
1288
  const font = options.font || "Arial";
1118
1289
  const fontString = (options.bold ? "bold " : "") + fontsize + "px " + font;
1119
- const canvas = document.createElement("canvas");
1120
- const ct = canvas.getContext("2d");
1290
+ const canvas2 = document.createElement("canvas");
1291
+ const ct = canvas2.getContext("2d");
1121
1292
  const textAlign = options.alignment ?? "left";
1122
1293
  if (options.dimensions) {
1123
- canvas.width = options.dimensions.width;
1124
- canvas.height = options.dimensions.height;
1125
- ct.clearRect(0, 0, canvas.width, canvas.height);
1294
+ canvas2.width = options.dimensions.width;
1295
+ canvas2.height = options.dimensions.height;
1296
+ ct.clearRect(0, 0, canvas2.width, canvas2.height);
1126
1297
  ct.font = fontString;
1127
1298
  ct.textAlign = textAlign;
1128
1299
  ct.fillStyle = `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, ${backgroundColor.a})`;
1129
1300
  ct.lineWidth = 0;
1130
- ct.fillRect(0, 0, canvas.width, canvas.height);
1301
+ ct.fillRect(0, 0, canvas2.width, canvas2.height);
1131
1302
  ct.fillStyle = `rgba(${textColor.r}, ${textColor.g}, ${textColor.b}, ${textColor.a})`;
1132
1303
  ct.font = fontString;
1133
1304
  printAtWordWrap(
1134
1305
  ct,
1135
1306
  message,
1136
- getTextAlignOffset(textAlign, canvas.width),
1307
+ getTextAlignOffset(textAlign, canvas2.width),
1137
1308
  fontsize,
1138
1309
  fontsize,
1139
- canvas.width,
1310
+ canvas2.width,
1140
1311
  padding,
1141
1312
  textAlign
1142
1313
  );
@@ -1145,28 +1316,28 @@ function CanvasText(message, options) {
1145
1316
  const metrics = ct.measureText(message);
1146
1317
  const textWidth = metrics.width;
1147
1318
  const textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
1148
- canvas.width = textWidth + padding * 2;
1149
- canvas.height = textHeight + padding;
1150
- ct.clearRect(0, 0, canvas.width, canvas.height);
1319
+ canvas2.width = textWidth + padding * 2;
1320
+ canvas2.height = textHeight + padding;
1321
+ ct.clearRect(0, 0, canvas2.width, canvas2.height);
1151
1322
  ct.font = fontString;
1152
1323
  ct.textAlign = textAlign;
1153
1324
  ct.fillStyle = `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, ${backgroundColor.a})`;
1154
1325
  ct.lineWidth = 0;
1155
- ct.fillRect(0, 0, canvas.width, canvas.height);
1326
+ ct.fillRect(0, 0, canvas2.width, canvas2.height);
1156
1327
  ct.fillStyle = `rgba(${textColor.r}, ${textColor.g}, ${textColor.b}, ${textColor.a})`;
1157
1328
  ct.font = fontString;
1158
1329
  ct.fillText(message, padding + getTextAlignOffset(textAlign, textWidth), textHeight);
1159
1330
  }
1160
- return canvas;
1331
+ return canvas2;
1161
1332
  }
1162
1333
  function THREECanvasTextTexture(text, options) {
1163
- const canvas = CanvasText(text, options);
1164
- const texture = new Texture2(canvas);
1334
+ const canvas2 = CanvasText(text, options);
1335
+ const texture = new Texture2(canvas2);
1165
1336
  texture.minFilter = LinearFilter;
1166
1337
  texture.magFilter = LinearFilter;
1167
1338
  texture.format = RGBAFormat;
1168
1339
  texture.needsUpdate = true;
1169
- return { texture, width: canvas.width, height: canvas.height };
1340
+ return { texture, width: canvas2.width, height: canvas2.height };
1170
1341
  }
1171
1342
 
1172
1343
  // src/character/CharacterTooltip.ts
@@ -1175,20 +1346,19 @@ var defaultLabelColor = new Color2(0);
1175
1346
  var defaultFontColor = new Color2(16777215);
1176
1347
  var defaultLabelAlignment = "center" /* center */;
1177
1348
  var defaultLabelFontSize = 8;
1178
- var defaultLabelPadding = 0;
1349
+ var defaultLabelPadding = 8;
1179
1350
  var defaultLabelWidth = 0.25;
1180
1351
  var defaultLabelHeight = 0.1;
1181
1352
  var defaultLabelCastShadows = true;
1182
1353
  var tooltipGeometry = new PlaneGeometry(1, 1, 1, 1);
1183
1354
  var CharacterTooltip = class extends Mesh3 {
1184
- constructor() {
1355
+ constructor(configArg) {
1185
1356
  super(tooltipGeometry);
1186
1357
  this.visibleOpacity = 0.85;
1187
1358
  this.targetOpacity = 0;
1188
1359
  this.fadingSpeed = 0.02;
1189
1360
  this.secondsToFadeOut = 10;
1190
- this.props = {
1191
- content: "",
1361
+ this.config = {
1192
1362
  alignment: defaultLabelAlignment,
1193
1363
  width: defaultLabelWidth,
1194
1364
  height: defaultLabelHeight,
@@ -1196,7 +1366,8 @@ var CharacterTooltip = class extends Mesh3 {
1196
1366
  padding: defaultLabelPadding,
1197
1367
  color: defaultLabelColor,
1198
1368
  fontColor: defaultFontColor,
1199
- castShadows: defaultLabelCastShadows
1369
+ castShadows: defaultLabelCastShadows,
1370
+ ...configArg
1200
1371
  };
1201
1372
  this.tooltipMaterial = new MeshBasicMaterial2({
1202
1373
  map: null,
@@ -1217,25 +1388,21 @@ var CharacterTooltip = class extends Mesh3 {
1217
1388
  }
1218
1389
  const { texture, width, height } = THREECanvasTextTexture(content, {
1219
1390
  bold: true,
1220
- fontSize: this.props.fontSize * fontScale,
1221
- paddingPx: this.props.padding,
1391
+ fontSize: this.config.fontSize * fontScale,
1392
+ paddingPx: this.config.padding,
1222
1393
  textColorRGB255A1: {
1223
- r: this.props.fontColor.r * 255,
1224
- g: this.props.fontColor.g * 255,
1225
- b: this.props.fontColor.b * 255,
1394
+ r: this.config.fontColor.r * 255,
1395
+ g: this.config.fontColor.g * 255,
1396
+ b: this.config.fontColor.b * 255,
1226
1397
  a: 1
1227
1398
  },
1228
1399
  backgroundColorRGB255A1: {
1229
- r: this.props.color.r * 255,
1230
- g: this.props.color.g * 255,
1231
- b: this.props.color.b * 255,
1400
+ r: this.config.color.r * 255,
1401
+ g: this.config.color.g * 255,
1402
+ b: this.config.color.b * 255,
1232
1403
  a: 1
1233
1404
  },
1234
- dimensions: {
1235
- width: this.props.width * (100 * fontScale),
1236
- height: this.props.height * (100 * fontScale)
1237
- },
1238
- alignment: this.props.alignment
1405
+ alignment: this.config.alignment
1239
1406
  });
1240
1407
  this.tooltipMaterial.map = texture;
1241
1408
  this.tooltipMaterial.map.magFilter = LinearFilter2;
@@ -1246,7 +1413,8 @@ var CharacterTooltip = class extends Mesh3 {
1246
1413
  this.position.y = 1.4;
1247
1414
  }
1248
1415
  setText(text, temporary = false) {
1249
- this.redrawText(text);
1416
+ const sanitizedText = text.replace(/(\r\n|\n|\r)/gm, "");
1417
+ this.redrawText(sanitizedText);
1250
1418
  this.visible = true;
1251
1419
  this.targetOpacity = this.visibleOpacity;
1252
1420
  if (temporary) {
@@ -1287,39 +1455,43 @@ var CharacterTooltip = class extends Mesh3 {
1287
1455
 
1288
1456
  // src/character/Character.ts
1289
1457
  var Character = class extends Group {
1290
- constructor(characterDescription, animationConfig, characterModelLoader, characterId, modelLoadedCallback, cameraManager, composer, isLocal) {
1458
+ constructor(config) {
1291
1459
  super();
1292
- this.characterDescription = characterDescription;
1293
- this.animationConfig = animationConfig;
1294
- this.characterModelLoader = characterModelLoader;
1295
- this.characterId = characterId;
1296
- this.modelLoadedCallback = modelLoadedCallback;
1297
- this.cameraManager = cameraManager;
1298
- this.composer = composer;
1299
- this.isLocal = isLocal;
1460
+ this.config = config;
1300
1461
  this.model = null;
1301
1462
  this.color = new Color3();
1302
- this.tooltip = null;
1303
1463
  this.speakingIndicator = null;
1304
1464
  this.tooltip = new CharacterTooltip();
1465
+ this.tooltip.setText(this.config.username, this.config.isLocal);
1305
1466
  this.add(this.tooltip);
1467
+ this.load().then(() => {
1468
+ this.config.modelLoadedCallback();
1469
+ });
1470
+ }
1471
+ updateCharacter(username, characterDescription) {
1472
+ this.config.username = username;
1473
+ this.config.characterDescription = characterDescription;
1306
1474
  this.load();
1475
+ this.tooltip.setText(username, this.config.isLocal);
1307
1476
  }
1308
1477
  async load() {
1309
- this.model = new CharacterModel(
1310
- this.characterDescription,
1311
- this.animationConfig,
1312
- this.characterModelLoader,
1313
- this.cameraManager,
1314
- this.characterId,
1315
- this.isLocal
1316
- );
1478
+ const previousModel = this.model;
1479
+ this.model = new CharacterModel({
1480
+ characterDescription: this.config.characterDescription,
1481
+ animationConfig: this.config.animationConfig,
1482
+ characterModelLoader: this.config.characterModelLoader,
1483
+ cameraManager: this.config.cameraManager,
1484
+ characterId: this.config.characterId,
1485
+ isLocal: this.config.isLocal
1486
+ });
1317
1487
  await this.model.init();
1488
+ if (previousModel && previousModel.mesh) {
1489
+ this.remove(previousModel.mesh);
1490
+ }
1318
1491
  this.add(this.model.mesh);
1319
1492
  if (this.speakingIndicator === null) {
1320
- this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1493
+ this.speakingIndicator = new CharacterSpeakingIndicator(this.config.composer.postPostScene);
1321
1494
  }
1322
- this.modelLoadedCallback();
1323
1495
  }
1324
1496
  updateAnimation(targetAnimation) {
1325
1497
  var _a;
@@ -1330,14 +1502,14 @@ var Character = class extends Group {
1330
1502
  if (!this.model)
1331
1503
  return;
1332
1504
  if (this.tooltip) {
1333
- this.tooltip.update(this.cameraManager.camera);
1505
+ this.tooltip.update(this.config.cameraManager.camera);
1334
1506
  }
1335
1507
  if (this.speakingIndicator) {
1336
1508
  this.speakingIndicator.setTime(time);
1337
1509
  if (this.model.mesh && this.model.headBone) {
1338
1510
  this.speakingIndicator.setBillboarding(
1339
1511
  (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector34()),
1340
- this.cameraManager.camera
1512
+ this.config.cameraManager.camera
1341
1513
  );
1342
1514
  }
1343
1515
  }
@@ -1354,33 +1526,216 @@ import { Euler as Euler2, Group as Group2, Quaternion as Quaternion5, Vector3 as
1354
1526
 
1355
1527
  // src/character/LocalController.ts
1356
1528
  import { Euler, Line3, Matrix4, Quaternion as Quaternion2, Ray, Raycaster as Raycaster2, Vector3 as Vector35 } from "three";
1529
+
1530
+ // src/tweakpane/blades/characterControlsFolder.ts
1531
+ var characterControllerValues = {
1532
+ gravity: 28,
1533
+ jumpForce: 18,
1534
+ doubleJumpForce: 17.7,
1535
+ coyoteJump: 120,
1536
+ airResistance: 0.5,
1537
+ groundResistance: 0,
1538
+ airControlModifier: 0.05,
1539
+ groundWalkControl: 0.75,
1540
+ groundRunControl: 1,
1541
+ baseControlMultiplier: 200,
1542
+ minimumSurfaceAngle: 0.905
1543
+ };
1544
+ var characterControllerOptions = {
1545
+ gravity: { min: 1, max: 100, step: 0.05 },
1546
+ jumpForce: { min: 1, max: 50, step: 0.05 },
1547
+ doubleJumpForce: { min: 1, max: 50, step: 0.05 },
1548
+ coyoteJump: { min: 60, max: 200, step: 1 },
1549
+ airResistance: { min: 0.01, max: 0.9, step: 0.01 },
1550
+ groundResistance: { min: -100, max: 0, step: 1 },
1551
+ airControlModifier: { min: 1e-3, max: 0.15, step: 0.01 },
1552
+ groundWalkControl: { min: 0.1, max: 1.5, step: 0.01 },
1553
+ groundRunControl: { min: 0.5, max: 2, step: 0.01 },
1554
+ baseControlMultiplier: { min: 150, max: 300, step: 1 },
1555
+ minimumSurfaceAngle: { min: 0.254, max: 1, step: 1e-3 }
1556
+ };
1557
+ var CharacterControlsFolder = class {
1558
+ constructor(parentFolder, expand = false) {
1559
+ this.characterData = {
1560
+ position: "(0, 0, 0)",
1561
+ onGround: "false",
1562
+ canJump: "false",
1563
+ canDoubleJump: "false",
1564
+ jumpCount: "0",
1565
+ coyoteTime: "false",
1566
+ coyoteJumped: "false"
1567
+ };
1568
+ this.folder = parentFolder.addFolder({ title: "character", expanded: expand });
1569
+ this.folder.addBinding(this.characterData, "position", { readonly: true });
1570
+ this.folder.addBinding(this.characterData, "onGround", { readonly: true });
1571
+ this.folder.addBinding(this.characterData, "canJump", { readonly: true });
1572
+ this.folder.addBinding(this.characterData, "canDoubleJump", { readonly: true });
1573
+ this.folder.addBinding(this.characterData, "jumpCount", { readonly: true });
1574
+ this.folder.addBinding(this.characterData, "coyoteTime", { readonly: true });
1575
+ this.folder.addBinding(this.characterData, "coyoteJumped", { readonly: true });
1576
+ this.folder.addBinding(
1577
+ characterControllerValues,
1578
+ "gravity",
1579
+ characterControllerOptions.gravity
1580
+ );
1581
+ this.folder.addBinding(
1582
+ characterControllerValues,
1583
+ "jumpForce",
1584
+ characterControllerOptions.jumpForce
1585
+ );
1586
+ this.folder.addBinding(
1587
+ characterControllerValues,
1588
+ "doubleJumpForce",
1589
+ characterControllerOptions.doubleJumpForce
1590
+ );
1591
+ this.folder.addBinding(
1592
+ characterControllerValues,
1593
+ "coyoteJump",
1594
+ characterControllerOptions.coyoteJump
1595
+ );
1596
+ this.folder.addBinding(
1597
+ characterControllerValues,
1598
+ "airResistance",
1599
+ characterControllerOptions.airResistance
1600
+ );
1601
+ this.folder.addBinding(
1602
+ characterControllerValues,
1603
+ "groundResistance",
1604
+ characterControllerOptions.groundResistance
1605
+ );
1606
+ this.folder.addBinding(
1607
+ characterControllerValues,
1608
+ "airControlModifier",
1609
+ characterControllerOptions.airControlModifier
1610
+ );
1611
+ this.folder.addBinding(
1612
+ characterControllerValues,
1613
+ "groundWalkControl",
1614
+ characterControllerOptions.groundWalkControl
1615
+ );
1616
+ this.folder.addBinding(
1617
+ characterControllerValues,
1618
+ "groundRunControl",
1619
+ characterControllerOptions.groundRunControl
1620
+ );
1621
+ this.folder.addBinding(
1622
+ characterControllerValues,
1623
+ "baseControlMultiplier",
1624
+ characterControllerOptions.baseControlMultiplier
1625
+ );
1626
+ this.folder.addBinding(
1627
+ characterControllerValues,
1628
+ "minimumSurfaceAngle",
1629
+ characterControllerOptions.minimumSurfaceAngle
1630
+ );
1631
+ }
1632
+ setupChangeEvent(localController) {
1633
+ this.folder.on("change", (e) => {
1634
+ const target = e.target.key;
1635
+ if (!target)
1636
+ return;
1637
+ switch (target) {
1638
+ case "gravity": {
1639
+ const value = e.value;
1640
+ localController.gravity = value * -1;
1641
+ break;
1642
+ }
1643
+ case "jumpForce": {
1644
+ const value = e.value;
1645
+ localController.jumpForce = value;
1646
+ break;
1647
+ }
1648
+ case "doubleJumpForce": {
1649
+ const value = e.value;
1650
+ localController.doubleJumpForce = value;
1651
+ break;
1652
+ }
1653
+ case "coyoteJump": {
1654
+ const value = e.value;
1655
+ localController.coyoteTimeThreshold = value;
1656
+ break;
1657
+ }
1658
+ case "airResistance": {
1659
+ const value = e.value;
1660
+ localController.airResistance = value;
1661
+ break;
1662
+ }
1663
+ case "groundResistance": {
1664
+ const value = e.value;
1665
+ localController.groundResistance = 0.99999999 + value * 1e-6;
1666
+ break;
1667
+ }
1668
+ case "airControlModifier": {
1669
+ const value = e.value;
1670
+ localController.airControlModifier = value;
1671
+ break;
1672
+ }
1673
+ case "groundWalkControl": {
1674
+ const value = e.value;
1675
+ localController.groundWalkControl = value;
1676
+ break;
1677
+ }
1678
+ case "groundRunControl": {
1679
+ const value = e.value;
1680
+ localController.groundRunControl = value;
1681
+ break;
1682
+ }
1683
+ case "baseControlMultiplier": {
1684
+ const value = e.value;
1685
+ localController.baseControl = value;
1686
+ break;
1687
+ }
1688
+ case "minimumSurfaceAngle": {
1689
+ const value = e.value;
1690
+ localController.minimumSurfaceAngle = value;
1691
+ break;
1692
+ }
1693
+ default:
1694
+ break;
1695
+ }
1696
+ });
1697
+ }
1698
+ update(localController) {
1699
+ const { x, y, z } = localController.latestPosition;
1700
+ this.characterData.position = `(${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`;
1701
+ this.characterData.onGround = `${localController.characterOnGround}`;
1702
+ this.characterData.canJump = `${localController.canJump || localController.coyoteTime ? "true" : "false"}`;
1703
+ this.characterData.canDoubleJump = `${localController.canDoubleJump}`;
1704
+ this.characterData.jumpCount = `${localController.jumpCounter}`;
1705
+ this.characterData.coyoteTime = `${localController.coyoteTime}`;
1706
+ this.characterData.coyoteJumped = `${localController.coyoteJumped}`;
1707
+ }
1708
+ };
1709
+
1710
+ // src/character/LocalController.ts
1357
1711
  var downVector = new Vector35(0, -1, 0);
1358
- var airResistance = 0.5;
1359
- var groundResistance = 0.99999999;
1360
- var airControlModifier = 0.05;
1361
- var groundWalkControl = 0.75;
1362
- var groundRunControl = 1;
1363
- var baseControl = 200;
1364
- var collisionDetectionSteps = 15;
1365
- var minimumSurfaceAngle = 0.9;
1366
1712
  var LocalController = class {
1367
- constructor(character, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1368
- this.character = character;
1369
- this.id = id;
1370
- this.collisionsManager = collisionsManager;
1371
- this.keyInputManager = keyInputManager;
1372
- this.cameraManager = cameraManager;
1373
- this.timeManager = timeManager;
1713
+ constructor(config) {
1714
+ this.config = config;
1374
1715
  this.capsuleInfo = {
1375
1716
  radius: 0.4,
1376
1717
  segment: new Line3(new Vector35(), new Vector35(0, 1.05, 0))
1377
1718
  };
1378
- this.gravity = -42;
1379
- this.jumpForce = 20;
1380
- this.coyoteTimeThreshold = 70;
1381
- this.coyoteTime = false;
1719
+ this.gravity = -characterControllerValues.gravity;
1720
+ this.jumpForce = characterControllerValues.jumpForce;
1721
+ this.doubleJumpForce = characterControllerValues.doubleJumpForce;
1722
+ this.coyoteTimeThreshold = characterControllerValues.coyoteJump;
1382
1723
  this.canJump = true;
1724
+ this.canDoubleJump = true;
1725
+ this.coyoteJumped = false;
1726
+ this.doubleJumpUsed = false;
1727
+ this.jumpCounter = 0;
1728
+ this.airResistance = characterControllerValues.airResistance;
1729
+ this.groundResistance = 0.99999999 + characterControllerValues.groundResistance * 1e-7;
1730
+ this.airControlModifier = characterControllerValues.airControlModifier;
1731
+ this.groundWalkControl = characterControllerValues.groundWalkControl;
1732
+ this.groundRunControl = characterControllerValues.groundRunControl;
1733
+ this.baseControl = characterControllerValues.baseControlMultiplier;
1734
+ this.minimumSurfaceAngle = characterControllerValues.minimumSurfaceAngle;
1735
+ this.latestPosition = new Vector35();
1383
1736
  this.characterOnGround = false;
1737
+ this.coyoteTime = false;
1738
+ this.collisionDetectionSteps = 15;
1384
1739
  this.characterWasOnGround = false;
1385
1740
  this.characterAirborneSince = 0;
1386
1741
  this.currentHeight = 0;
@@ -1407,57 +1762,67 @@ var LocalController = class {
1407
1762
  this.surfaceTempVector5 = new Vector35();
1408
1763
  this.surfaceTempRay = new Ray();
1409
1764
  this.lastFrameSurfaceState = null;
1765
+ this.jumpPressed = false;
1766
+ // Tracks if the jump button is pressed
1767
+ this.jumpReleased = true;
1410
1768
  this.networkState = {
1411
- id: this.id,
1769
+ id: this.config.id,
1412
1770
  position: { x: 0, y: 0, z: 0 },
1413
1771
  rotation: { quaternionY: 0, quaternionW: 1 },
1414
1772
  state: 0 /* idle */
1415
1773
  };
1416
1774
  }
1417
1775
  updateControllerState() {
1418
- this.forward = this.keyInputManager.forward;
1419
- this.backward = this.keyInputManager.backward;
1420
- this.left = this.keyInputManager.left;
1421
- this.right = this.keyInputManager.right;
1422
- this.run = this.keyInputManager.run;
1423
- this.jump = this.keyInputManager.jump;
1424
- this.anyDirection = this.keyInputManager.anyDirection;
1425
- this.conflictingDirections = this.keyInputManager.conflictingDirection;
1776
+ var _a, _b, _c, _d, _e;
1777
+ this.forward = this.config.keyInputManager.forward || ((_a = this.config.virtualJoystick) == null ? void 0 : _a.up) || false;
1778
+ this.backward = this.config.keyInputManager.backward || ((_b = this.config.virtualJoystick) == null ? void 0 : _b.down) || false;
1779
+ this.left = this.config.keyInputManager.left || ((_c = this.config.virtualJoystick) == null ? void 0 : _c.left) || false;
1780
+ this.right = this.config.keyInputManager.right || ((_d = this.config.virtualJoystick) == null ? void 0 : _d.right) || false;
1781
+ this.run = this.config.keyInputManager.run;
1782
+ this.jump = this.config.keyInputManager.jump;
1783
+ this.anyDirection = this.config.keyInputManager.anyDirection || ((_e = this.config.virtualJoystick) == null ? void 0 : _e.hasDirection) || false;
1784
+ this.conflictingDirections = this.config.keyInputManager.conflictingDirection;
1785
+ if (!this.jump) {
1786
+ this.jumpReleased = true;
1787
+ }
1426
1788
  }
1427
1789
  update() {
1428
1790
  this.updateControllerState();
1429
- this.rayCaster.set(this.character.position, this.vectorDown);
1430
- const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
1791
+ this.rayCaster.set(this.config.character.position, this.vectorDown);
1792
+ const firstRaycastHit = this.config.collisionsManager.raycastFirst(this.rayCaster.ray);
1431
1793
  if (firstRaycastHit !== null) {
1432
1794
  this.currentHeight = firstRaycastHit[0];
1433
1795
  this.currentSurfaceAngle.copy(firstRaycastHit[1]);
1434
1796
  }
1435
1797
  if (this.anyDirection || !this.characterOnGround) {
1436
1798
  const targetAnimation = this.getTargetAnimation();
1437
- this.character.updateAnimation(targetAnimation);
1799
+ this.config.character.updateAnimation(targetAnimation);
1438
1800
  } else {
1439
- this.character.updateAnimation(0 /* idle */);
1801
+ this.config.character.updateAnimation(0 /* idle */);
1440
1802
  }
1441
1803
  if (this.anyDirection) {
1442
1804
  this.updateRotation();
1443
1805
  }
1444
- for (let i = 0; i < collisionDetectionSteps; i++) {
1806
+ for (let i = 0; i < this.collisionDetectionSteps; i++) {
1445
1807
  this.updatePosition(
1446
- this.timeManager.deltaTime,
1447
- this.timeManager.deltaTime / collisionDetectionSteps,
1808
+ this.config.timeManager.deltaTime,
1809
+ this.config.timeManager.deltaTime / this.collisionDetectionSteps,
1448
1810
  i
1449
1811
  );
1450
1812
  }
1451
- if (this.character.position.y < 0) {
1813
+ if (this.config.character.position.y < -100) {
1452
1814
  this.resetPosition();
1453
1815
  }
1454
1816
  this.updateNetworkState();
1455
1817
  }
1456
1818
  getTargetAnimation() {
1457
- if (!this.character)
1819
+ if (!this.config.character)
1458
1820
  return 0 /* idle */;
1459
1821
  const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
1460
1822
  if (this.currentHeight > jumpHeight && !this.characterOnGround) {
1823
+ if (this.doubleJumpUsed) {
1824
+ return 6 /* doubleJump */;
1825
+ }
1461
1826
  return 4 /* air */;
1462
1827
  }
1463
1828
  if (this.conflictingDirections) {
@@ -1487,22 +1852,22 @@ var LocalController = class {
1487
1852
  }
1488
1853
  }
1489
1854
  updateAzimuthalAngle() {
1490
- const camToModelDistance = this.cameraManager.camera.position.distanceTo(
1491
- this.character.position
1855
+ const camToModelDistance = this.config.cameraManager.camera.position.distanceTo(
1856
+ this.config.character.position
1492
1857
  );
1493
1858
  const isCameraFirstPerson = camToModelDistance < 2;
1494
1859
  if (isCameraFirstPerson) {
1495
- const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.cameraManager.camera.quaternion);
1860
+ const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.config.cameraManager.camera.quaternion);
1496
1861
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1497
1862
  } else {
1498
1863
  this.azimuthalAngle = Math.atan2(
1499
- this.cameraManager.camera.position.x - this.character.position.x,
1500
- this.cameraManager.camera.position.z - this.character.position.z
1864
+ this.config.cameraManager.camera.position.x - this.config.character.position.x,
1865
+ this.config.cameraManager.camera.position.z - this.config.character.position.z
1501
1866
  );
1502
1867
  }
1503
1868
  }
1504
1869
  computeAngularDifference(rotationQuaternion) {
1505
- return 2 * Math.acos(Math.abs(this.character.quaternion.dot(rotationQuaternion)));
1870
+ return 2 * Math.acos(Math.abs(this.config.character.quaternion.dot(rotationQuaternion)));
1506
1871
  }
1507
1872
  updateRotation() {
1508
1873
  this.updateRotationOffset();
@@ -1514,34 +1879,62 @@ var LocalController = class {
1514
1879
  const angularDifference = this.computeAngularDifference(rotationQuaternion);
1515
1880
  const desiredTime = 0.07;
1516
1881
  const angularSpeed = angularDifference / desiredTime;
1517
- const frameRotation = angularSpeed * this.timeManager.deltaTime;
1518
- this.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1882
+ const frameRotation = angularSpeed * this.config.timeManager.deltaTime;
1883
+ this.config.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1519
1884
  }
1520
- applyControls(deltaTime) {
1521
- const resistance = this.characterOnGround ? groundResistance : airResistance;
1522
- const speedFactor = Math.pow(1 - resistance, deltaTime);
1523
- this.characterVelocity.multiplyScalar(speedFactor);
1524
- const acceleration = this.tempVector.set(0, 0, 0);
1885
+ processJump(currentAcceleration, deltaTime) {
1525
1886
  if (this.characterOnGround) {
1887
+ this.coyoteJumped = false;
1888
+ this.canDoubleJump = false;
1889
+ this.doubleJumpUsed = false;
1890
+ this.jumpCounter = 0;
1526
1891
  if (!this.jump) {
1892
+ this.canDoubleJump = !this.doubleJumpUsed && this.jumpReleased && this.jumpCounter === 1;
1527
1893
  this.canJump = true;
1894
+ this.jumpReleased = true;
1528
1895
  }
1529
- if (this.jump && this.canJump) {
1530
- acceleration.y += this.jumpForce / deltaTime;
1896
+ if (this.jump && this.canJump && this.jumpReleased) {
1897
+ currentAcceleration.y += this.jumpForce / deltaTime;
1531
1898
  this.canJump = false;
1899
+ this.jumpReleased = false;
1900
+ this.jumpCounter++;
1532
1901
  } else {
1533
- if (this.currentSurfaceAngle.y < minimumSurfaceAngle) {
1534
- acceleration.y += this.gravity;
1902
+ if (this.currentSurfaceAngle.y < this.minimumSurfaceAngle) {
1903
+ currentAcceleration.y += this.gravity;
1535
1904
  }
1536
1905
  }
1537
- } else if (this.jump && this.coyoteTime) {
1538
- acceleration.y += this.jumpForce / deltaTime;
1539
- this.canJump = false;
1540
1906
  } else {
1541
- acceleration.y += this.gravity;
1542
- this.canJump = false;
1907
+ if (this.jump && !this.coyoteJumped && this.coyoteTime) {
1908
+ this.coyoteJumped = true;
1909
+ currentAcceleration.y += this.jumpForce / deltaTime;
1910
+ this.canJump = false;
1911
+ this.jumpReleased = false;
1912
+ this.jumpCounter++;
1913
+ } else if (this.jump && this.canDoubleJump) {
1914
+ currentAcceleration.y += this.doubleJumpForce / deltaTime;
1915
+ this.doubleJumpUsed = true;
1916
+ this.jumpReleased = false;
1917
+ this.jumpCounter++;
1918
+ } else {
1919
+ currentAcceleration.y += this.gravity;
1920
+ this.canJump = false;
1921
+ }
1922
+ }
1923
+ if (!this.jump) {
1924
+ this.jumpReleased = true;
1925
+ if (!this.characterOnGround) {
1926
+ currentAcceleration.y += this.gravity;
1927
+ }
1543
1928
  }
1544
- const control = (this.characterOnGround ? this.run ? groundRunControl : groundWalkControl : airControlModifier) * baseControl;
1929
+ }
1930
+ applyControls(deltaTime) {
1931
+ const resistance = this.characterOnGround ? this.groundResistance : this.airResistance;
1932
+ const speedFactor = Math.pow(1 - resistance, deltaTime);
1933
+ this.characterVelocity.multiplyScalar(speedFactor);
1934
+ const acceleration = this.tempVector.set(0, 0, 0);
1935
+ this.canDoubleJump = !this.doubleJumpUsed && this.jumpReleased && this.jumpCounter === 1;
1936
+ this.processJump(acceleration, deltaTime);
1937
+ const control = (this.characterOnGround ? this.run ? this.groundRunControl : this.groundWalkControl : this.airControlModifier) * this.baseControl;
1545
1938
  const controlAcceleration = this.tempVector2.set(0, 0, 0);
1546
1939
  if (!this.conflictingDirections) {
1547
1940
  if (this.forward) {
@@ -1567,37 +1960,45 @@ var LocalController = class {
1567
1960
  }
1568
1961
  acceleration.add(controlAcceleration);
1569
1962
  this.characterVelocity.addScaledVector(acceleration, deltaTime);
1570
- this.character.position.addScaledVector(this.characterVelocity, deltaTime);
1963
+ this.config.character.position.addScaledVector(this.characterVelocity, deltaTime);
1571
1964
  }
1572
1965
  updatePosition(deltaTime, stepDeltaTime, iter) {
1573
1966
  this.applyControls(stepDeltaTime);
1574
1967
  if (iter === 0) {
1575
- const lastMovement = this.getMovementFromSurfaces(this.character.position, deltaTime);
1968
+ const lastMovement = this.getMovementFromSurfaces(this.config.character.position, deltaTime);
1576
1969
  if (lastMovement) {
1577
- this.character.position.add(lastMovement.position);
1578
- const asQuaternion = this.tempQuaternion.setFromEuler(this.character.rotation);
1970
+ this.config.character.position.add(lastMovement.position);
1971
+ const asQuaternion = this.tempQuaternion.setFromEuler(this.config.character.rotation);
1579
1972
  const lastMovementEuler = this.tempEuler.setFromQuaternion(lastMovement.rotation);
1580
1973
  lastMovementEuler.x = 0;
1581
1974
  lastMovementEuler.z = 0;
1582
1975
  lastMovement.rotation.setFromEuler(lastMovementEuler);
1583
1976
  asQuaternion.multiply(lastMovement.rotation);
1584
- this.character.rotation.setFromQuaternion(asQuaternion);
1977
+ this.config.character.rotation.setFromQuaternion(asQuaternion);
1585
1978
  }
1586
1979
  }
1587
- this.character.updateMatrixWorld();
1980
+ this.config.character.updateMatrixWorld();
1588
1981
  const avatarSegment = this.tempSegment;
1589
1982
  avatarSegment.copy(this.capsuleInfo.segment);
1590
- avatarSegment.start.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1591
- avatarSegment.end.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1983
+ avatarSegment.start.applyMatrix4(this.config.character.matrixWorld).applyMatrix4(this.tempMatrix);
1984
+ avatarSegment.end.applyMatrix4(this.config.character.matrixWorld).applyMatrix4(this.tempMatrix);
1592
1985
  const positionBeforeCollisions = this.tempVector.copy(avatarSegment.start);
1593
- this.collisionsManager.applyColliders(avatarSegment, this.capsuleInfo.radius);
1594
- this.character.position.copy(avatarSegment.start);
1986
+ this.config.collisionsManager.applyColliders(avatarSegment, this.capsuleInfo.radius);
1987
+ this.config.character.position.copy(avatarSegment.start);
1595
1988
  const deltaCollisionPosition = avatarSegment.start.sub(positionBeforeCollisions);
1596
1989
  this.characterOnGround = deltaCollisionPosition.y > 0;
1990
+ if (this.characterOnGround) {
1991
+ this.doubleJumpUsed = false;
1992
+ this.jumpCounter = 0;
1993
+ }
1597
1994
  if (this.characterWasOnGround && !this.characterOnGround) {
1598
1995
  this.characterAirborneSince = Date.now();
1599
1996
  }
1997
+ if (!this.jump) {
1998
+ this.jumpReleased = true;
1999
+ }
1600
2000
  this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
2001
+ this.latestPosition = this.config.character.position.clone();
1601
2002
  this.characterWasOnGround = this.characterOnGround;
1602
2003
  }
1603
2004
  getMovementFromSurfaces(userPosition, deltaTime) {
@@ -1636,7 +2037,7 @@ var LocalController = class {
1636
2037
  }
1637
2038
  newPosition.setY(newPosition.y + 0.05);
1638
2039
  const ray = this.surfaceTempRay.set(newPosition, downVector);
1639
- const hit = this.collisionsManager.raycastFirst(ray);
2040
+ const hit = this.config.collisionsManager.raycastFirst(ray);
1640
2041
  if (hit && hit[0] < 0.8) {
1641
2042
  const currentCollisionMeshState = hit[2];
1642
2043
  this.lastFrameSurfaceState = [
@@ -1655,53 +2056,75 @@ var LocalController = class {
1655
2056
  return lastMovement;
1656
2057
  }
1657
2058
  updateNetworkState() {
1658
- const characterQuaternion = this.character.getWorldQuaternion(this.tempQuaternion);
2059
+ const characterQuaternion = this.config.character.getWorldQuaternion(this.tempQuaternion);
2060
+ const cameraQuaternion = new Quaternion2();
2061
+ this.config.cameraManager.camera.getWorldQuaternion(cameraQuaternion);
1659
2062
  this.networkState = {
1660
- id: this.id,
2063
+ id: this.config.id,
1661
2064
  position: {
1662
- x: this.character.position.x,
1663
- y: this.character.position.y,
1664
- z: this.character.position.z
2065
+ x: this.config.character.position.x,
2066
+ y: this.config.character.position.y,
2067
+ z: this.config.character.position.z
1665
2068
  },
1666
2069
  rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1667
- state: this.character.getCurrentAnimation()
2070
+ camPosition: {
2071
+ x: this.config.cameraManager.camera.position.x,
2072
+ y: this.config.cameraManager.camera.position.y,
2073
+ z: this.config.cameraManager.camera.position.z
2074
+ },
2075
+ camQuaternion: {
2076
+ y: cameraQuaternion.y,
2077
+ w: cameraQuaternion.w
2078
+ },
2079
+ state: this.config.character.getCurrentAnimation()
1668
2080
  };
1669
2081
  }
1670
2082
  resetPosition() {
1671
2083
  this.characterVelocity.y = 0;
1672
- this.character.position.y = 3;
2084
+ this.config.character.position.y = 3;
1673
2085
  this.characterOnGround = false;
2086
+ this.doubleJumpUsed = false;
2087
+ this.jumpReleased = true;
2088
+ this.jumpCounter = 0;
1674
2089
  }
1675
2090
  };
1676
2091
 
1677
2092
  // src/character/RemoteController.ts
1678
2093
  import { Quaternion as Quaternion3, Vector3 as Vector36 } from "three";
2094
+ var tempQuaternion = new Quaternion3();
1679
2095
  var RemoteController = class {
1680
- constructor(character, id) {
1681
- this.character = character;
1682
- this.id = id;
2096
+ constructor(config) {
2097
+ this.config = config;
1683
2098
  this.currentAnimation = 0 /* idle */;
1684
2099
  this.networkState = {
1685
- id: this.id,
1686
- position: { x: 0, y: 0, z: 0 },
1687
- rotation: { quaternionY: 0, quaternionW: 1 },
2100
+ id: this.config.id,
2101
+ position: {
2102
+ x: this.config.character.position.x,
2103
+ y: this.config.character.position.y,
2104
+ z: this.config.character.position.z
2105
+ },
2106
+ rotation: {
2107
+ quaternionY: tempQuaternion.setFromEuler(this.config.character.rotation).y,
2108
+ quaternionW: 1
2109
+ },
1688
2110
  state: this.currentAnimation
1689
2111
  };
1690
2112
  }
1691
2113
  update(clientUpdate, time, deltaTime) {
1692
- if (!this.character)
2114
+ if (!this.config.character) {
1693
2115
  return;
2116
+ }
1694
2117
  this.updateFromNetwork(clientUpdate);
1695
- this.character.update(time, deltaTime);
2118
+ this.config.character.update(time, deltaTime);
1696
2119
  }
1697
2120
  updateFromNetwork(clientUpdate) {
1698
2121
  const { position, rotation, state } = clientUpdate;
1699
- this.character.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
2122
+ this.config.character.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1700
2123
  const rotationQuaternion = new Quaternion3(0, rotation.quaternionY, 0, rotation.quaternionW);
1701
- this.character.quaternion.slerp(rotationQuaternion, 0.6);
2124
+ this.config.character.quaternion.slerp(rotationQuaternion, 0.6);
1702
2125
  if (state !== this.currentAnimation) {
1703
2126
  this.currentAnimation = state;
1704
- this.character.updateAnimation(state);
2127
+ this.config.character.updateAnimation(state);
1705
2128
  }
1706
2129
  }
1707
2130
  };
@@ -1732,20 +2155,10 @@ function decodeCharacterAndCamera(hash) {
1732
2155
 
1733
2156
  // src/character/CharacterManager.ts
1734
2157
  var CharacterManager = class {
1735
- constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, keyInputManager, clientStates, sendUpdate, animationConfig, characterDescription) {
1736
- this.composer = composer;
1737
- this.characterModelLoader = characterModelLoader;
1738
- this.collisionsManager = collisionsManager;
1739
- this.cameraManager = cameraManager;
1740
- this.timeManager = timeManager;
1741
- this.keyInputManager = keyInputManager;
1742
- this.clientStates = clientStates;
1743
- this.sendUpdate = sendUpdate;
1744
- this.animationConfig = animationConfig;
1745
- this.characterDescription = characterDescription;
1746
- this.updateLocationHash = true;
2158
+ constructor(config) {
2159
+ this.config = config;
1747
2160
  this.headTargetOffset = new Vector38(0, 1.3, 0);
1748
- this.id = 0;
2161
+ this.localClientId = 0;
1749
2162
  this.remoteCharacters = /* @__PURE__ */ new Map();
1750
2163
  this.remoteCharacterControllers = /* @__PURE__ */ new Map();
1751
2164
  this.localCharacterSpawned = false;
@@ -1753,21 +2166,21 @@ var CharacterManager = class {
1753
2166
  this.speakingCharacters = /* @__PURE__ */ new Map();
1754
2167
  this.group = new Group2();
1755
2168
  }
1756
- spawnLocalCharacter(characterDescription, id, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1757
- var _a;
1758
- const character = new Character(
2169
+ spawnLocalCharacter(id, username, characterDescription, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
2170
+ const character = new Character({
2171
+ username,
1759
2172
  characterDescription,
1760
- this.animationConfig,
1761
- this.characterModelLoader,
1762
- id,
1763
- () => {
2173
+ animationConfig: this.config.animationConfig,
2174
+ characterModelLoader: this.config.characterModelLoader,
2175
+ characterId: id,
2176
+ modelLoadedCallback: () => {
1764
2177
  },
1765
- this.cameraManager,
1766
- this.composer,
1767
- true
1768
- );
2178
+ cameraManager: this.config.cameraManager,
2179
+ composer: this.config.composer,
2180
+ isLocal: true
2181
+ });
1769
2182
  const quaternion = new Quaternion5().setFromEuler(character.rotation);
1770
- this.sendUpdate({
2183
+ this.config.sendUpdate({
1771
2184
  id,
1772
2185
  position: {
1773
2186
  x: spawnPosition.x,
@@ -1777,41 +2190,43 @@ var CharacterManager = class {
1777
2190
  rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1778
2191
  state: 0 /* idle */
1779
2192
  });
1780
- this.id = id;
2193
+ this.localClientId = id;
1781
2194
  this.localCharacter = character;
1782
- this.localController = new LocalController(
1783
- this.localCharacter,
1784
- this.id,
1785
- this.collisionsManager,
1786
- this.keyInputManager,
1787
- this.cameraManager,
1788
- this.timeManager
1789
- );
2195
+ this.localController = new LocalController({
2196
+ character: this.localCharacter,
2197
+ id: this.localClientId,
2198
+ collisionsManager: this.config.collisionsManager,
2199
+ keyInputManager: this.config.keyInputManager,
2200
+ virtualJoystick: this.config.virtualJoystick,
2201
+ cameraManager: this.config.cameraManager,
2202
+ timeManager: this.config.timeManager
2203
+ });
1790
2204
  this.localCharacter.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1791
2205
  this.localCharacter.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1792
- (_a = character.tooltip) == null ? void 0 : _a.setText(`${id}`, true);
1793
2206
  this.group.add(character);
1794
2207
  this.localCharacterSpawned = true;
1795
2208
  }
1796
- spawnRemoteCharacter(characterDescription, id, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1797
- var _a;
1798
- const character = new Character(
2209
+ setupTweakPane(tweakPane) {
2210
+ tweakPane.setupCharacterController(this.localController);
2211
+ }
2212
+ spawnRemoteCharacter(id, username, characterDescription, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
2213
+ const character = new Character({
2214
+ username,
1799
2215
  characterDescription,
1800
- this.animationConfig,
1801
- this.characterModelLoader,
1802
- id,
1803
- () => {
2216
+ animationConfig: this.config.animationConfig,
2217
+ characterModelLoader: this.config.characterModelLoader,
2218
+ characterId: id,
2219
+ modelLoadedCallback: () => {
1804
2220
  },
1805
- this.cameraManager,
1806
- this.composer,
1807
- false
1808
- );
2221
+ cameraManager: this.config.cameraManager,
2222
+ composer: this.config.composer,
2223
+ isLocal: false
2224
+ });
1809
2225
  this.remoteCharacters.set(id, character);
1810
- const remoteController = new RemoteController(character, id);
1811
- remoteController.character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1812
- remoteController.character.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
2226
+ character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
2227
+ character.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
2228
+ const remoteController = new RemoteController({ character, id });
1813
2229
  this.remoteCharacterControllers.set(id, remoteController);
1814
- (_a = character.tooltip) == null ? void 0 : _a.setText(`${id}`);
1815
2230
  this.group.add(character);
1816
2231
  }
1817
2232
  getLocalCharacterPositionAndRotation() {
@@ -1840,83 +2255,82 @@ var CharacterManager = class {
1840
2255
  setSpeakingCharacter(id, value) {
1841
2256
  this.speakingCharacters.set(id, value);
1842
2257
  }
2258
+ respawnIfPresent(id) {
2259
+ const characterInfo = this.config.characterResolve(id);
2260
+ if (this.localCharacter && this.localClientId == id) {
2261
+ this.localCharacter.updateCharacter(
2262
+ characterInfo.username,
2263
+ characterInfo.characterDescription
2264
+ );
2265
+ }
2266
+ const remoteCharacter = this.remoteCharacters.get(id);
2267
+ if (remoteCharacter) {
2268
+ remoteCharacter.updateCharacter(characterInfo.username, characterInfo.characterDescription);
2269
+ }
2270
+ }
1843
2271
  update() {
1844
2272
  var _a, _b, _c;
1845
2273
  if (this.localCharacter) {
1846
- this.localCharacter.update(this.timeManager.time, this.timeManager.deltaTime);
1847
- if (this.speakingCharacters.has(this.id)) {
1848
- (_a = this.localCharacter.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
2274
+ this.localCharacter.update(this.config.timeManager.time, this.config.timeManager.deltaTime);
2275
+ if (this.speakingCharacters.has(this.localClientId)) {
2276
+ (_a = this.localCharacter.speakingIndicator) == null ? void 0 : _a.setSpeaking(
2277
+ this.speakingCharacters.get(this.localClientId)
2278
+ );
1849
2279
  }
1850
2280
  this.localController.update();
1851
- if (this.timeManager.frame % 2 === 0) {
1852
- this.sendUpdate(this.localController.networkState);
2281
+ if (this.config.timeManager.frame % 2 === 0) {
2282
+ this.config.sendUpdate(this.localController.networkState);
1853
2283
  }
1854
2284
  const targetOffset = new Vector38();
1855
2285
  targetOffset.add(this.headTargetOffset).applyQuaternion(this.localCharacter.quaternion).add(this.localCharacter.position);
1856
- this.cameraManager.setTarget(targetOffset);
1857
- for (const [id, update] of this.clientStates) {
2286
+ this.config.cameraManager.setTarget(targetOffset);
2287
+ for (const [id, update] of this.config.remoteUserStates) {
1858
2288
  if (this.remoteCharacters.has(id) && this.speakingCharacters.has(id)) {
1859
2289
  const character = this.remoteCharacters.get(id);
1860
2290
  (_b = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _b.setSpeaking(this.speakingCharacters.get(id));
1861
2291
  }
1862
2292
  const { position } = update;
1863
2293
  if (!this.remoteCharacters.has(id) && this.localCharacterSpawned === true) {
2294
+ const characterInfo = this.config.characterResolve(id);
1864
2295
  this.spawnRemoteCharacter(
1865
- this.characterDescription,
1866
2296
  id,
2297
+ characterInfo.username,
2298
+ characterInfo.characterDescription,
1867
2299
  new Vector38(position.x, position.y, position.z)
1868
2300
  );
1869
2301
  }
1870
2302
  const characterController = this.remoteCharacterControllers.get(id);
1871
2303
  if (characterController) {
1872
- characterController.update(update, this.timeManager.time, this.timeManager.deltaTime);
2304
+ characterController.update(
2305
+ update,
2306
+ this.config.timeManager.time,
2307
+ this.config.timeManager.deltaTime
2308
+ );
1873
2309
  }
1874
2310
  }
1875
2311
  for (const [id, character] of this.remoteCharacters) {
1876
- if (!this.clientStates.has(id)) {
2312
+ if (!this.config.remoteUserStates.has(id)) {
1877
2313
  (_c = character.speakingIndicator) == null ? void 0 : _c.dispose();
1878
2314
  this.group.remove(character);
1879
2315
  this.remoteCharacters.delete(id);
1880
2316
  this.remoteCharacterControllers.delete(id);
1881
2317
  }
1882
2318
  }
1883
- if (this.updateLocationHash && this.timeManager.frame % 60 === 0) {
1884
- window.location.hash = encodeCharacterAndCamera(
2319
+ if (this.config.updateURLLocation && this.config.timeManager.frame % 60 === 0 && document.hasFocus()) {
2320
+ const hash = encodeCharacterAndCamera(
1885
2321
  this.localCharacter,
1886
- this.cameraManager.camera
2322
+ this.config.cameraManager.camera
1887
2323
  );
2324
+ const url = new URL(window.location.href);
2325
+ url.hash = hash;
2326
+ window.history.replaceState({}, "", url);
1888
2327
  }
1889
2328
  }
1890
2329
  }
1891
2330
  };
1892
2331
 
1893
2332
  // src/character/CharacterModelLoader.ts
1894
- import { LoadingManager } from "three";
1895
- import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
1896
- var CachedGLTFLoader = class extends ThreeGLTFLoader {
1897
- constructor(manager, debug = false) {
1898
- super(manager);
1899
- this.debug = debug;
1900
- this.blobCache = /* @__PURE__ */ new Map();
1901
- }
1902
- setBlobUrl(originalUrl, blobUrl) {
1903
- this.blobCache.set(originalUrl, blobUrl);
1904
- }
1905
- getBlobUrl(originalUrl) {
1906
- return this.blobCache.get(originalUrl);
1907
- }
1908
- load(url, onLoad, onProgress, onError) {
1909
- const blobUrl = this.getBlobUrl(url);
1910
- if (blobUrl) {
1911
- if (this.debug === true) {
1912
- console.log(`Loading cached ${url.split("/").pop()}`);
1913
- }
1914
- super.load(blobUrl, onLoad, onProgress, onError);
1915
- } else {
1916
- super.load(url, onLoad, onProgress, onError);
1917
- }
1918
- }
1919
- };
2333
+ import { ModelLoader as ModelLoader2 } from "@mml-io/model-loader";
1920
2334
  var LRUCache = class {
1921
2335
  constructor(maxSize = 100) {
1922
2336
  this.maxSize = maxSize;
@@ -1941,16 +2355,13 @@ var LRUCache = class {
1941
2355
  var CharacterModelLoader = class {
1942
2356
  constructor(maxCacheSize = 100, debug = false) {
1943
2357
  this.debug = debug;
2358
+ this.modelLoader = new ModelLoader2();
1944
2359
  this.ongoingLoads = /* @__PURE__ */ new Map();
1945
- this.loadingManager = new LoadingManager();
1946
- this.gltfLoader = new CachedGLTFLoader(this.loadingManager, this.debug);
1947
2360
  this.modelCache = new LRUCache(maxCacheSize);
1948
2361
  }
1949
2362
  async load(fileUrl, fileType) {
1950
2363
  const cachedModel = this.modelCache.get(fileUrl);
1951
2364
  if (cachedModel) {
1952
- const blobURL = URL.createObjectURL(cachedModel.blob);
1953
- this.gltfLoader.setBlobUrl(fileUrl, blobURL);
1954
2365
  return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
1955
2366
  } else {
1956
2367
  if (this.debug === true) {
@@ -1978,26 +2389,21 @@ var CharacterModelLoader = class {
1978
2389
  }
1979
2390
  async loadFromUrl(url, fileType, extension) {
1980
2391
  if (["gltf", "glb"].includes(extension)) {
1981
- return new Promise((resolve, reject) => {
1982
- this.gltfLoader.load(
2392
+ return new Promise(async (resolve, reject) => {
2393
+ const modelLoadResult = await this.modelLoader.load(
1983
2394
  url,
1984
- (object) => {
1985
- if (fileType === "model") {
1986
- resolve(object.scene);
1987
- } else if (fileType === "animation") {
1988
- resolve(object.animations[0]);
1989
- } else {
1990
- const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
1991
- console.error(error);
1992
- reject(error);
1993
- }
1994
- },
1995
- void 0,
1996
- (error) => {
1997
- console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
1998
- reject(error);
2395
+ (loaded, total) => {
1999
2396
  }
2000
2397
  );
2398
+ if (fileType === "model") {
2399
+ resolve(modelLoadResult.group);
2400
+ } else if (fileType === "animation") {
2401
+ resolve(modelLoadResult.animations[0]);
2402
+ } else {
2403
+ const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
2404
+ console.error(error);
2405
+ reject(error);
2406
+ }
2001
2407
  });
2002
2408
  } else {
2003
2409
  console.error(`Error: can't recognize ${url} extension: ${extension}`);
@@ -2011,17 +2417,9 @@ var KeyInputManager = class {
2011
2417
  this.shouldCaptureKeyPress = shouldCaptureKeyPress;
2012
2418
  this.keys = /* @__PURE__ */ new Map();
2013
2419
  this.eventHandlerCollection = new EventHandlerCollection();
2014
- this.directionJoystick = null;
2015
2420
  this.eventHandlerCollection.add(document, "keydown", this.onKeyDown.bind(this));
2016
2421
  this.eventHandlerCollection.add(document, "keyup", this.onKeyUp.bind(this));
2017
2422
  this.eventHandlerCollection.add(window, "blur", this.handleUnfocus.bind(this));
2018
- this.directionJoystick = new VirtualJoystick({
2019
- radius: 70,
2020
- inner_radius: 20,
2021
- x: 70,
2022
- y: 0,
2023
- mouse_support: false
2024
- });
2025
2423
  }
2026
2424
  handleUnfocus(_event) {
2027
2425
  this.keys.clear();
@@ -2045,19 +2443,19 @@ var KeyInputManager = class {
2045
2443
  return this.keys.get(key) || false;
2046
2444
  }
2047
2445
  isMovementKeyPressed() {
2048
- return ["w" /* W */, "a" /* A */, "s" /* S */, "d" /* D */].some((key) => this.isKeyPressed(key)) || this.directionJoystick.hasDirection;
2446
+ return ["w" /* W */, "a" /* A */, "s" /* S */, "d" /* D */].some((key) => this.isKeyPressed(key));
2049
2447
  }
2050
2448
  get forward() {
2051
- return this.isKeyPressed("w" /* W */) || this.directionJoystick.up;
2449
+ return this.isKeyPressed("w" /* W */);
2052
2450
  }
2053
2451
  get backward() {
2054
- return this.isKeyPressed("s" /* S */) || this.directionJoystick.down;
2452
+ return this.isKeyPressed("s" /* S */);
2055
2453
  }
2056
2454
  get left() {
2057
- return this.isKeyPressed("a" /* A */) || this.directionJoystick.left;
2455
+ return this.isKeyPressed("a" /* A */);
2058
2456
  }
2059
2457
  get right() {
2060
- return this.isKeyPressed("d" /* D */) || this.directionJoystick.right;
2458
+ return this.isKeyPressed("d" /* D */);
2061
2459
  }
2062
2460
  get run() {
2063
2461
  return this.isKeyPressed("shift" /* SHIFT */);
@@ -2085,40 +2483,35 @@ import {
2085
2483
  } from "mml-web";
2086
2484
  import { Group as Group3 } from "three";
2087
2485
  var MMLCompositionScene = class {
2088
- constructor(targetElement, renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation) {
2089
- this.renderer = renderer;
2090
- this.scene = scene;
2091
- this.camera = camera;
2092
- this.audioListener = audioListener;
2093
- this.collisionsManager = collisionsManager;
2094
- this.getUserPositionAndRotation = getUserPositionAndRotation;
2486
+ constructor(config) {
2487
+ this.config = config;
2095
2488
  this.chatProbes = /* @__PURE__ */ new Set();
2096
2489
  this.group = new Group3();
2097
- this.promptManager = PromptManager.init(targetElement);
2490
+ this.promptManager = PromptManager.init(this.config.targetElement);
2098
2491
  const { interactionListener, interactionManager } = InteractionManager.init(
2099
- targetElement,
2100
- this.camera,
2101
- this.scene
2492
+ this.config.targetElement,
2493
+ this.config.camera,
2494
+ this.config.scene
2102
2495
  );
2103
2496
  this.interactionManager = interactionManager;
2104
2497
  this.interactionListener = interactionListener;
2105
2498
  this.loadingProgressManager = new LoadingProgressManager();
2106
2499
  this.mmlScene = {
2107
- getAudioListener: () => this.audioListener,
2108
- getRenderer: () => this.renderer,
2109
- getThreeScene: () => this.scene,
2500
+ getAudioListener: () => this.config.audioListener,
2501
+ getRenderer: () => this.config.renderer,
2502
+ getThreeScene: () => this.config.scene,
2110
2503
  getRootContainer: () => this.group,
2111
- getCamera: () => this.camera,
2504
+ getCamera: () => this.config.camera,
2112
2505
  addCollider: (object, mElement) => {
2113
- this.collisionsManager.addMeshesGroup(object, mElement);
2506
+ this.config.collisionsManager.addMeshesGroup(object, mElement);
2114
2507
  },
2115
2508
  updateCollider: (object) => {
2116
- this.collisionsManager.updateMeshesGroup(object);
2509
+ this.config.collisionsManager.updateMeshesGroup(object);
2117
2510
  },
2118
2511
  removeCollider: (object) => {
2119
- this.collisionsManager.removeMeshesGroup(object);
2512
+ this.config.collisionsManager.removeMeshesGroup(object);
2120
2513
  },
2121
- getUserPositionAndRotation: this.getUserPositionAndRotation,
2514
+ getUserPositionAndRotation: this.config.getUserPositionAndRotation,
2122
2515
  addInteraction: (interaction) => {
2123
2516
  this.interactionListener.addInteraction(interaction);
2124
2517
  },
@@ -2143,7 +2536,7 @@ var MMLCompositionScene = class {
2143
2536
  return this.loadingProgressManager;
2144
2537
  }
2145
2538
  };
2146
- this.clickTrigger = MMLClickTrigger.init(targetElement, this.mmlScene);
2539
+ this.clickTrigger = MMLClickTrigger.init(this.config.targetElement, this.mmlScene);
2147
2540
  }
2148
2541
  onChatMessage(message) {
2149
2542
  for (const chatProbe of this.chatProbes) {
@@ -2163,8 +2556,8 @@ import { Pane } from "tweakpane";
2163
2556
 
2164
2557
  // src/tweakpane/blades/bcsFolder.ts
2165
2558
  var bcsValues = {
2166
- brightness: 0,
2167
- contrast: 1,
2559
+ brightness: 0.06,
2560
+ contrast: 1.2,
2168
2561
  saturation: 1
2169
2562
  };
2170
2563
  var bcsOptions = {
@@ -2211,10 +2604,10 @@ var BrightnessContrastSaturationFolder = class {
2211
2604
  // src/tweakpane/blades/environmentFolder.ts
2212
2605
  var sunValues = {
2213
2606
  sunPosition: {
2214
- sunAzimuthalAngle: 214.5,
2215
- sunPolarAngle: -41.5
2607
+ sunAzimuthalAngle: 219,
2608
+ sunPolarAngle: -37
2216
2609
  },
2217
- sunIntensity: 1,
2610
+ sunIntensity: 3.7,
2218
2611
  sunColor: { r: 1, g: 1, b: 1 }
2219
2612
  };
2220
2613
  var sunOptions = {
@@ -2222,20 +2615,29 @@ var sunOptions = {
2222
2615
  sunAzimuthalAngle: { min: 0, max: 360, step: 1 },
2223
2616
  sunPolarAngle: { min: -90, max: 90, step: 1 }
2224
2617
  },
2225
- sunIntensity: { min: 0, max: 3, step: 0.05 }
2618
+ sunIntensity: { min: 0, max: 10, step: 0.1 }
2226
2619
  };
2227
2620
  var envValues = {
2621
+ skyboxAzimuthalAngle: 0,
2622
+ skyboxPolarAngle: 0,
2623
+ envMapIntensity: 0.07,
2624
+ skyboxIntensity: 0.8,
2625
+ skyboxBlurriness: 0,
2228
2626
  ambientLight: {
2229
- ambientLightIntensity: 0.05,
2627
+ ambientLightIntensity: 0.27,
2230
2628
  ambientLightColor: { r: 1, g: 1, b: 1 }
2231
2629
  },
2232
2630
  fog: {
2233
- fogNear: 30,
2234
- fogFar: 210,
2235
- fogColor: { r: 0.42, g: 0.48, b: 0.59 }
2631
+ fogNear: 21,
2632
+ fogFar: 180,
2633
+ fogColor: { r: 0.7, g: 0.7, b: 0.7 }
2236
2634
  }
2237
2635
  };
2238
2636
  var envOptions = {
2637
+ skyboxAzimuthalAngle: { min: 0, max: 360, step: 1 },
2638
+ skyboxPolarAngle: { min: 0, max: 360, step: 1 },
2639
+ skyboxIntensity: { min: 0, max: 1.3, step: 0.01 },
2640
+ skyboxBlurriness: { min: 0, max: 0.1, step: 1e-3 },
2239
2641
  ambientLight: {
2240
2642
  ambientLightIntensity: { min: 0, max: 1, step: 0.01 }
2241
2643
  },
@@ -2248,6 +2650,9 @@ var EnvironmentFolder = class {
2248
2650
  constructor(parentFolder, expand = false) {
2249
2651
  this.folder = parentFolder.addFolder({ title: "environment", expanded: expand });
2250
2652
  this.sun = this.folder.addFolder({ title: "sun", expanded: true });
2653
+ this.envMap = this.folder.addFolder({ title: "envMap", expanded: true });
2654
+ this.fog = this.folder.addFolder({ title: "fog", expanded: true });
2655
+ this.skybox = this.folder.addFolder({ title: "skybox", expanded: true });
2251
2656
  this.ambient = this.folder.addFolder({ title: "ambient", expanded: true });
2252
2657
  this.sun.addBinding(
2253
2658
  sunValues.sunPosition,
@@ -2263,7 +2668,12 @@ var EnvironmentFolder = class {
2263
2668
  this.sun.addBinding(sunValues, "sunColor", {
2264
2669
  color: { type: "float" }
2265
2670
  });
2266
- this.sunButton = this.sun.addButton({ title: "Set HDRI" });
2671
+ this.hdrButton = this.skybox.addButton({ title: "Set HDRI" });
2672
+ this.skybox.addBinding(envValues, "skyboxIntensity", envOptions.skyboxIntensity);
2673
+ this.skybox.addBinding(envValues, "skyboxBlurriness", envOptions.skyboxBlurriness);
2674
+ this.skybox.addBinding(envValues, "skyboxAzimuthalAngle", envOptions.skyboxAzimuthalAngle);
2675
+ this.skybox.addBinding(envValues, "skyboxPolarAngle", envOptions.skyboxPolarAngle);
2676
+ this.envMap.addBinding(envValues, "envMapIntensity", envOptions.skyboxIntensity);
2267
2677
  this.ambient.addBinding(
2268
2678
  envValues.ambientLight,
2269
2679
  "ambientLightIntensity",
@@ -2272,13 +2682,13 @@ var EnvironmentFolder = class {
2272
2682
  this.ambient.addBinding(envValues.ambientLight, "ambientLightColor", {
2273
2683
  color: { type: "float" }
2274
2684
  });
2275
- this.ambient.addBinding(envValues.fog, "fogNear", envOptions.fog.fogNear);
2276
- this.ambient.addBinding(envValues.fog, "fogFar", envOptions.fog.fogFar);
2277
- this.ambient.addBinding(envValues.fog, "fogColor", {
2685
+ this.fog.addBinding(envValues.fog, "fogNear", envOptions.fog.fogNear);
2686
+ this.fog.addBinding(envValues.fog, "fogFar", envOptions.fog.fogFar);
2687
+ this.fog.addBinding(envValues.fog, "fogColor", {
2278
2688
  color: { type: "float" }
2279
2689
  });
2280
2690
  }
2281
- setupChangeEvent(setHDR, setAmbientLight, setFog, sun) {
2691
+ setupChangeEvent(scene, setHDR, setSkyboxAzimuthalAngle, setSkyboxPolarAngle, setAmbientLight, setFog, sun) {
2282
2692
  this.sun.on("change", (e) => {
2283
2693
  const target = e.target.key;
2284
2694
  if (!target)
@@ -2313,9 +2723,43 @@ var EnvironmentFolder = class {
2313
2723
  break;
2314
2724
  }
2315
2725
  });
2316
- this.sunButton.on("click", () => {
2726
+ this.hdrButton.on("click", () => {
2317
2727
  setHDR();
2318
2728
  });
2729
+ this.envMap.on("change", (e) => {
2730
+ const target = e.target.key;
2731
+ if (!target)
2732
+ return;
2733
+ switch (target) {
2734
+ case "envMapIntensity":
2735
+ scene.environmentIntensity = e.value;
2736
+ break;
2737
+ }
2738
+ });
2739
+ this.skybox.on("change", (e) => {
2740
+ const target = e.target.key;
2741
+ if (!target)
2742
+ return;
2743
+ switch (target) {
2744
+ case "skyboxAzimuthalAngle": {
2745
+ const value = e.value;
2746
+ setSkyboxAzimuthalAngle(value);
2747
+ break;
2748
+ }
2749
+ case "skyboxPolarAngle": {
2750
+ const value = e.value;
2751
+ envValues.skyboxPolarAngle = value;
2752
+ setSkyboxPolarAngle(value);
2753
+ break;
2754
+ }
2755
+ case "skyboxIntensity":
2756
+ scene.backgroundIntensity = e.value;
2757
+ break;
2758
+ case "skyboxBlurriness":
2759
+ scene.backgroundBlurriness = e.value;
2760
+ break;
2761
+ }
2762
+ });
2319
2763
  this.ambient.on("change", (e) => {
2320
2764
  const target = e.target.key;
2321
2765
  if (!target)
@@ -2336,6 +2780,13 @@ var EnvironmentFolder = class {
2336
2780
  setAmbientLight();
2337
2781
  break;
2338
2782
  }
2783
+ }
2784
+ });
2785
+ this.fog.on("change", (e) => {
2786
+ const target = e.target.key;
2787
+ if (!target)
2788
+ return;
2789
+ switch (target) {
2339
2790
  case "fogNear": {
2340
2791
  envValues.fog.fogNear = e.value;
2341
2792
  setFog();
@@ -2356,8 +2807,6 @@ var EnvironmentFolder = class {
2356
2807
  setFog();
2357
2808
  break;
2358
2809
  }
2359
- default:
2360
- break;
2361
2810
  }
2362
2811
  });
2363
2812
  }
@@ -2365,15 +2814,15 @@ var EnvironmentFolder = class {
2365
2814
 
2366
2815
  // src/tweakpane/blades/postExtrasFolder.ts
2367
2816
  var extrasValues = {
2368
- grain: 0.055,
2369
- bloom: 3
2817
+ grain: 0.045,
2818
+ bloom: 0.15
2370
2819
  };
2371
2820
  var extrasOptions = {
2372
2821
  grain: {
2373
2822
  amount: { min: 0, max: 0.2, step: 2e-3 }
2374
2823
  },
2375
2824
  bloom: {
2376
- amount: { min: 0, max: 5, step: 0.05 }
2825
+ amount: { min: 0, max: 50, step: 0.05 }
2377
2826
  }
2378
2827
  };
2379
2828
  var PostExtrasFolder = class {
@@ -2405,16 +2854,12 @@ var PostExtrasFolder = class {
2405
2854
  var rendererValues = {
2406
2855
  shadowMap: 2,
2407
2856
  toneMapping: 5,
2408
- exposure: 1,
2409
- bgIntensity: 1,
2410
- bgBlurriness: 0
2857
+ exposure: 1.7
2411
2858
  };
2412
2859
  var rendererOptions = {
2413
2860
  shadowMap: { min: 0, max: 2, step: 1 },
2414
2861
  toneMapping: { min: 0, max: 5, step: 1 },
2415
- exposure: { min: 0, max: 3, step: 0.01 },
2416
- bgIntensity: { min: 0, max: 1.3, step: 0.01 },
2417
- bgBlurriness: { min: 0, max: 0.1, step: 1e-3 }
2862
+ exposure: { min: 0, max: 3, step: 0.01 }
2418
2863
  };
2419
2864
  var shadowMapTypes = {
2420
2865
  0: "BasicShadowMap",
@@ -2438,6 +2883,8 @@ var setShadowMapType = (value) => {
2438
2883
  };
2439
2884
  var setToneMappingType = (value) => {
2440
2885
  monitoredValues.toneMappingType = toneMappingTypes[value];
2886
+ rendererValues.toneMapping = value;
2887
+ console.log(rendererValues.toneMapping);
2441
2888
  };
2442
2889
  var RendererFolder = class {
2443
2890
  constructor(parentFolder, expand = false) {
@@ -2450,23 +2897,21 @@ var RendererFolder = class {
2450
2897
  this.folder.addBinding(rendererValues, "toneMapping", rendererOptions.toneMapping);
2451
2898
  this.folder.addBinding(monitoredValues, "toneMappingType", { readonly: true });
2452
2899
  this.folder.addBinding(rendererValues, "exposure", rendererOptions.exposure);
2453
- this.folder.addBinding(rendererValues, "bgIntensity", rendererOptions.bgIntensity);
2454
- this.folder.addBinding(rendererValues, "bgBlurriness", rendererOptions.bgBlurriness);
2455
2900
  }
2456
- setupChangeEvent(scene, renderer, toneMappingFolder, toneMappingPass) {
2901
+ setupChangeEvent(renderer, toneMappingFolder, toneMappingPass) {
2457
2902
  this.folder.on("change", (e) => {
2458
2903
  const target = e.target.key;
2459
2904
  if (!target)
2460
2905
  return;
2461
2906
  switch (target) {
2462
2907
  case "shadowMap": {
2463
- const value = e.value;
2464
- renderer.shadowMap.type = value;
2465
- setShadowMapType(value);
2908
+ const value2 = e.value;
2909
+ renderer.shadowMap.type = value2;
2910
+ setShadowMapType(value2);
2466
2911
  break;
2467
2912
  }
2468
2913
  case "toneMapping":
2469
- renderer.toneMapping = e.value;
2914
+ const value = e.value;
2470
2915
  toneMappingFolder.hidden = e.value !== 5;
2471
2916
  toneMappingPass.enabled = e.value === 5 ? true : false;
2472
2917
  setToneMappingType(e.value);
@@ -2474,12 +2919,6 @@ var RendererFolder = class {
2474
2919
  case "exposure":
2475
2920
  renderer.toneMappingExposure = e.value;
2476
2921
  break;
2477
- case "bgIntensity":
2478
- scene.backgroundIntensity = e.value;
2479
- break;
2480
- case "bgBlurriness":
2481
- scene.backgroundBlurriness = e.value;
2482
- break;
2483
2922
  default:
2484
2923
  break;
2485
2924
  }
@@ -2538,10 +2977,10 @@ var ppssaoValues = {
2538
2977
  samples: 30,
2539
2978
  rings: 11,
2540
2979
  luminanceInfluence: 0.7,
2541
- radius: 0.03,
2542
- intensity: 2.5,
2543
- bias: 0.05,
2544
- fade: 0.03,
2980
+ radius: 0.045,
2981
+ intensity: 3.14,
2982
+ bias: 0.01,
2983
+ fade: 0.06,
2545
2984
  resolutionScale: 0.5,
2546
2985
  color: { r: 0, g: 0, b: 0 },
2547
2986
  worldDistanceThreshold: 30,
@@ -2568,7 +3007,7 @@ var n8ssaoValues = {
2568
3007
  halfRes: true,
2569
3008
  aoRadius: 5,
2570
3009
  distanceFalloff: 3,
2571
- intensity: 1,
3010
+ intensity: 1.5,
2572
3011
  color: { r: 0, g: 0, b: 0 },
2573
3012
  aoSamples: 16,
2574
3013
  denoiseSamples: 4,
@@ -2742,6 +3181,10 @@ var SSAOFolder = class {
2742
3181
  case "enabled":
2743
3182
  if (e.value === true) {
2744
3183
  composer.addPass(n8aopass, this.postProcessingSSAOIndex + 2);
3184
+ composer.passes[this.postProcessingSSAOIndex + 2].setSize(
3185
+ window.innerWidth,
3186
+ window.innerHeight
3187
+ );
2745
3188
  } else {
2746
3189
  composer.removePass(n8aopass);
2747
3190
  }
@@ -2784,7 +3227,7 @@ var SSAOFolder = class {
2784
3227
 
2785
3228
  // src/tweakpane/blades/toneMappingFolder.ts
2786
3229
  var toneMappingValues = {
2787
- mode: 2,
3230
+ mode: 7,
2788
3231
  resolution: 512,
2789
3232
  whitePoint: 32,
2790
3233
  middleGrey: 21,
@@ -2793,20 +3236,24 @@ var toneMappingValues = {
2793
3236
  adaptationRate: 2
2794
3237
  };
2795
3238
  var toneMappingOptions = {
2796
- mode: { min: 0, max: 4, step: 1 },
3239
+ mode: { min: 0, max: 8, step: 1 },
2797
3240
  resolution: { min: 64, max: 512, step: 64 },
2798
3241
  whitePoint: { min: 0, max: 32, step: 0.01 },
2799
3242
  middleGrey: { min: 0, max: 32, step: 0.01 },
2800
3243
  minLuminance: { min: 0, max: 32, step: 1e-3 },
2801
- averageLuminance: { min: 1e-3, max: 0.2, step: 1e-3 },
3244
+ averageLuminance: { min: 1e-3, max: 2, step: 1e-3 },
2802
3245
  adaptationRate: { min: 0.1, max: 2, step: 0.1 }
2803
3246
  };
2804
3247
  var customToneMappingTypes = {
2805
- 0: "REINHARD",
2806
- 1: "REINHARD2",
2807
- 2: "REINHARD2_ADAPTIVE",
2808
- 3: "OPTIMIZED_CINEON",
2809
- 4: "ACES_FILMIC"
3248
+ 0: "LINEAR",
3249
+ 1: "REINHARD",
3250
+ 2: "REINHARD2",
3251
+ 3: "REINHARD2_ADAPTIVE",
3252
+ 4: "UNCHARTED2",
3253
+ 5: "OPTIMIZED_CINEON",
3254
+ 6: "ACES_FILMIC",
3255
+ 7: "AGX",
3256
+ 8: "NEUTRAL"
2810
3257
  };
2811
3258
  var customToneMappingBlade = {
2812
3259
  customToneMappingType: customToneMappingTypes[toneMappingValues.mode]
@@ -2829,13 +3276,13 @@ var ToneMappingFolder = class {
2829
3276
  "minLuminance",
2830
3277
  toneMappingOptions.minLuminance
2831
3278
  );
2832
- this.minLuminance.hidden = toneMappingValues.mode === 2 ? true : false;
3279
+ this.minLuminance.hidden = false;
2833
3280
  this.avgLuminance = this.folder.addBinding(
2834
3281
  toneMappingValues,
2835
3282
  "averageLuminance",
2836
3283
  toneMappingOptions.averageLuminance
2837
3284
  );
2838
- this.avgLuminance.hidden = toneMappingValues.mode === 2 ? true : false;
3285
+ this.avgLuminance.hidden = false;
2839
3286
  this.folder.addBinding(toneMappingValues, "adaptationRate", toneMappingOptions.adaptationRate);
2840
3287
  }
2841
3288
  setupChangeEvent(toneMappingEffect) {
@@ -2844,8 +3291,6 @@ var ToneMappingFolder = class {
2844
3291
  if (!target)
2845
3292
  return;
2846
3293
  if (target === "mode") {
2847
- this.minLuminance.hidden = toneMappingValues.mode === 2 ? true : false;
2848
- this.avgLuminance.hidden = toneMappingValues.mode === 2 ? true : false;
2849
3294
  setCustomToneMappingType(e.value);
2850
3295
  }
2851
3296
  toneMappingEffect[target] = e.value;
@@ -2942,16 +3387,26 @@ var tweakPaneStyle = `
2942
3387
 
2943
3388
  // src/tweakpane/TweakPane.ts
2944
3389
  var TweakPane = class {
2945
- constructor(renderer, scene, composer) {
3390
+ constructor(holderElement, renderer, scene, composer) {
3391
+ this.holderElement = holderElement;
2946
3392
  this.renderer = renderer;
2947
3393
  this.scene = scene;
2948
3394
  this.composer = composer;
2949
3395
  this.saveVisibilityInLocalStorage = true;
2950
3396
  this.guiVisible = false;
2951
- const appWrapper = document.getElementById("app");
2952
3397
  const tweakPaneWrapper = document.createElement("div");
2953
- tweakPaneWrapper.id = "tweakpane-panel";
2954
- appWrapper.appendChild(tweakPaneWrapper);
3398
+ tweakPaneWrapper.style.position = "fixed";
3399
+ tweakPaneWrapper.style.width = "400px";
3400
+ tweakPaneWrapper.style.height = "100%";
3401
+ tweakPaneWrapper.style.top = "0px";
3402
+ tweakPaneWrapper.style.right = "calc(-50vw)";
3403
+ tweakPaneWrapper.style.zIndex = "99";
3404
+ tweakPaneWrapper.style.overflow = "auto";
3405
+ tweakPaneWrapper.style.backgroundColor = "rgba(0, 0, 0, 0.66)";
3406
+ tweakPaneWrapper.style.paddingLeft = "5px";
3407
+ tweakPaneWrapper.style.boxShadow = "-7px 0px 12px rgba(0, 0, 0, 0.5)";
3408
+ tweakPaneWrapper.style.transition = "right cubic-bezier(0.83, 0, 0.17, 1) 0.7s";
3409
+ holderElement.appendChild(tweakPaneWrapper);
2955
3410
  this.gui = new Pane({ container: tweakPaneWrapper });
2956
3411
  this.gui.registerPlugin(EssentialsPlugin);
2957
3412
  if (this.saveVisibilityInLocalStorage) {
@@ -2976,11 +3431,14 @@ var TweakPane = class {
2976
3431
  this.postExtrasFolder = new PostExtrasFolder(this.gui, false);
2977
3432
  this.character = new CharacterFolder(this.gui, false);
2978
3433
  this.environment = new EnvironmentFolder(this.gui, false);
3434
+ this.camera = new CameraFolder(this.gui, false);
3435
+ this.characterControls = new CharacterControlsFolder(this.gui, false);
2979
3436
  this.toneMappingFolder.folder.hidden = rendererValues.toneMapping === 5 ? false : true;
2980
3437
  this.export = this.gui.addFolder({ title: "import / export", expanded: false });
2981
- window.addEventListener("keydown", this.processKey.bind(this));
2982
- this.setupRenderPane = this.setupRenderPane.bind(this);
2983
- this.setupGUIListeners.bind(this)();
3438
+ window.addEventListener("keydown", (e) => {
3439
+ this.processKey(e);
3440
+ });
3441
+ this.setupGUIListeners();
2984
3442
  }
2985
3443
  setupGUIListeners() {
2986
3444
  const gui = this.gui;
@@ -2995,9 +3453,8 @@ var TweakPane = class {
2995
3453
  if (e.key === "p")
2996
3454
  this.toggleGUI();
2997
3455
  }
2998
- setupRenderPane(composer, normalPass, ppssaoEffect, ppssaoPass, n8aopass, toneMappingEffect, toneMappingPass, brightnessContrastSaturation, bloomEffect, gaussGrainEffect, hasLighting, sun, setHDR, setAmbientLight, setFog) {
3456
+ setupRenderPane(composer, normalPass, ppssaoEffect, ppssaoPass, n8aopass, toneMappingEffect, toneMappingPass, brightnessContrastSaturation, bloomEffect, gaussGrainEffect, hasLighting, sun, setHDR, setSkyboxAzimuthalAngle, setSkyboxPolarAngle, setAmbientLight, setFog) {
2999
3457
  this.rendererFolder.setupChangeEvent(
3000
- this.scene,
3001
3458
  this.renderer,
3002
3459
  this.toneMappingFolder.folder,
3003
3460
  toneMappingPass
@@ -3006,7 +3463,15 @@ var TweakPane = class {
3006
3463
  this.ssaoFolder.setupChangeEvent(composer, normalPass, ppssaoEffect, ppssaoPass, n8aopass);
3007
3464
  this.bcsFolder.setupChangeEvent(brightnessContrastSaturation);
3008
3465
  this.postExtrasFolder.setupChangeEvent(bloomEffect, gaussGrainEffect);
3009
- this.environment.setupChangeEvent(setHDR, setAmbientLight, setFog, sun);
3466
+ this.environment.setupChangeEvent(
3467
+ this.scene,
3468
+ setHDR,
3469
+ setSkyboxAzimuthalAngle,
3470
+ setSkyboxPolarAngle,
3471
+ setAmbientLight,
3472
+ setFog,
3473
+ sun
3474
+ );
3010
3475
  this.environment.folder.hidden = hasLighting === false || sun === null;
3011
3476
  const exportButton = this.export.addButton({ title: "export" });
3012
3477
  exportButton.on("click", () => {
@@ -3019,9 +3484,21 @@ var TweakPane = class {
3019
3484
  });
3020
3485
  });
3021
3486
  }
3487
+ setupCamPane(cameraManager) {
3488
+ this.camera.setupChangeEvent(cameraManager);
3489
+ }
3490
+ setupCharacterController(localController) {
3491
+ this.characterControls.setupChangeEvent(localController);
3492
+ }
3022
3493
  updateStats(timeManager) {
3023
3494
  this.renderStatsFolder.update(this.renderer, this.composer, timeManager);
3024
3495
  }
3496
+ updateCameraData(cameraManager) {
3497
+ this.camera.update(cameraManager);
3498
+ }
3499
+ updateCharacterData(localController) {
3500
+ this.characterControls.update(localController);
3501
+ }
3025
3502
  formatDateForFilename() {
3026
3503
  const date = /* @__PURE__ */ new Date();
3027
3504
  const year = date.getFullYear();
@@ -3077,33 +3554,37 @@ var TweakPane = class {
3077
3554
  };
3078
3555
 
3079
3556
  // src/rendering/composer.ts
3557
+ import { HDRJPGLoader } from "@monogrid/gainmap-js";
3080
3558
  import {
3559
+ BlendFunction as BlendFunction2,
3560
+ BloomEffect,
3561
+ EdgeDetectionMode,
3081
3562
  EffectComposer as EffectComposer2,
3082
- RenderPass,
3083
3563
  EffectPass as EffectPass2,
3084
3564
  FXAAEffect,
3565
+ NormalPass as NormalPass2,
3566
+ PredicationMode,
3567
+ RenderPass,
3085
3568
  ShaderPass,
3086
- BloomEffect,
3087
- SSAOEffect as SSAOEffect2,
3088
- BlendFunction as BlendFunction2,
3089
- TextureEffect,
3090
- ToneMappingEffect,
3091
3569
  SMAAEffect,
3092
3570
  SMAAPreset,
3093
- EdgeDetectionMode,
3094
- PredicationMode,
3095
- NormalPass as NormalPass2
3571
+ SSAOEffect as SSAOEffect2,
3572
+ TextureEffect,
3573
+ ToneMappingEffect
3096
3574
  } from "postprocessing";
3097
3575
  import {
3098
3576
  AmbientLight,
3099
3577
  Color as Color7,
3578
+ EquirectangularReflectionMapping,
3579
+ Euler as Euler3,
3100
3580
  Fog as Fog2,
3101
3581
  HalfFloatType as HalfFloatType2,
3102
3582
  LinearSRGBColorSpace,
3103
- LoadingManager as LoadingManager2,
3583
+ LoadingManager,
3584
+ MathUtils,
3104
3585
  PMREMGenerator,
3105
- SRGBColorSpace,
3106
3586
  Scene as Scene4,
3587
+ SRGBColorSpace,
3107
3588
  Vector2 as Vector27,
3108
3589
  WebGLRenderer as WebGLRenderer4
3109
3590
  } from "three";
@@ -3135,9 +3616,9 @@ var Sun = class extends Group4 {
3135
3616
  if (this.debug === true) {
3136
3617
  this.camHelper = new CameraHelper(this.shadowCamera);
3137
3618
  }
3138
- this.directionalLight = new DirectionalLight(16777215, 0.5);
3619
+ this.directionalLight = new DirectionalLight(16777215);
3139
3620
  this.directionalLight.intensity = sunValues.sunIntensity;
3140
- this.directionalLight.shadow.normalBias = 0.01;
3621
+ this.directionalLight.shadow.normalBias = 0.1;
3141
3622
  this.directionalLight.shadow.radius = 0.02;
3142
3623
  this.directionalLight.shadow.camera = this.shadowCamera;
3143
3624
  this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
@@ -4648,7 +5129,12 @@ var N8SSAOPass = class extends Pass {
4648
5129
 
4649
5130
  // src/rendering/composer.ts
4650
5131
  var Composer = class {
4651
- constructor(scene, camera, spawnSun = false) {
5132
+ constructor({
5133
+ scene,
5134
+ camera,
5135
+ spawnSun = false,
5136
+ environmentConfiguration
5137
+ }) {
4652
5138
  this.width = 1;
4653
5139
  this.height = 1;
4654
5140
  this.resolution = new Vector27(this.width, this.height);
@@ -4657,15 +5143,14 @@ var Composer = class {
4657
5143
  this.gaussGrainEffect = GaussGrainEffect;
4658
5144
  this.ambientLight = null;
4659
5145
  this.sun = null;
5146
+ var _a;
4660
5147
  this.scene = scene;
4661
5148
  this.postPostScene = new Scene4();
4662
5149
  this.camera = camera;
4663
5150
  this.spawnSun = spawnSun;
4664
5151
  this.renderer = new WebGLRenderer4({
4665
5152
  powerPreference: "high-performance",
4666
- antialias: false,
4667
- stencil: false,
4668
- depth: false
5153
+ antialias: false
4669
5154
  });
4670
5155
  this.renderer.outputColorSpace = SRGBColorSpace;
4671
5156
  this.renderer.info.autoReset = false;
@@ -4674,7 +5159,9 @@ var Composer = class {
4674
5159
  this.renderer.shadowMap.type = rendererValues.shadowMap;
4675
5160
  this.renderer.toneMapping = rendererValues.toneMapping;
4676
5161
  this.renderer.toneMappingExposure = rendererValues.exposure;
4677
- this.setAmbientLight();
5162
+ this.environmentConfiguration = environmentConfiguration;
5163
+ this.updateSkyboxAndEnvValues();
5164
+ this.updateAmbientLightValues();
4678
5165
  this.setFog();
4679
5166
  this.effectComposer = new EffectComposer2(this.renderer, {
4680
5167
  frameBufferType: HalfFloatType2
@@ -4707,6 +5194,9 @@ var Composer = class {
4707
5194
  this.ppssaoPass = new EffectPass2(this.camera, this.ppssaoEffect, this.normalTextureEffect);
4708
5195
  this.ppssaoPass.enabled = ppssaoValues.enabled;
4709
5196
  this.fxaaEffect = new FXAAEffect();
5197
+ if ((_a = environmentConfiguration == null ? void 0 : environmentConfiguration.postProcessing) == null ? void 0 : _a.bloomIntensity) {
5198
+ extrasValues.bloom = environmentConfiguration.postProcessing.bloomIntensity;
5199
+ }
4710
5200
  this.bloomEffect = new BloomEffect({
4711
5201
  intensity: extrasValues.bloom
4712
5202
  });
@@ -4746,6 +5236,8 @@ var Composer = class {
4746
5236
  this.bcs.uniforms.contrast.value = bcsValues.contrast;
4747
5237
  this.bcs.uniforms.saturation.value = bcsValues.saturation;
4748
5238
  this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
5239
+ this.gaussGrainEffect.uniforms.amount.value = extrasValues.grain;
5240
+ this.gaussGrainEffect.uniforms.alpha.value = 1;
4749
5241
  this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
4750
5242
  this.effectComposer.addPass(this.renderPass);
4751
5243
  if (ppssaoValues.enabled) {
@@ -4756,7 +5248,6 @@ var Composer = class {
4756
5248
  this.effectComposer.addPass(this.n8aopass);
4757
5249
  }
4758
5250
  this.effectComposer.addPass(this.fxaaPass);
4759
- this.effectComposer.addPass(this.smaaPass);
4760
5251
  this.effectComposer.addPass(this.bloomPass);
4761
5252
  this.effectComposer.addPass(this.toneMappingPass);
4762
5253
  this.effectComposer.addPass(this.bcsPass);
@@ -4765,6 +5256,7 @@ var Composer = class {
4765
5256
  this.sun = new Sun();
4766
5257
  this.scene.add(this.sun);
4767
5258
  }
5259
+ this.updateSunValues();
4768
5260
  this.resizeListener = () => {
4769
5261
  this.fitContainer();
4770
5262
  };
@@ -4786,6 +5278,14 @@ var Composer = class {
4786
5278
  this.spawnSun,
4787
5279
  this.sun,
4788
5280
  this.setHDRIFromFile.bind(this),
5281
+ (azimuthalAngle) => {
5282
+ envValues.skyboxAzimuthalAngle = azimuthalAngle;
5283
+ this.updateSkyboxRotation();
5284
+ },
5285
+ (polarAngle) => {
5286
+ envValues.skyboxPolarAngle = polarAngle;
5287
+ this.updateSkyboxRotation();
5288
+ },
4789
5289
  this.setAmbientLight.bind(this),
4790
5290
  this.setFog.bind(this)
4791
5291
  );
@@ -4829,30 +5329,83 @@ var Composer = class {
4829
5329
  this.bloomPass.setSize(this.width, this.height);
4830
5330
  this.toneMappingPass.setSize(this.width, this.height);
4831
5331
  this.gaussGrainPass.setSize(this.width, this.height);
5332
+ this.gaussGrainEffect.uniforms.resolution.value = new Vector27(this.width, this.height);
4832
5333
  this.renderer.setSize(this.width, this.height);
4833
5334
  }
4834
5335
  render(timeManager) {
4835
5336
  this.renderer.info.reset();
4836
5337
  this.normalPass.texture.needsUpdate = true;
4837
- this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
4838
5338
  this.gaussGrainEffect.uniforms.time.value = timeManager.time;
4839
- this.gaussGrainEffect.uniforms.alpha.value = 1;
4840
5339
  this.effectComposer.render();
5340
+ this.renderer.clearDepth();
4841
5341
  this.renderer.render(this.postPostScene, this.camera);
4842
5342
  }
5343
+ updateSkyboxRotation() {
5344
+ this.scene.backgroundRotation = new Euler3(
5345
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5346
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5347
+ 0
5348
+ );
5349
+ this.scene.environmentRotation = new Euler3(
5350
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5351
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5352
+ 0
5353
+ );
5354
+ }
5355
+ useHDRJPG(url, fromFile = false) {
5356
+ const pmremGenerator = new PMREMGenerator(this.renderer);
5357
+ const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
5358
+ const hdrJpgEquirectangularMap = hdrJpg.renderTarget.texture;
5359
+ hdrJpgEquirectangularMap.mapping = EquirectangularReflectionMapping;
5360
+ hdrJpgEquirectangularMap.needsUpdate = true;
5361
+ const envMap = pmremGenerator.fromEquirectangular(hdrJpgEquirectangularMap).texture;
5362
+ if (envMap) {
5363
+ envMap.colorSpace = LinearSRGBColorSpace;
5364
+ envMap.needsUpdate = true;
5365
+ this.scene.environment = envMap;
5366
+ this.scene.environmentIntensity = envValues.envMapIntensity;
5367
+ this.scene.environmentRotation = new Euler3(
5368
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5369
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5370
+ 0
5371
+ );
5372
+ this.scene.background = envMap;
5373
+ this.scene.backgroundIntensity = envValues.skyboxIntensity;
5374
+ this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
5375
+ this.scene.backgroundRotation = new Euler3(
5376
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5377
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5378
+ 0
5379
+ );
5380
+ this.isEnvHDRI = true;
5381
+ hdrJpgEquirectangularMap.dispose();
5382
+ pmremGenerator.dispose();
5383
+ }
5384
+ hdrJpg.dispose();
5385
+ });
5386
+ }
4843
5387
  useHDRI(url, fromFile = false) {
4844
- if (this.isEnvHDRI && fromFile === false || !this.renderer)
5388
+ if (this.isEnvHDRI && fromFile === false || !this.renderer) {
4845
5389
  return;
5390
+ }
4846
5391
  const pmremGenerator = new PMREMGenerator(this.renderer);
4847
- new RGBELoader(new LoadingManager2()).load(
5392
+ new RGBELoader(new LoadingManager()).load(
4848
5393
  url,
4849
5394
  (texture) => {
4850
5395
  const envMap = pmremGenerator.fromEquirectangular(texture).texture;
4851
5396
  if (envMap) {
4852
5397
  envMap.colorSpace = LinearSRGBColorSpace;
4853
5398
  envMap.needsUpdate = true;
5399
+ this.scene.environment = envMap;
5400
+ this.scene.environmentIntensity = envValues.envMapIntensity;
5401
+ this.scene.environmentRotation = new Euler3(
5402
+ MathUtils.degToRad(envValues.skyboxPolarAngle),
5403
+ MathUtils.degToRad(envValues.skyboxAzimuthalAngle),
5404
+ 0
5405
+ );
4854
5406
  this.scene.background = envMap;
4855
- this.scene.backgroundIntensity = rendererValues.bgIntensity;
5407
+ this.scene.backgroundIntensity = envValues.skyboxIntensity;
5408
+ this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
4856
5409
  this.isEnvHDRI = true;
4857
5410
  texture.dispose();
4858
5411
  pmremGenerator.dispose();
@@ -4870,7 +5423,7 @@ var Composer = class {
4870
5423
  return;
4871
5424
  const fileInput = document.createElement("input");
4872
5425
  fileInput.type = "file";
4873
- fileInput.accept = ".hdr";
5426
+ fileInput.accept = ".hdr,.jpg";
4874
5427
  fileInput.addEventListener("change", () => {
4875
5428
  var _a;
4876
5429
  const file = (_a = fileInput.files) == null ? void 0 : _a[0];
@@ -4878,9 +5431,16 @@ var Composer = class {
4878
5431
  console.log("no file");
4879
5432
  return;
4880
5433
  }
5434
+ const extension = file.name.split(".").pop();
4881
5435
  const fileURL = URL.createObjectURL(file);
4882
5436
  if (fileURL) {
4883
- this.useHDRI(fileURL, true);
5437
+ if (extension === "hdr") {
5438
+ this.useHDRI(fileURL, true);
5439
+ } else if (extension === "jpg") {
5440
+ this.useHDRJPG(fileURL);
5441
+ } else {
5442
+ console.error(`Unrecognized extension for HDR file ${file.name}`);
5443
+ }
4884
5444
  URL.revokeObjectURL(fileURL);
4885
5445
  document.body.removeChild(fileInput);
4886
5446
  }
@@ -4912,6 +5472,51 @@ var Composer = class {
4912
5472
  );
4913
5473
  this.scene.add(this.ambientLight);
4914
5474
  }
5475
+ updateSunValues() {
5476
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
5477
+ if (typeof ((_b = (_a = this.environmentConfiguration) == null ? void 0 : _a.sun) == null ? void 0 : _b.intensity) === "number") {
5478
+ sunValues.sunIntensity = this.environmentConfiguration.sun.intensity;
5479
+ (_c = this.sun) == null ? void 0 : _c.setIntensity(this.environmentConfiguration.sun.intensity);
5480
+ }
5481
+ if (typeof ((_e = (_d = this.environmentConfiguration) == null ? void 0 : _d.sun) == null ? void 0 : _e.azimuthalAngle) === "number") {
5482
+ sunValues.sunPosition.sunAzimuthalAngle = this.environmentConfiguration.sun.azimuthalAngle;
5483
+ (_f = this.sun) == null ? void 0 : _f.setAzimuthalAngle(this.environmentConfiguration.sun.azimuthalAngle);
5484
+ }
5485
+ if (typeof ((_h = (_g = this.environmentConfiguration) == null ? void 0 : _g.sun) == null ? void 0 : _h.polarAngle) === "number") {
5486
+ sunValues.sunPosition.sunPolarAngle = this.environmentConfiguration.sun.polarAngle;
5487
+ (_i = this.sun) == null ? void 0 : _i.setPolarAngle(this.environmentConfiguration.sun.polarAngle);
5488
+ }
5489
+ }
5490
+ updateSkyboxAndEnvValues() {
5491
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
5492
+ if (typeof ((_b = (_a = this.environmentConfiguration) == null ? void 0 : _a.envMap) == null ? void 0 : _b.intensity) === "number") {
5493
+ envValues.envMapIntensity = (_c = this.environmentConfiguration) == null ? void 0 : _c.envMap.intensity;
5494
+ }
5495
+ this.scene.environmentIntensity = envValues.envMapIntensity;
5496
+ if (typeof ((_e = (_d = this.environmentConfiguration) == null ? void 0 : _d.skybox) == null ? void 0 : _e.intensity) === "number") {
5497
+ envValues.skyboxIntensity = (_f = this.environmentConfiguration) == null ? void 0 : _f.skybox.intensity;
5498
+ }
5499
+ this.scene.backgroundIntensity = envValues.skyboxIntensity;
5500
+ if (typeof ((_h = (_g = this.environmentConfiguration) == null ? void 0 : _g.skybox) == null ? void 0 : _h.blurriness) === "number") {
5501
+ envValues.skyboxBlurriness = (_i = this.environmentConfiguration) == null ? void 0 : _i.skybox.blurriness;
5502
+ }
5503
+ this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
5504
+ if (typeof ((_k = (_j = this.environmentConfiguration) == null ? void 0 : _j.skybox) == null ? void 0 : _k.azimuthalAngle) === "number") {
5505
+ envValues.skyboxAzimuthalAngle = (_l = this.environmentConfiguration) == null ? void 0 : _l.skybox.azimuthalAngle;
5506
+ this.updateSkyboxRotation();
5507
+ }
5508
+ if (typeof ((_n = (_m = this.environmentConfiguration) == null ? void 0 : _m.skybox) == null ? void 0 : _n.polarAngle) === "number") {
5509
+ envValues.skyboxPolarAngle = (_o = this.environmentConfiguration) == null ? void 0 : _o.skybox.polarAngle;
5510
+ this.updateSkyboxRotation();
5511
+ }
5512
+ }
5513
+ updateAmbientLightValues() {
5514
+ var _a, _b;
5515
+ if (typeof ((_b = (_a = this.environmentConfiguration) == null ? void 0 : _a.ambientLight) == null ? void 0 : _b.intensity) === "number") {
5516
+ envValues.ambientLight.ambientLightIntensity = this.environmentConfiguration.ambientLight.intensity;
5517
+ this.setAmbientLight();
5518
+ }
5519
+ }
4915
5520
  };
4916
5521
 
4917
5522
  // src/time/TimeManager.ts
@@ -4969,7 +5574,7 @@ import {
4969
5574
  Box3,
4970
5575
  Color as Color8,
4971
5576
  DoubleSide,
4972
- Euler as Euler3,
5577
+ Euler as Euler4,
4973
5578
  Group as Group5,
4974
5579
  Line3 as Line32,
4975
5580
  Matrix4 as Matrix46,
@@ -4993,7 +5598,7 @@ var CollisionsManager = class {
4993
5598
  this.tempMatrix = new Matrix46();
4994
5599
  this.tempMatrix2 = new Matrix46();
4995
5600
  this.tempBox = new Box3();
4996
- this.tempEuler = new Euler3();
5601
+ this.tempEuler = new Euler4();
4997
5602
  this.tempSegment = new Line32();
4998
5603
  this.tempSegment2 = new Line32();
4999
5604
  this.collisionMeshState = /* @__PURE__ */ new Map();
@@ -5195,6 +5800,200 @@ var CollisionsManager = class {
5195
5800
  this.collisionTrigger.setCurrentCollisions(reportedCollidingElements);
5196
5801
  }
5197
5802
  };
5803
+
5804
+ // src/ground-plane/GroundPlane.ts
5805
+ import {
5806
+ CanvasTexture,
5807
+ CircleGeometry as CircleGeometry2,
5808
+ FrontSide as FrontSide2,
5809
+ Group as Group6,
5810
+ LinearMipMapLinearFilter,
5811
+ Mesh as Mesh6,
5812
+ MeshStandardMaterial as MeshStandardMaterial3,
5813
+ NearestFilter as NearestFilter2,
5814
+ RepeatWrapping as RepeatWrapping2
5815
+ } from "three";
5816
+ var canvas = document.createElement("canvas");
5817
+ canvas.width = 2;
5818
+ canvas.height = 2;
5819
+ var ctx = canvas.getContext("2d");
5820
+ ctx.fillStyle = "#e0e0e0";
5821
+ ctx.fillRect(0, 0, 2, 2);
5822
+ ctx.fillStyle = "#606060";
5823
+ ctx.fillRect(0, 0, 1, 1);
5824
+ ctx.fillRect(1, 1, 1, 1);
5825
+ var GroundPlane = class extends Group6 {
5826
+ constructor() {
5827
+ super();
5828
+ this.floorSize = 210;
5829
+ this.floorTexture = null;
5830
+ this.floorGeometry = new CircleGeometry2(this.floorSize, this.floorSize);
5831
+ this.floorMesh = null;
5832
+ this.floorMaterial = new MeshStandardMaterial3({
5833
+ color: 16777215,
5834
+ side: FrontSide2,
5835
+ metalness: 0.05,
5836
+ roughness: 0.95
5837
+ });
5838
+ this.floorMesh = new Mesh6(this.floorGeometry, this.floorMaterial);
5839
+ this.floorMesh.receiveShadow = true;
5840
+ this.floorMesh.rotation.x = Math.PI * -0.5;
5841
+ this.add(this.floorMesh);
5842
+ this.floorTexture = new CanvasTexture(canvas);
5843
+ this.floorTexture.wrapS = RepeatWrapping2;
5844
+ this.floorTexture.wrapT = RepeatWrapping2;
5845
+ this.floorTexture.magFilter = NearestFilter2;
5846
+ this.floorTexture.minFilter = LinearMipMapLinearFilter;
5847
+ this.floorTexture.repeat.set(this.floorSize / 1.5, this.floorSize / 1.5);
5848
+ this.floorMaterial.map = this.floorTexture;
5849
+ this.floorMaterial.needsUpdate = true;
5850
+ }
5851
+ };
5852
+
5853
+ // src/loading-screen/LoadingScreen.ts
5854
+ import { LoadingProgressManager as LoadingProgressManager2 } from "mml-web";
5855
+ var LoadingScreen = class {
5856
+ constructor(loadingProgressManager) {
5857
+ this.loadingProgressManager = loadingProgressManager;
5858
+ this.hasCompleted = false;
5859
+ this.element = document.createElement("div");
5860
+ this.element.style.position = "absolute";
5861
+ this.element.style.top = "0";
5862
+ this.element.style.left = "0";
5863
+ this.element.style.width = "100%";
5864
+ this.element.style.height = "100%";
5865
+ this.element.style.background = "linear-gradient(45deg, #28284B 0%, #303056 100%)";
5866
+ this.element.style.color = "white";
5867
+ this.element.addEventListener("click", (event) => {
5868
+ event.stopPropagation();
5869
+ });
5870
+ this.element.addEventListener("mousedown", (event) => {
5871
+ event.stopPropagation();
5872
+ });
5873
+ this.element.addEventListener("mousemove", (event) => {
5874
+ event.stopPropagation();
5875
+ });
5876
+ this.element.addEventListener("mouseup", (event) => {
5877
+ event.stopPropagation();
5878
+ });
5879
+ this.loadingBannerText = document.createElement("div");
5880
+ this.loadingBannerText.textContent = "Loading...";
5881
+ this.loadingBannerText.style.position = "absolute";
5882
+ this.loadingBannerText.style.display = "flex";
5883
+ this.loadingBannerText.style.top = "0";
5884
+ this.loadingBannerText.style.left = "0";
5885
+ this.loadingBannerText.style.width = "100%";
5886
+ this.loadingBannerText.style.height = "100%";
5887
+ this.loadingBannerText.style.color = "white";
5888
+ this.loadingBannerText.style.fontSize = "80px";
5889
+ this.loadingBannerText.style.fontWeight = "bold";
5890
+ this.loadingBannerText.style.fontFamily = "sans-serif";
5891
+ this.loadingBannerText.style.alignItems = "center";
5892
+ this.loadingBannerText.style.justifyContent = "center";
5893
+ this.element.append(this.loadingBannerText);
5894
+ this.progressDebugViewHolder = document.createElement("div");
5895
+ this.progressDebugViewHolder.style.display = "flex";
5896
+ this.progressDebugViewHolder.style.position = "absolute";
5897
+ this.progressDebugViewHolder.style.maxHeight = "calc(100% - 74px)";
5898
+ this.progressDebugViewHolder.style.left = "0";
5899
+ this.progressDebugViewHolder.style.bottom = "74px";
5900
+ this.progressDebugViewHolder.style.width = "100%";
5901
+ this.progressDebugViewHolder.style.justifyContent = "center";
5902
+ this.element.append(this.progressDebugViewHolder);
5903
+ this.progressDebugView = document.createElement("div");
5904
+ this.progressDebugView.style.backgroundColor = "rgba(128, 128, 128, 0.25)";
5905
+ this.progressDebugView.style.border = "1px solid black";
5906
+ this.progressDebugView.style.maxWidth = "100%";
5907
+ this.progressDebugView.style.overflow = "auto";
5908
+ this.progressDebugViewHolder.append(this.progressDebugView);
5909
+ this.debugCheckbox = document.createElement("input");
5910
+ this.debugCheckbox.type = "checkbox";
5911
+ this.debugCheckbox.checked = false;
5912
+ this.debugCheckbox.addEventListener("change", () => {
5913
+ this.progressDebugElement.style.display = this.debugCheckbox.checked ? "block" : "none";
5914
+ if (this.hasCompleted) {
5915
+ this.dispose();
5916
+ }
5917
+ });
5918
+ this.debugLabel = document.createElement("label");
5919
+ this.debugLabel.textContent = "Debug loading";
5920
+ this.debugLabel.style.fontFamily = "sans-serif";
5921
+ this.debugLabel.style.padding = "5px";
5922
+ this.debugLabel.style.display = "inline-block";
5923
+ this.debugLabel.style.userSelect = "none";
5924
+ this.debugLabel.append(this.debugCheckbox);
5925
+ this.progressDebugView.append(this.debugLabel);
5926
+ this.progressDebugElement = document.createElement("pre");
5927
+ this.progressDebugElement.style.margin = "0";
5928
+ this.progressDebugElement.style.display = this.debugCheckbox.checked ? "block" : "none";
5929
+ this.progressDebugView.append(this.progressDebugElement);
5930
+ this.progressBarHolder = document.createElement("div");
5931
+ this.progressBarHolder.style.display = "flex";
5932
+ this.progressBarHolder.style.alignItems = "center";
5933
+ this.progressBarHolder.style.justifyContent = "center";
5934
+ this.progressBarHolder.style.position = "absolute";
5935
+ this.progressBarHolder.style.bottom = "20px";
5936
+ this.progressBarHolder.style.left = "0";
5937
+ this.progressBarHolder.style.width = "100%";
5938
+ this.element.append(this.progressBarHolder);
5939
+ this.progressBarBackground = document.createElement("div");
5940
+ this.progressBarBackground.style.position = "relative";
5941
+ this.progressBarBackground.style.width = "500px";
5942
+ this.progressBarBackground.style.maxWidth = "80%";
5943
+ this.progressBarBackground.style.backgroundColor = "gray";
5944
+ this.progressBarBackground.style.height = "50px";
5945
+ this.progressBarBackground.style.lineHeight = "50px";
5946
+ this.progressBarBackground.style.borderRadius = "25px";
5947
+ this.progressBarBackground.style.border = "2px solid white";
5948
+ this.progressBarBackground.style.overflow = "hidden";
5949
+ this.progressBarHolder.append(this.progressBarBackground);
5950
+ this.progressBar = document.createElement("div");
5951
+ this.progressBar.style.position = "absolute";
5952
+ this.progressBar.style.top = "0";
5953
+ this.progressBar.style.left = "0";
5954
+ this.progressBar.style.width = "0";
5955
+ this.progressBar.style.height = "100%";
5956
+ this.progressBar.style.backgroundColor = "#0050a4";
5957
+ this.progressBarBackground.append(this.progressBar);
5958
+ this.loadingStatusText = document.createElement("div");
5959
+ this.loadingStatusText.style.position = "absolute";
5960
+ this.loadingStatusText.style.top = "0";
5961
+ this.loadingStatusText.style.left = "0";
5962
+ this.loadingStatusText.style.width = "100%";
5963
+ this.loadingStatusText.style.height = "100%";
5964
+ this.loadingStatusText.style.color = "white";
5965
+ this.loadingStatusText.style.textAlign = "center";
5966
+ this.loadingStatusText.style.verticalAlign = "middle";
5967
+ this.loadingStatusText.style.fontFamily = "sans-serif";
5968
+ this.loadingStatusText.style.fontWeight = "bold";
5969
+ this.loadingStatusText.textContent = "Loading...";
5970
+ this.progressBarBackground.append(this.loadingStatusText);
5971
+ this.loadingCallback = () => {
5972
+ const [loadingRatio, completedLoading] = this.loadingProgressManager.toRatio();
5973
+ if (completedLoading) {
5974
+ if (!this.hasCompleted) {
5975
+ this.hasCompleted = true;
5976
+ if (!this.debugCheckbox.checked) {
5977
+ this.dispose();
5978
+ }
5979
+ }
5980
+ this.loadingStatusText.textContent = "Completed";
5981
+ this.progressBar.style.width = "100%";
5982
+ } else {
5983
+ this.loadingStatusText.textContent = `Loading... ${(loadingRatio * 100).toFixed(2)}%`;
5984
+ this.progressBar.style.width = `${loadingRatio * 100}%`;
5985
+ }
5986
+ this.progressDebugElement.textContent = LoadingProgressManager2.LoadingProgressSummaryToString(
5987
+ this.loadingProgressManager.toSummary()
5988
+ );
5989
+ };
5990
+ this.loadingProgressManager.addProgressCallback(this.loadingCallback);
5991
+ }
5992
+ dispose() {
5993
+ this.loadingProgressManager.removeProgressCallback(this.loadingCallback);
5994
+ this.element.remove();
5995
+ }
5996
+ };
5198
5997
  export {
5199
5998
  AnimationState,
5200
5999
  CameraManager,
@@ -5202,11 +6001,14 @@ export {
5202
6001
  CharacterModelLoader,
5203
6002
  CollisionsManager,
5204
6003
  Composer,
6004
+ GroundPlane,
5205
6005
  KeyInputManager,
6006
+ LoadingScreen,
5206
6007
  MMLCompositionScene,
5207
6008
  Sun,
5208
6009
  TimeManager,
5209
6010
  TweakPane,
6011
+ VirtualJoystick,
5210
6012
  clamp,
5211
6013
  decodeCharacterAndCamera,
5212
6014
  ease,