@mml-io/3d-web-client-core 0.16.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
@@ -235,6 +235,132 @@ var VirtualJoystick = class _VirtualJoystick {
235
235
  }
236
236
  };
237
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
+ };
363
+
238
364
  // src/tweakpane/tweakPaneActivity.ts
239
365
  var isTweakpaneActive = false;
240
366
  function setTweakpaneActive(status) {
@@ -248,17 +374,21 @@ function getTweakpaneActive() {
248
374
  var CameraManager = class {
249
375
  constructor(targetElement, collisionsManager, initialPhi = Math.PI / 2, initialTheta = -Math.PI / 2) {
250
376
  this.collisionsManager = collisionsManager;
251
- this.initialDistance = 3.3;
252
- this.minDistance = 0.1;
253
- this.maxDistance = 8;
254
- 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;
255
388
  this.fov = this.initialFOV;
256
- this.minFOV = 85;
257
- this.maxFOV = 60;
258
389
  this.targetFOV = this.initialFOV;
259
390
  this.minPolarAngle = Math.PI * 0.25;
260
391
  this.maxPolarAngle = Math.PI * 0.95;
261
- this.dampingFactor = 0.091;
262
392
  this.targetDistance = this.initialDistance;
263
393
  this.distance = this.initialDistance;
264
394
  this.desiredDistance = this.initialDistance;
@@ -279,7 +409,7 @@ var CameraManager = class {
279
409
  this.targetPhi = initialPhi;
280
410
  this.theta = initialTheta;
281
411
  this.targetTheta = initialTheta;
282
- this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 300);
412
+ this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
283
413
  this.camera.position.set(0, 1.4, -this.initialDistance);
284
414
  this.rayCaster = new Raycaster();
285
415
  this.hasTouchControl = VirtualJoystick.checkForTouch();
@@ -287,7 +417,8 @@ var CameraManager = class {
287
417
  [targetElement, "mousedown", this.onMouseDown.bind(this)],
288
418
  [document, "mouseup", this.onMouseUp.bind(this)],
289
419
  [document, "mousemove", this.onMouseMove.bind(this)],
290
- [targetElement, "wheel", this.onMouseWheel.bind(this)]
420
+ [targetElement, "wheel", this.onMouseWheel.bind(this)],
421
+ [targetElement, "contextmenu", this.onContextMenu.bind(this)]
291
422
  ]);
292
423
  if (this.hasTouchControl) {
293
424
  this.eventHandlerCollection.add(targetElement, "touchstart", this.onTouchStart.bind(this), {
@@ -301,6 +432,9 @@ var CameraManager = class {
301
432
  });
302
433
  }
303
434
  }
435
+ setupTweakPane(tweakPane) {
436
+ tweakPane.setupCamPane(this);
437
+ }
304
438
  onTouchStart(evt) {
305
439
  Array.from(evt.touches).forEach((touch) => {
306
440
  this.dragging = true;
@@ -320,8 +454,8 @@ var CameraManager = class {
320
454
  this.lastTouchX = touch.clientX;
321
455
  this.lastTouchY = touch.clientY;
322
456
  if (this.targetTheta !== null && this.targetPhi !== null) {
323
- this.targetTheta += dx * 0.01;
324
- this.targetPhi -= dy * 0.01;
457
+ this.targetTheta += dx * this.dampingScale;
458
+ this.targetPhi -= dy * this.dampingScale;
325
459
  this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
326
460
  }
327
461
  }
@@ -334,24 +468,32 @@ var CameraManager = class {
334
468
  }
335
469
  }
336
470
  }
337
- onMouseDown() {
338
- 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
+ }
339
476
  }
340
- onMouseUp(_event) {
341
- 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
+ }
342
482
  }
343
483
  onMouseMove(event) {
344
- if (!this.dragging || getTweakpaneActive())
484
+ if (getTweakpaneActive())
345
485
  return;
346
- if (this.targetTheta === null || this.targetPhi === null)
347
- return;
348
- this.targetTheta += event.movementX * 0.01;
349
- this.targetPhi -= event.movementY * 0.01;
350
- this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
351
- 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
+ }
352
494
  }
353
495
  onMouseWheel(event) {
354
- const scrollAmount = event.deltaY * 1e-3;
496
+ const scrollAmount = event.deltaY * this.zoomScale * 0.1;
355
497
  this.targetDistance += scrollAmount;
356
498
  this.targetDistance = Math.max(
357
499
  this.minDistance,
@@ -360,6 +502,9 @@ var CameraManager = class {
360
502
  this.desiredDistance = this.targetDistance;
361
503
  event.preventDefault();
362
504
  }
505
+ onContextMenu(event) {
506
+ event.preventDefault();
507
+ }
363
508
  setTarget(target) {
364
509
  if (!this.isLerping) {
365
510
  this.target.copy(target);
@@ -390,14 +535,7 @@ var CameraManager = class {
390
535
  this.theta = this.targetTheta;
391
536
  this.distance = this.targetDistance;
392
537
  this.desiredDistance = this.targetDistance;
393
- this.targetFOV = remap(
394
- this.targetDistance,
395
- this.minDistance,
396
- this.maxDistance,
397
- this.minFOV,
398
- this.maxFOV
399
- );
400
- this.fov = this.targetFOV;
538
+ this.recomputeFoV(true);
401
539
  }
402
540
  adjustCameraPosition() {
403
541
  const offsetDistance = 0.5;
@@ -412,7 +550,7 @@ var CameraManager = class {
412
550
  this.targetDistance = cameraToPlayerDistance - firstRaycastHit[0];
413
551
  this.distance = this.targetDistance;
414
552
  } else {
415
- this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
553
+ this.targetDistance += (this.desiredDistance - this.targetDistance) * this.damping * 4;
416
554
  }
417
555
  }
418
556
  dispose() {
@@ -424,6 +562,18 @@ var CameraManager = class {
424
562
  updateAspect(aspect) {
425
563
  this.camera.aspect = aspect;
426
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
+ }
427
577
  update() {
428
578
  if (this.isLerping && this.lerpFactor < 1) {
429
579
  this.lerpFactor += 0.01 / this.lerpDuration;
@@ -433,20 +583,14 @@ var CameraManager = class {
433
583
  this.adjustCameraPosition();
434
584
  }
435
585
  if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
436
- this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
437
- this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
438
- 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;
439
589
  const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
440
590
  const y = this.target.y + this.distance * Math.cos(this.phi);
441
591
  const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
442
- this.targetFOV = remap(
443
- this.targetDistance,
444
- this.minDistance,
445
- this.maxDistance,
446
- this.minFOV,
447
- this.maxFOV
448
- );
449
- this.fov += (this.targetFOV - this.fov) * this.dampingFactor;
592
+ this.recomputeFoV();
593
+ this.fov += (this.targetFOV - this.fov) * this.damping;
450
594
  this.camera.fov = this.fov;
451
595
  this.camera.updateProjectionMatrix();
452
596
  this.camera.position.set(x, y, z);
@@ -713,7 +857,7 @@ var CharacterMaterial = class extends MeshStandardMaterial {
713
857
  }
714
858
  update() {
715
859
  if (this.config.isLocal) {
716
- this.targetAlpha = this.config.cameraManager.targetDistance < 0.4 ? 0 : 1;
860
+ this.targetAlpha = this.config.cameraManager.distance < 0.4 ? 0 : 1;
717
861
  this.currentAlpha += ease(this.targetAlpha, this.currentAlpha, 0.07);
718
862
  if (this.currentAlpha > 0.999) {
719
863
  this.currentAlpha = 1;
@@ -748,6 +892,7 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
748
892
  AnimationState2[AnimationState2["jumpToAir"] = 3] = "jumpToAir";
749
893
  AnimationState2[AnimationState2["air"] = 4] = "air";
750
894
  AnimationState2[AnimationState2["airToGround"] = 5] = "airToGround";
895
+ AnimationState2[AnimationState2["doubleJump"] = 6] = "doubleJump";
751
896
  return AnimationState2;
752
897
  })(AnimationState || {});
753
898
 
@@ -761,24 +906,35 @@ var _CharacterModel = class _CharacterModel {
761
906
  this.animations = {};
762
907
  this.animationMixer = null;
763
908
  this.currentAnimation = 0 /* idle */;
909
+ this.isPostDoubleJump = false;
764
910
  }
765
911
  async init() {
766
912
  await this.loadMainMesh();
767
913
  await this.setAnimationFromFile(
768
914
  this.config.animationConfig.idleAnimationFileUrl,
769
- 0 /* idle */
915
+ 0 /* idle */,
916
+ true
770
917
  );
771
918
  await this.setAnimationFromFile(
772
919
  this.config.animationConfig.jogAnimationFileUrl,
773
- 1 /* walking */
920
+ 1 /* walking */,
921
+ true
774
922
  );
775
923
  await this.setAnimationFromFile(
776
924
  this.config.animationConfig.sprintAnimationFileUrl,
777
- 2 /* running */
925
+ 2 /* running */,
926
+ true
778
927
  );
779
928
  await this.setAnimationFromFile(
780
929
  this.config.animationConfig.airAnimationFileUrl,
781
- 4 /* air */
930
+ 4 /* air */,
931
+ true
932
+ );
933
+ await this.setAnimationFromFile(
934
+ this.config.animationConfig.doubleJumpAnimationFileUrl,
935
+ 6 /* doubleJump */,
936
+ false,
937
+ 1.3
782
938
  );
783
939
  this.applyCustomMaterials();
784
940
  }
@@ -816,6 +972,13 @@ var _CharacterModel = class _CharacterModel {
816
972
  });
817
973
  }
818
974
  updateAnimation(targetAnimation) {
975
+ if (this.isPostDoubleJump) {
976
+ if (targetAnimation === 6 /* doubleJump */) {
977
+ targetAnimation = 4 /* air */;
978
+ } else {
979
+ this.isPostDoubleJump = false;
980
+ }
981
+ }
819
982
  if (this.currentAnimation !== targetAnimation) {
820
983
  this.transitionToAnimation(targetAnimation);
821
984
  }
@@ -906,16 +1069,21 @@ var _CharacterModel = class _CharacterModel {
906
1069
  });
907
1070
  return animationClip;
908
1071
  }
909
- async setAnimationFromFile(animationFileUrl, animationType) {
1072
+ async setAnimationFromFile(animationFileUrl, animationType, loop = true, playbackSpeed = 1) {
910
1073
  return new Promise(async (resolve, reject) => {
911
1074
  const animation = await this.config.characterModelLoader.load(animationFileUrl, "animation");
912
1075
  const cleanAnimation = this.cleanAnimationClips(this.mesh, animation);
913
1076
  if (typeof animation !== "undefined" && cleanAnimation instanceof AnimationClip) {
914
1077
  this.animations[animationType] = this.animationMixer.clipAction(cleanAnimation);
915
1078
  this.animations[animationType].stop();
1079
+ this.animations[animationType].timeScale = playbackSpeed;
916
1080
  if (animationType === 0 /* idle */) {
917
1081
  this.animations[animationType].play();
918
1082
  }
1083
+ if (!loop) {
1084
+ this.animations[animationType].setLoop(LoopRepeat, 1);
1085
+ this.animations[animationType].clampWhenFinished = true;
1086
+ }
919
1087
  resolve();
920
1088
  } else {
921
1089
  reject(`failed to load ${animationType} from ${animationFileUrl}`);
@@ -923,20 +1091,30 @@ var _CharacterModel = class _CharacterModel {
923
1091
  });
924
1092
  }
925
1093
  transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
926
- if (!this.mesh)
1094
+ if (!this.mesh) {
927
1095
  return;
1096
+ }
928
1097
  const currentAction = this.animations[this.currentAnimation];
929
1098
  this.currentAnimation = targetAnimation;
930
1099
  const targetAction = this.animations[targetAnimation];
931
- if (!targetAction)
1100
+ if (!targetAction) {
932
1101
  return;
1102
+ }
933
1103
  if (currentAction) {
934
- currentAction.enabled = true;
935
1104
  currentAction.fadeOut(transitionDuration);
936
1105
  }
937
- if (!targetAction.isRunning())
1106
+ targetAction.reset();
1107
+ if (!targetAction.isRunning()) {
938
1108
  targetAction.play();
939
- 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
+ }
940
1118
  targetAction.enabled = true;
941
1119
  targetAction.fadeIn(transitionDuration);
942
1120
  }
@@ -1348,15 +1526,189 @@ import { Euler as Euler2, Group as Group2, Quaternion as Quaternion5, Vector3 as
1348
1526
 
1349
1527
  // src/character/LocalController.ts
1350
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
1351
1711
  var downVector = new Vector35(0, -1, 0);
1352
- var airResistance = 0.5;
1353
- var groundResistance = 0.99999999;
1354
- var airControlModifier = 0.05;
1355
- var groundWalkControl = 0.75;
1356
- var groundRunControl = 1;
1357
- var baseControl = 200;
1358
- var collisionDetectionSteps = 15;
1359
- var minimumSurfaceAngle = 0.9;
1360
1712
  var LocalController = class {
1361
1713
  constructor(config) {
1362
1714
  this.config = config;
@@ -1364,12 +1716,26 @@ var LocalController = class {
1364
1716
  radius: 0.4,
1365
1717
  segment: new Line3(new Vector35(), new Vector35(0, 1.05, 0))
1366
1718
  };
1367
- this.gravity = -42;
1368
- this.jumpForce = 20;
1369
- this.coyoteTimeThreshold = 70;
1370
- this.coyoteTime = false;
1719
+ this.gravity = -characterControllerValues.gravity;
1720
+ this.jumpForce = characterControllerValues.jumpForce;
1721
+ this.doubleJumpForce = characterControllerValues.doubleJumpForce;
1722
+ this.coyoteTimeThreshold = characterControllerValues.coyoteJump;
1371
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();
1372
1736
  this.characterOnGround = false;
1737
+ this.coyoteTime = false;
1738
+ this.collisionDetectionSteps = 15;
1373
1739
  this.characterWasOnGround = false;
1374
1740
  this.characterAirborneSince = 0;
1375
1741
  this.currentHeight = 0;
@@ -1396,6 +1762,9 @@ var LocalController = class {
1396
1762
  this.surfaceTempVector5 = new Vector35();
1397
1763
  this.surfaceTempRay = new Ray();
1398
1764
  this.lastFrameSurfaceState = null;
1765
+ this.jumpPressed = false;
1766
+ // Tracks if the jump button is pressed
1767
+ this.jumpReleased = true;
1399
1768
  this.networkState = {
1400
1769
  id: this.config.id,
1401
1770
  position: { x: 0, y: 0, z: 0 },
@@ -1413,6 +1782,9 @@ var LocalController = class {
1413
1782
  this.jump = this.config.keyInputManager.jump;
1414
1783
  this.anyDirection = this.config.keyInputManager.anyDirection || ((_e = this.config.virtualJoystick) == null ? void 0 : _e.hasDirection) || false;
1415
1784
  this.conflictingDirections = this.config.keyInputManager.conflictingDirection;
1785
+ if (!this.jump) {
1786
+ this.jumpReleased = true;
1787
+ }
1416
1788
  }
1417
1789
  update() {
1418
1790
  this.updateControllerState();
@@ -1431,14 +1803,14 @@ var LocalController = class {
1431
1803
  if (this.anyDirection) {
1432
1804
  this.updateRotation();
1433
1805
  }
1434
- for (let i = 0; i < collisionDetectionSteps; i++) {
1806
+ for (let i = 0; i < this.collisionDetectionSteps; i++) {
1435
1807
  this.updatePosition(
1436
1808
  this.config.timeManager.deltaTime,
1437
- this.config.timeManager.deltaTime / collisionDetectionSteps,
1809
+ this.config.timeManager.deltaTime / this.collisionDetectionSteps,
1438
1810
  i
1439
1811
  );
1440
1812
  }
1441
- if (this.config.character.position.y < 0) {
1813
+ if (this.config.character.position.y < -100) {
1442
1814
  this.resetPosition();
1443
1815
  }
1444
1816
  this.updateNetworkState();
@@ -1448,6 +1820,9 @@ var LocalController = class {
1448
1820
  return 0 /* idle */;
1449
1821
  const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
1450
1822
  if (this.currentHeight > jumpHeight && !this.characterOnGround) {
1823
+ if (this.doubleJumpUsed) {
1824
+ return 6 /* doubleJump */;
1825
+ }
1451
1826
  return 4 /* air */;
1452
1827
  }
1453
1828
  if (this.conflictingDirections) {
@@ -1507,31 +1882,59 @@ var LocalController = class {
1507
1882
  const frameRotation = angularSpeed * this.config.timeManager.deltaTime;
1508
1883
  this.config.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1509
1884
  }
1510
- applyControls(deltaTime) {
1511
- const resistance = this.characterOnGround ? groundResistance : airResistance;
1512
- const speedFactor = Math.pow(1 - resistance, deltaTime);
1513
- this.characterVelocity.multiplyScalar(speedFactor);
1514
- const acceleration = this.tempVector.set(0, 0, 0);
1885
+ processJump(currentAcceleration, deltaTime) {
1515
1886
  if (this.characterOnGround) {
1887
+ this.coyoteJumped = false;
1888
+ this.canDoubleJump = false;
1889
+ this.doubleJumpUsed = false;
1890
+ this.jumpCounter = 0;
1516
1891
  if (!this.jump) {
1892
+ this.canDoubleJump = !this.doubleJumpUsed && this.jumpReleased && this.jumpCounter === 1;
1517
1893
  this.canJump = true;
1894
+ this.jumpReleased = true;
1518
1895
  }
1519
- if (this.jump && this.canJump) {
1520
- acceleration.y += this.jumpForce / deltaTime;
1896
+ if (this.jump && this.canJump && this.jumpReleased) {
1897
+ currentAcceleration.y += this.jumpForce / deltaTime;
1521
1898
  this.canJump = false;
1899
+ this.jumpReleased = false;
1900
+ this.jumpCounter++;
1522
1901
  } else {
1523
- if (this.currentSurfaceAngle.y < minimumSurfaceAngle) {
1524
- acceleration.y += this.gravity;
1902
+ if (this.currentSurfaceAngle.y < this.minimumSurfaceAngle) {
1903
+ currentAcceleration.y += this.gravity;
1525
1904
  }
1526
1905
  }
1527
- } else if (this.jump && this.coyoteTime) {
1528
- acceleration.y += this.jumpForce / deltaTime;
1529
- this.canJump = false;
1530
1906
  } else {
1531
- acceleration.y += this.gravity;
1532
- 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
+ }
1533
1928
  }
1534
- 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;
1535
1938
  const controlAcceleration = this.tempVector2.set(0, 0, 0);
1536
1939
  if (!this.conflictingDirections) {
1537
1940
  if (this.forward) {
@@ -1584,10 +1987,18 @@ var LocalController = class {
1584
1987
  this.config.character.position.copy(avatarSegment.start);
1585
1988
  const deltaCollisionPosition = avatarSegment.start.sub(positionBeforeCollisions);
1586
1989
  this.characterOnGround = deltaCollisionPosition.y > 0;
1990
+ if (this.characterOnGround) {
1991
+ this.doubleJumpUsed = false;
1992
+ this.jumpCounter = 0;
1993
+ }
1587
1994
  if (this.characterWasOnGround && !this.characterOnGround) {
1588
1995
  this.characterAirborneSince = Date.now();
1589
1996
  }
1997
+ if (!this.jump) {
1998
+ this.jumpReleased = true;
1999
+ }
1590
2000
  this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
2001
+ this.latestPosition = this.config.character.position.clone();
1591
2002
  this.characterWasOnGround = this.characterOnGround;
1592
2003
  }
1593
2004
  getMovementFromSurfaces(userPosition, deltaTime) {
@@ -1672,6 +2083,9 @@ var LocalController = class {
1672
2083
  this.characterVelocity.y = 0;
1673
2084
  this.config.character.position.y = 3;
1674
2085
  this.characterOnGround = false;
2086
+ this.doubleJumpUsed = false;
2087
+ this.jumpReleased = true;
2088
+ this.jumpCounter = 0;
1675
2089
  }
1676
2090
  };
1677
2091
 
@@ -1743,7 +2157,6 @@ function decodeCharacterAndCamera(hash) {
1743
2157
  var CharacterManager = class {
1744
2158
  constructor(config) {
1745
2159
  this.config = config;
1746
- this.updateLocationHash = true;
1747
2160
  this.headTargetOffset = new Vector38(0, 1.3, 0);
1748
2161
  this.localClientId = 0;
1749
2162
  this.remoteCharacters = /* @__PURE__ */ new Map();
@@ -1793,6 +2206,9 @@ var CharacterManager = class {
1793
2206
  this.group.add(character);
1794
2207
  this.localCharacterSpawned = true;
1795
2208
  }
2209
+ setupTweakPane(tweakPane) {
2210
+ tweakPane.setupCharacterController(this.localController);
2211
+ }
1796
2212
  spawnRemoteCharacter(id, username, characterDescription, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1797
2213
  const character = new Character({
1798
2214
  username,
@@ -1900,11 +2316,14 @@ var CharacterManager = class {
1900
2316
  this.remoteCharacterControllers.delete(id);
1901
2317
  }
1902
2318
  }
1903
- if (this.updateLocationHash && this.config.timeManager.frame % 60 === 0) {
1904
- window.location.hash = encodeCharacterAndCamera(
2319
+ if (this.config.updateURLLocation && this.config.timeManager.frame % 60 === 0 && document.hasFocus()) {
2320
+ const hash = encodeCharacterAndCamera(
1905
2321
  this.localCharacter,
1906
2322
  this.config.cameraManager.camera
1907
2323
  );
2324
+ const url = new URL(window.location.href);
2325
+ url.hash = hash;
2326
+ window.history.replaceState({}, "", url);
1908
2327
  }
1909
2328
  }
1910
2329
  }
@@ -2199,6 +2618,11 @@ var sunOptions = {
2199
2618
  sunIntensity: { min: 0, max: 10, step: 0.1 }
2200
2619
  };
2201
2620
  var envValues = {
2621
+ skyboxAzimuthalAngle: 0,
2622
+ skyboxPolarAngle: 0,
2623
+ envMapIntensity: 0.07,
2624
+ skyboxIntensity: 0.8,
2625
+ skyboxBlurriness: 0,
2202
2626
  ambientLight: {
2203
2627
  ambientLightIntensity: 0.27,
2204
2628
  ambientLightColor: { r: 1, g: 1, b: 1 }
@@ -2210,6 +2634,10 @@ var envValues = {
2210
2634
  }
2211
2635
  };
2212
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 },
2213
2641
  ambientLight: {
2214
2642
  ambientLightIntensity: { min: 0, max: 1, step: 0.01 }
2215
2643
  },
@@ -2222,6 +2650,9 @@ var EnvironmentFolder = class {
2222
2650
  constructor(parentFolder, expand = false) {
2223
2651
  this.folder = parentFolder.addFolder({ title: "environment", expanded: expand });
2224
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 });
2225
2656
  this.ambient = this.folder.addFolder({ title: "ambient", expanded: true });
2226
2657
  this.sun.addBinding(
2227
2658
  sunValues.sunPosition,
@@ -2237,7 +2668,12 @@ var EnvironmentFolder = class {
2237
2668
  this.sun.addBinding(sunValues, "sunColor", {
2238
2669
  color: { type: "float" }
2239
2670
  });
2240
- 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);
2241
2677
  this.ambient.addBinding(
2242
2678
  envValues.ambientLight,
2243
2679
  "ambientLightIntensity",
@@ -2246,13 +2682,13 @@ var EnvironmentFolder = class {
2246
2682
  this.ambient.addBinding(envValues.ambientLight, "ambientLightColor", {
2247
2683
  color: { type: "float" }
2248
2684
  });
2249
- this.ambient.addBinding(envValues.fog, "fogNear", envOptions.fog.fogNear);
2250
- this.ambient.addBinding(envValues.fog, "fogFar", envOptions.fog.fogFar);
2251
- 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", {
2252
2688
  color: { type: "float" }
2253
2689
  });
2254
2690
  }
2255
- setupChangeEvent(setHDR, setAmbientLight, setFog, sun) {
2691
+ setupChangeEvent(scene, setHDR, setSkyboxAzimuthalAngle, setSkyboxPolarAngle, setAmbientLight, setFog, sun) {
2256
2692
  this.sun.on("change", (e) => {
2257
2693
  const target = e.target.key;
2258
2694
  if (!target)
@@ -2287,9 +2723,43 @@ var EnvironmentFolder = class {
2287
2723
  break;
2288
2724
  }
2289
2725
  });
2290
- this.sunButton.on("click", () => {
2726
+ this.hdrButton.on("click", () => {
2291
2727
  setHDR();
2292
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
+ });
2293
2763
  this.ambient.on("change", (e) => {
2294
2764
  const target = e.target.key;
2295
2765
  if (!target)
@@ -2310,6 +2780,13 @@ var EnvironmentFolder = class {
2310
2780
  setAmbientLight();
2311
2781
  break;
2312
2782
  }
2783
+ }
2784
+ });
2785
+ this.fog.on("change", (e) => {
2786
+ const target = e.target.key;
2787
+ if (!target)
2788
+ return;
2789
+ switch (target) {
2313
2790
  case "fogNear": {
2314
2791
  envValues.fog.fogNear = e.value;
2315
2792
  setFog();
@@ -2330,8 +2807,6 @@ var EnvironmentFolder = class {
2330
2807
  setFog();
2331
2808
  break;
2332
2809
  }
2333
- default:
2334
- break;
2335
2810
  }
2336
2811
  });
2337
2812
  }
@@ -2340,7 +2815,7 @@ var EnvironmentFolder = class {
2340
2815
  // src/tweakpane/blades/postExtrasFolder.ts
2341
2816
  var extrasValues = {
2342
2817
  grain: 0.045,
2343
- bloom: 0.75
2818
+ bloom: 0.15
2344
2819
  };
2345
2820
  var extrasOptions = {
2346
2821
  grain: {
@@ -2379,16 +2854,12 @@ var PostExtrasFolder = class {
2379
2854
  var rendererValues = {
2380
2855
  shadowMap: 2,
2381
2856
  toneMapping: 5,
2382
- exposure: 1.7,
2383
- bgIntensity: 0.8,
2384
- bgBlurriness: 0
2857
+ exposure: 1.7
2385
2858
  };
2386
2859
  var rendererOptions = {
2387
2860
  shadowMap: { min: 0, max: 2, step: 1 },
2388
2861
  toneMapping: { min: 0, max: 5, step: 1 },
2389
- exposure: { min: 0, max: 3, step: 0.01 },
2390
- bgIntensity: { min: 0, max: 1.3, step: 0.01 },
2391
- bgBlurriness: { min: 0, max: 0.1, step: 1e-3 }
2862
+ exposure: { min: 0, max: 3, step: 0.01 }
2392
2863
  };
2393
2864
  var shadowMapTypes = {
2394
2865
  0: "BasicShadowMap",
@@ -2426,10 +2897,8 @@ var RendererFolder = class {
2426
2897
  this.folder.addBinding(rendererValues, "toneMapping", rendererOptions.toneMapping);
2427
2898
  this.folder.addBinding(monitoredValues, "toneMappingType", { readonly: true });
2428
2899
  this.folder.addBinding(rendererValues, "exposure", rendererOptions.exposure);
2429
- this.folder.addBinding(rendererValues, "bgIntensity", rendererOptions.bgIntensity);
2430
- this.folder.addBinding(rendererValues, "bgBlurriness", rendererOptions.bgBlurriness);
2431
2900
  }
2432
- setupChangeEvent(scene, renderer, toneMappingFolder, toneMappingPass) {
2901
+ setupChangeEvent(renderer, toneMappingFolder, toneMappingPass) {
2433
2902
  this.folder.on("change", (e) => {
2434
2903
  const target = e.target.key;
2435
2904
  if (!target)
@@ -2450,12 +2919,6 @@ var RendererFolder = class {
2450
2919
  case "exposure":
2451
2920
  renderer.toneMappingExposure = e.value;
2452
2921
  break;
2453
- case "bgIntensity":
2454
- scene.backgroundIntensity = e.value;
2455
- break;
2456
- case "bgBlurriness":
2457
- scene.backgroundBlurriness = e.value;
2458
- break;
2459
2922
  default:
2460
2923
  break;
2461
2924
  }
@@ -2544,7 +3007,7 @@ var n8ssaoValues = {
2544
3007
  halfRes: true,
2545
3008
  aoRadius: 5,
2546
3009
  distanceFalloff: 3,
2547
- intensity: 1,
3010
+ intensity: 1.5,
2548
3011
  color: { r: 0, g: 0, b: 0 },
2549
3012
  aoSamples: 16,
2550
3013
  denoiseSamples: 4,
@@ -2968,11 +3431,14 @@ var TweakPane = class {
2968
3431
  this.postExtrasFolder = new PostExtrasFolder(this.gui, false);
2969
3432
  this.character = new CharacterFolder(this.gui, false);
2970
3433
  this.environment = new EnvironmentFolder(this.gui, false);
3434
+ this.camera = new CameraFolder(this.gui, false);
3435
+ this.characterControls = new CharacterControlsFolder(this.gui, false);
2971
3436
  this.toneMappingFolder.folder.hidden = rendererValues.toneMapping === 5 ? false : true;
2972
3437
  this.export = this.gui.addFolder({ title: "import / export", expanded: false });
2973
- window.addEventListener("keydown", this.processKey.bind(this));
2974
- this.setupRenderPane = this.setupRenderPane.bind(this);
2975
- this.setupGUIListeners.bind(this)();
3438
+ window.addEventListener("keydown", (e) => {
3439
+ this.processKey(e);
3440
+ });
3441
+ this.setupGUIListeners();
2976
3442
  }
2977
3443
  setupGUIListeners() {
2978
3444
  const gui = this.gui;
@@ -2987,9 +3453,8 @@ var TweakPane = class {
2987
3453
  if (e.key === "p")
2988
3454
  this.toggleGUI();
2989
3455
  }
2990
- 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) {
2991
3457
  this.rendererFolder.setupChangeEvent(
2992
- this.scene,
2993
3458
  this.renderer,
2994
3459
  this.toneMappingFolder.folder,
2995
3460
  toneMappingPass
@@ -2998,7 +3463,15 @@ var TweakPane = class {
2998
3463
  this.ssaoFolder.setupChangeEvent(composer, normalPass, ppssaoEffect, ppssaoPass, n8aopass);
2999
3464
  this.bcsFolder.setupChangeEvent(brightnessContrastSaturation);
3000
3465
  this.postExtrasFolder.setupChangeEvent(bloomEffect, gaussGrainEffect);
3001
- 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
+ );
3002
3475
  this.environment.folder.hidden = hasLighting === false || sun === null;
3003
3476
  const exportButton = this.export.addButton({ title: "export" });
3004
3477
  exportButton.on("click", () => {
@@ -3011,9 +3484,21 @@ var TweakPane = class {
3011
3484
  });
3012
3485
  });
3013
3486
  }
3487
+ setupCamPane(cameraManager) {
3488
+ this.camera.setupChangeEvent(cameraManager);
3489
+ }
3490
+ setupCharacterController(localController) {
3491
+ this.characterControls.setupChangeEvent(localController);
3492
+ }
3014
3493
  updateStats(timeManager) {
3015
3494
  this.renderStatsFolder.update(this.renderer, this.composer, timeManager);
3016
3495
  }
3496
+ updateCameraData(cameraManager) {
3497
+ this.camera.update(cameraManager);
3498
+ }
3499
+ updateCharacterData(localController) {
3500
+ this.characterControls.update(localController);
3501
+ }
3017
3502
  formatDateForFilename() {
3018
3503
  const date = /* @__PURE__ */ new Date();
3019
3504
  const year = date.getFullYear();
@@ -3071,35 +3556,37 @@ var TweakPane = class {
3071
3556
  // src/rendering/composer.ts
3072
3557
  import { HDRJPGLoader } from "@monogrid/gainmap-js";
3073
3558
  import {
3559
+ BlendFunction as BlendFunction2,
3560
+ BloomEffect,
3561
+ EdgeDetectionMode,
3074
3562
  EffectComposer as EffectComposer2,
3075
- RenderPass,
3076
3563
  EffectPass as EffectPass2,
3077
3564
  FXAAEffect,
3565
+ NormalPass as NormalPass2,
3566
+ PredicationMode,
3567
+ RenderPass,
3078
3568
  ShaderPass,
3079
- BloomEffect,
3080
- SSAOEffect as SSAOEffect2,
3081
- BlendFunction as BlendFunction2,
3082
- TextureEffect,
3083
- ToneMappingEffect,
3084
3569
  SMAAEffect,
3085
3570
  SMAAPreset,
3086
- EdgeDetectionMode,
3087
- PredicationMode,
3088
- NormalPass as NormalPass2
3571
+ SSAOEffect as SSAOEffect2,
3572
+ TextureEffect,
3573
+ ToneMappingEffect
3089
3574
  } from "postprocessing";
3090
3575
  import {
3091
3576
  AmbientLight,
3092
3577
  Color as Color7,
3578
+ EquirectangularReflectionMapping,
3579
+ Euler as Euler3,
3093
3580
  Fog as Fog2,
3094
3581
  HalfFloatType as HalfFloatType2,
3095
3582
  LinearSRGBColorSpace,
3096
3583
  LoadingManager,
3584
+ MathUtils,
3097
3585
  PMREMGenerator,
3098
- SRGBColorSpace,
3099
3586
  Scene as Scene4,
3587
+ SRGBColorSpace,
3100
3588
  Vector2 as Vector27,
3101
- WebGLRenderer as WebGLRenderer4,
3102
- EquirectangularReflectionMapping
3589
+ WebGLRenderer as WebGLRenderer4
3103
3590
  } from "three";
3104
3591
  import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
3105
3592
 
@@ -4642,7 +5129,12 @@ var N8SSAOPass = class extends Pass {
4642
5129
 
4643
5130
  // src/rendering/composer.ts
4644
5131
  var Composer = class {
4645
- constructor(scene, camera, spawnSun = false) {
5132
+ constructor({
5133
+ scene,
5134
+ camera,
5135
+ spawnSun = false,
5136
+ environmentConfiguration
5137
+ }) {
4646
5138
  this.width = 1;
4647
5139
  this.height = 1;
4648
5140
  this.resolution = new Vector27(this.width, this.height);
@@ -4651,6 +5143,7 @@ var Composer = class {
4651
5143
  this.gaussGrainEffect = GaussGrainEffect;
4652
5144
  this.ambientLight = null;
4653
5145
  this.sun = null;
5146
+ var _a;
4654
5147
  this.scene = scene;
4655
5148
  this.postPostScene = new Scene4();
4656
5149
  this.camera = camera;
@@ -4666,7 +5159,9 @@ var Composer = class {
4666
5159
  this.renderer.shadowMap.type = rendererValues.shadowMap;
4667
5160
  this.renderer.toneMapping = rendererValues.toneMapping;
4668
5161
  this.renderer.toneMappingExposure = rendererValues.exposure;
4669
- this.setAmbientLight();
5162
+ this.environmentConfiguration = environmentConfiguration;
5163
+ this.updateSkyboxAndEnvValues();
5164
+ this.updateAmbientLightValues();
4670
5165
  this.setFog();
4671
5166
  this.effectComposer = new EffectComposer2(this.renderer, {
4672
5167
  frameBufferType: HalfFloatType2
@@ -4699,6 +5194,9 @@ var Composer = class {
4699
5194
  this.ppssaoPass = new EffectPass2(this.camera, this.ppssaoEffect, this.normalTextureEffect);
4700
5195
  this.ppssaoPass.enabled = ppssaoValues.enabled;
4701
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
+ }
4702
5200
  this.bloomEffect = new BloomEffect({
4703
5201
  intensity: extrasValues.bloom
4704
5202
  });
@@ -4758,6 +5256,7 @@ var Composer = class {
4758
5256
  this.sun = new Sun();
4759
5257
  this.scene.add(this.sun);
4760
5258
  }
5259
+ this.updateSunValues();
4761
5260
  this.resizeListener = () => {
4762
5261
  this.fitContainer();
4763
5262
  };
@@ -4779,6 +5278,14 @@ var Composer = class {
4779
5278
  this.spawnSun,
4780
5279
  this.sun,
4781
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
+ },
4782
5289
  this.setAmbientLight.bind(this),
4783
5290
  this.setFog.bind(this)
4784
5291
  );
@@ -4833,6 +5340,18 @@ var Composer = class {
4833
5340
  this.renderer.clearDepth();
4834
5341
  this.renderer.render(this.postPostScene, this.camera);
4835
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
+ }
4836
5355
  useHDRJPG(url, fromFile = false) {
4837
5356
  const pmremGenerator = new PMREMGenerator(this.renderer);
4838
5357
  const hdrJpg = new HDRJPGLoader(this.renderer).load(url, () => {
@@ -4843,8 +5362,21 @@ var Composer = class {
4843
5362
  if (envMap) {
4844
5363
  envMap.colorSpace = LinearSRGBColorSpace;
4845
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
+ );
4846
5372
  this.scene.background = envMap;
4847
- this.scene.backgroundIntensity = rendererValues.bgIntensity;
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
+ );
4848
5380
  this.isEnvHDRI = true;
4849
5381
  hdrJpgEquirectangularMap.dispose();
4850
5382
  pmremGenerator.dispose();
@@ -4853,8 +5385,9 @@ var Composer = class {
4853
5385
  });
4854
5386
  }
4855
5387
  useHDRI(url, fromFile = false) {
4856
- if (this.isEnvHDRI && fromFile === false || !this.renderer)
5388
+ if (this.isEnvHDRI && fromFile === false || !this.renderer) {
4857
5389
  return;
5390
+ }
4858
5391
  const pmremGenerator = new PMREMGenerator(this.renderer);
4859
5392
  new RGBELoader(new LoadingManager()).load(
4860
5393
  url,
@@ -4863,8 +5396,16 @@ var Composer = class {
4863
5396
  if (envMap) {
4864
5397
  envMap.colorSpace = LinearSRGBColorSpace;
4865
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
+ );
4866
5406
  this.scene.background = envMap;
4867
- this.scene.backgroundIntensity = rendererValues.bgIntensity;
5407
+ this.scene.backgroundIntensity = envValues.skyboxIntensity;
5408
+ this.scene.backgroundBlurriness = envValues.skyboxBlurriness;
4868
5409
  this.isEnvHDRI = true;
4869
5410
  texture.dispose();
4870
5411
  pmremGenerator.dispose();
@@ -4931,6 +5472,51 @@ var Composer = class {
4931
5472
  );
4932
5473
  this.scene.add(this.ambientLight);
4933
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
+ }
4934
5520
  };
4935
5521
 
4936
5522
  // src/time/TimeManager.ts
@@ -4988,7 +5574,7 @@ import {
4988
5574
  Box3,
4989
5575
  Color as Color8,
4990
5576
  DoubleSide,
4991
- Euler as Euler3,
5577
+ Euler as Euler4,
4992
5578
  Group as Group5,
4993
5579
  Line3 as Line32,
4994
5580
  Matrix4 as Matrix46,
@@ -4996,7 +5582,7 @@ import {
4996
5582
  MeshBasicMaterial as MeshBasicMaterial3,
4997
5583
  Quaternion as Quaternion6,
4998
5584
  Ray as Ray2,
4999
- Vector3 as Vector315
5585
+ Vector3 as Vector314
5000
5586
  } from "three";
5001
5587
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
5002
5588
  import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
@@ -5004,15 +5590,15 @@ import { MeshBVH, MeshBVHHelper } from "three-mesh-bvh";
5004
5590
  var CollisionsManager = class {
5005
5591
  constructor(scene) {
5006
5592
  this.debug = false;
5007
- this.tempVector = new Vector315();
5008
- this.tempVector2 = new Vector315();
5009
- this.tempVector3 = new Vector315();
5593
+ this.tempVector = new Vector314();
5594
+ this.tempVector2 = new Vector314();
5595
+ this.tempVector3 = new Vector314();
5010
5596
  this.tempQuaternion = new Quaternion6();
5011
5597
  this.tempRay = new Ray2();
5012
5598
  this.tempMatrix = new Matrix46();
5013
5599
  this.tempMatrix2 = new Matrix46();
5014
5600
  this.tempBox = new Box3();
5015
- this.tempEuler = new Euler3();
5601
+ this.tempEuler = new Euler4();
5016
5602
  this.tempSegment = new Line32();
5017
5603
  this.tempSegment2 = new Line32();
5018
5604
  this.collisionMeshState = /* @__PURE__ */ new Map();
@@ -5022,7 +5608,7 @@ var CollisionsManager = class {
5022
5608
  raycastFirst(ray) {
5023
5609
  let minimumDistance = null;
5024
5610
  let minimumHit = null;
5025
- let minimumNormal = new Vector315();
5611
+ let minimumNormal = new Vector314();
5026
5612
  for (const [, collisionMeshState] of this.collisionMeshState) {
5027
5613
  this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(collisionMeshState.matrix).invert());
5028
5614
  const hit = collisionMeshState.meshBVH.raycastFirst(this.tempRay, DoubleSide);
@@ -5163,7 +5749,7 @@ var CollisionsManager = class {
5163
5749
  const realDistance = intersectionSegment.distance();
5164
5750
  if (realDistance < capsuleRadius) {
5165
5751
  if (!collisionPosition) {
5166
- collisionPosition = new Vector315().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
5752
+ collisionPosition = new Vector314().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
5167
5753
  }
5168
5754
  const ratio = realDistance / modelReferenceDistance;
5169
5755
  const realDepth = capsuleRadius - realDistance;