@mml-io/3d-web-client-core 0.1.0 → 0.3.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
@@ -6,7 +6,7 @@ var __publicField = (obj, key, value) => {
6
6
  };
7
7
 
8
8
  // src/camera/CameraManager.ts
9
- import { PerspectiveCamera, Vector3 as Vector32 } from "three";
9
+ import { PerspectiveCamera, Raycaster, Vector3 as Vector32 } from "three";
10
10
 
11
11
  // src/helpers/math-helpers.ts
12
12
  import { Vector3 } from "three";
@@ -27,25 +27,51 @@ var round = (n, digits) => {
27
27
  var ease = (target, n, factor) => {
28
28
  return round((target - n) * factor, 5);
29
29
  };
30
+ function clamp(value, min, max) {
31
+ return Math.min(Math.max(value, min), max);
32
+ }
33
+ var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
34
+ return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
35
+ };
36
+
37
+ // src/tweakpane/tweakPaneActivity.ts
38
+ var isTweakpaneActive = false;
39
+ function setTweakpaneActive(status) {
40
+ isTweakpaneActive = status;
41
+ }
42
+ function getTweakpaneActive() {
43
+ return isTweakpaneActive;
44
+ }
30
45
 
31
46
  // src/camera/CameraManager.ts
32
47
  var CameraManager = class {
33
- constructor() {
48
+ constructor(collisionsManager) {
49
+ this.collisionsManager = collisionsManager;
34
50
  __publicField(this, "camera");
35
- __publicField(this, "dragging", false);
36
- __publicField(this, "target", new Vector32(0, 1.55, 0));
37
- __publicField(this, "targetDistance");
38
- __publicField(this, "maxTargetDistance", 20);
39
- __publicField(this, "distance");
51
+ __publicField(this, "initialDistance", 2.5);
52
+ __publicField(this, "minDistance", 0.1);
53
+ __publicField(this, "maxDistance", 6);
54
+ __publicField(this, "initialFOV", 80);
55
+ __publicField(this, "fov", this.initialFOV);
56
+ __publicField(this, "minFOV", 63);
57
+ __publicField(this, "maxFOV", 85);
58
+ __publicField(this, "targetFOV", this.initialFOV);
59
+ __publicField(this, "minPolarAngle", Math.PI * 0.25);
60
+ __publicField(this, "maxPolarAngle", Math.PI * 0.95);
61
+ __publicField(this, "dampingFactor", 0.091);
62
+ __publicField(this, "targetDistance", this.initialDistance);
63
+ __publicField(this, "distance", this.initialDistance);
40
64
  __publicField(this, "targetPhi", Math.PI / 2);
41
65
  __publicField(this, "phi", Math.PI / 2);
42
66
  __publicField(this, "targetTheta", -Math.PI / 2);
43
67
  __publicField(this, "theta", -Math.PI / 2);
68
+ __publicField(this, "dragging", false);
69
+ __publicField(this, "target", new Vector32(0, 1.55, 0));
44
70
  __publicField(this, "hadTarget", false);
45
- this.camera = new PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 2e3);
46
- this.camera.position.set(0, 1.4, 3);
47
- this.targetDistance = 2.5;
48
- this.distance = this.targetDistance;
71
+ __publicField(this, "rayCaster");
72
+ this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
73
+ this.camera.position.set(0, 1.4, -this.initialDistance);
74
+ this.rayCaster = new Raycaster();
49
75
  document.addEventListener("mousedown", this.onMouseDown.bind(this));
50
76
  document.addEventListener("mouseup", this.onMouseUp.bind(this));
51
77
  document.addEventListener("mousemove", this.onMouseMove.bind(this));
@@ -65,22 +91,21 @@ var CameraManager = class {
65
91
  this.dragging = false;
66
92
  }
67
93
  onMouseMove(event) {
68
- if (!this.dragging) {
94
+ if (!this.dragging || getTweakpaneActive() === true)
69
95
  return;
70
- }
71
- if (this.targetTheta === null || this.targetPhi === null) {
96
+ if (this.targetTheta === null || this.targetPhi === null)
72
97
  return;
73
- }
74
98
  this.targetTheta += event.movementX * 0.01;
75
99
  this.targetPhi -= event.movementY * 0.01;
76
- this.targetPhi = Math.max(Math.PI * 0.1, Math.min(Math.PI - Math.PI * 0.1, this.targetPhi));
77
- this.targetPhi = Math.min(Math.PI * 0.7, this.targetPhi);
100
+ this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
78
101
  }
79
102
  onMouseWheel(event) {
80
- const scrollAmount = event.deltaY * 0.01;
103
+ const scrollAmount = event.deltaY * 1e-3;
81
104
  this.targetDistance += scrollAmount;
82
- this.targetDistance = Math.max(0, this.targetDistance);
83
- this.targetDistance = Math.min(this.targetDistance, this.maxTargetDistance);
105
+ this.targetDistance = Math.max(
106
+ this.minDistance,
107
+ Math.min(this.maxDistance, this.targetDistance)
108
+ );
84
109
  }
85
110
  setTarget(target) {
86
111
  this.target.copy(target);
@@ -102,19 +127,41 @@ var CameraManager = class {
102
127
  this.theta = this.targetTheta;
103
128
  this.distance = this.targetDistance;
104
129
  }
130
+ adjustCameraPosition() {
131
+ this.rayCaster.set(
132
+ this.camera.position,
133
+ this.target.clone().sub(this.camera.position).normalize()
134
+ );
135
+ const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
136
+ const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
137
+ if (minimumDistance !== null && minimumDistance <= cameraToPlayerDistance) {
138
+ this.targetDistance = cameraToPlayerDistance - minimumDistance;
139
+ this.distance = this.targetDistance;
140
+ }
141
+ }
105
142
  update() {
106
- if (this.target === null) {
143
+ if (this.target === null)
107
144
  return;
108
- }
109
145
  if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
110
- this.distance += ease(this.targetDistance, this.distance, 0.02);
111
- this.distance = Math.min(this.distance, this.maxTargetDistance);
112
- this.phi += ease(this.targetPhi, this.phi, 0.07);
113
- this.theta += ease(this.targetTheta, this.theta, 0.07);
146
+ this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
147
+ this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
148
+ this.theta += (this.targetTheta - this.theta) * this.dampingFactor;
114
149
  const x = this.target.x + this.distance * Math.sin(this.phi) * Math.cos(this.theta);
115
150
  const y = this.target.y + this.distance * Math.cos(this.phi);
116
151
  const z = this.target.z + this.distance * Math.sin(this.phi) * Math.sin(this.theta);
117
- this.camera.position.set(x, y, z);
152
+ this.targetFOV = remap(
153
+ this.targetDistance,
154
+ this.minDistance,
155
+ this.maxDistance,
156
+ this.minFOV,
157
+ this.maxFOV
158
+ );
159
+ this.fov += ease(this.targetFOV, this.fov, 0.07);
160
+ this.camera.fov = this.fov;
161
+ this.camera.updateProjectionMatrix();
162
+ this.camera.updateMatrixWorld();
163
+ this.camera.position.set(x, clamp(y, 0.1, Infinity), z);
164
+ this.adjustCameraPosition();
118
165
  this.camera.lookAt(this.target);
119
166
  }
120
167
  }
@@ -225,6 +272,41 @@ ${before}
225
272
  );
226
273
  }
227
274
 
275
+ // src/tweakpane/characterSettings.ts
276
+ var characterValues = {
277
+ material: {
278
+ transmission: 1,
279
+ metalness: 0.8,
280
+ roughness: 0.12,
281
+ ior: 1.5,
282
+ thickness: 0.1,
283
+ specularColor: { r: 1, g: 1, b: 1 },
284
+ specularIntensity: 0.1,
285
+ emissive: { r: 1, g: 1, b: 1 },
286
+ emissiveIntensity: 0.1,
287
+ envMapIntensity: 1,
288
+ sheenColor: { r: 1, g: 1, b: 1 },
289
+ sheen: 0.5,
290
+ clearcoat: 0,
291
+ clearcoatRoughness: 0
292
+ }
293
+ };
294
+ var characterOptions = {
295
+ material: {
296
+ transmission: { min: 0.01, max: 3, step: 0.01 },
297
+ metalness: { min: 0, max: 1, step: 0.01 },
298
+ roughness: { min: 0, max: 1, step: 0.01 },
299
+ ior: { min: 0, max: 5, step: 0.01 },
300
+ thickness: { min: 0, max: 1, step: 0.01 },
301
+ specularIntensity: { min: 0, max: 1, step: 0.01 },
302
+ emissiveIntensity: { min: 0, max: 1, step: 0.01 },
303
+ envMapIntensity: { min: 0, max: 1, step: 0.01 },
304
+ sheen: { min: 0, max: 1, step: 0.01 },
305
+ clearcoat: { min: 0, max: 1, step: 0.01 },
306
+ clearcoatRoughness: { min: 0, max: 1, step: 0.01 }
307
+ }
308
+ };
309
+
228
310
  // src/character/CharacterMaterial.ts
229
311
  var CharacterMaterial = class extends MeshPhysicalMaterial {
230
312
  constructor() {
@@ -232,16 +314,32 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
232
314
  __publicField(this, "uniforms", {});
233
315
  __publicField(this, "colorsCube216", []);
234
316
  this.color = new Color(16777215);
235
- this.transmission = 0.5;
236
- this.metalness = 0.5;
237
- this.roughness = 0.3;
238
- this.ior = 2;
239
- this.thickness = 0.1;
240
- this.specularColor = new Color(30719);
241
- this.specularIntensity = 0.1;
242
- this.envMapIntensity = 1.8;
243
- this.sheenColor = new Color(7798903);
244
- this.sheen = 0.35;
317
+ this.transmission = characterValues.material.transmission;
318
+ this.metalness = characterValues.material.metalness;
319
+ this.roughness = characterValues.material.roughness;
320
+ this.ior = characterValues.material.ior;
321
+ this.thickness = characterValues.material.thickness;
322
+ this.specularColor = new Color().setRGB(
323
+ characterValues.material.specularColor.r,
324
+ characterValues.material.specularColor.g,
325
+ characterValues.material.specularColor.b
326
+ );
327
+ this.specularIntensity = characterValues.material.specularIntensity;
328
+ this.emissive = new Color().setRGB(
329
+ characterValues.material.emissive.r,
330
+ characterValues.material.emissive.g,
331
+ characterValues.material.emissive.b
332
+ );
333
+ this.emissiveIntensity = characterValues.material.emissiveIntensity;
334
+ this.envMapIntensity = characterValues.material.envMapIntensity;
335
+ this.sheenColor = new Color().setRGB(
336
+ characterValues.material.sheenColor.r,
337
+ characterValues.material.sheenColor.g,
338
+ characterValues.material.sheenColor.b
339
+ );
340
+ this.sheen = characterValues.material.sheen;
341
+ this.clearcoat = characterValues.material.clearcoat;
342
+ this.clearcoatRoughness = characterValues.material.clearcoatRoughness;
245
343
  this.onBeforeCompile = (shader) => {
246
344
  this.uniforms = UniformsUtils.clone(shader.uniforms);
247
345
  this.uniforms.nearClip = { value: 0.01 };
@@ -257,6 +355,7 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
257
355
  shader.fragmentShader,
258
356
  /* glsl */
259
357
  `
358
+ //#define showPattern
260
359
  varying vec2 vUv;
261
360
  uniform float nearClip;
262
361
  uniform float farClip;
@@ -265,6 +364,25 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
265
364
  uniform float time;
266
365
  uniform vec3 diffuseRandomColor;
267
366
  ${bayerDither}
367
+
368
+ #ifdef showPattern
369
+ vec2 rand2(vec2 p) {
370
+ return fract(vec2(sin(p.x * 591.32 + p.y * 154.077), cos(p.x * 391.32 + p.y * 49.077)));
371
+ }
372
+ float voronoi(in vec2 x) {
373
+ vec2 p = floor(x);
374
+ vec2 f = fract(x);
375
+ float minDistance = 1.0;
376
+ for(int j = -1; j <= 1; j ++)
377
+ for(int i = -1; i <= 1; i ++) {
378
+ vec2 b = vec2(i, j);
379
+ vec2 rand = 0.5 + 0.5 * sin(time * 1.5 + 12.0 * rand2(p + b));
380
+ vec2 r = vec2(b) - f + rand;
381
+ minDistance = min(minDistance, length(r));
382
+ }
383
+ return minDistance;
384
+ }
385
+ #endif
268
386
  `
269
387
  );
270
388
  shader.fragmentShader = injectBefore(
@@ -286,10 +404,22 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
286
404
  d = bayerDither(bayerbr, p - ivec2(4, 4));
287
405
  }
288
406
  if (distance <= ditheringNear + d * ditheringRange) discard;
289
- vec2 suv = vUv;
290
- float s = clamp(0.35 + 0.35 * sin(5.0 * -time + suv.y * 500.0), 0.0, 1.0);
407
+
408
+ vec2 uv = vUv;
409
+ float s = clamp(0.35 + 0.35 * sin(5.0 * -time + vUv.y * 600.0), 0.0, 1.0);
291
410
  float scanLines = pow(s, 1.33);
411
+
292
412
  outgoingLight *= diffuseRandomColor;
413
+
414
+ #ifdef showPattern
415
+ float val = pow(voronoi(uv * 8.0) * 1.2, 0.5);
416
+ float thickness = 1.0 / 500.0;
417
+ vec2 g = step(mod(uv, 0.015), vec2(thickness));
418
+ float a = 1.0 - clamp(val * (g.x + g.y), 0.0, 1.0);
419
+ vec3 grid = vec3(smoothstep(0.01, 0.0, a) * 1.15) * diffuseRandomColor;
420
+ outgoingLight += grid;
421
+ #endif
422
+
293
423
  outgoingLight += smoothstep(0.1, 0.0, scanLines) * 0.1;
294
424
  `
295
425
  );
@@ -297,8 +427,8 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
297
427
  this.generateColorCube();
298
428
  }
299
429
  generateColorCube() {
300
- const saturation = 0.7;
301
- const lightness = 0.8;
430
+ const saturation = 0.4;
431
+ const lightness = 0.7;
302
432
  const goldenRatioConjugate = 0.618033988749895;
303
433
  let hue = 0;
304
434
  for (let i = 0; i < 216; i++) {
@@ -308,6 +438,34 @@ var CharacterMaterial = class extends MeshPhysicalMaterial {
308
438
  hue = (hue + goldenRatioConjugate) % 1;
309
439
  }
310
440
  }
441
+ update() {
442
+ this.transmission = characterValues.material.transmission;
443
+ this.metalness = characterValues.material.metalness;
444
+ this.roughness = characterValues.material.roughness;
445
+ this.ior = characterValues.material.ior;
446
+ this.thickness = characterValues.material.thickness;
447
+ this.specularColor = new Color().setRGB(
448
+ characterValues.material.specularColor.r,
449
+ characterValues.material.specularColor.g,
450
+ characterValues.material.specularColor.b
451
+ );
452
+ this.specularIntensity = characterValues.material.specularIntensity;
453
+ this.emissive = new Color().setRGB(
454
+ characterValues.material.emissive.r,
455
+ characterValues.material.emissive.g,
456
+ characterValues.material.emissive.b
457
+ );
458
+ this.emissiveIntensity = characterValues.material.emissiveIntensity;
459
+ this.envMapIntensity = characterValues.material.envMapIntensity;
460
+ this.sheenColor = new Color().setRGB(
461
+ characterValues.material.sheenColor.r,
462
+ characterValues.material.sheenColor.g,
463
+ characterValues.material.sheenColor.b
464
+ );
465
+ this.sheen = characterValues.material.sheen;
466
+ this.clearcoat = characterValues.material.clearcoat;
467
+ this.clearcoatRoughness = characterValues.material.clearcoatRoughness;
468
+ }
311
469
  };
312
470
 
313
471
  // src/character/CharacterState.ts
@@ -323,73 +481,130 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
323
481
 
324
482
  // src/character/ModelLoader.ts
325
483
  import { LoadingManager } from "three";
326
- import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
327
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
328
- var ModelLoader = class {
329
- constructor() {
330
- __publicField(this, "debug", false);
484
+ import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
485
+ var CachedGLTFLoader = class extends ThreeGLTFLoader {
486
+ constructor(manager) {
487
+ super(manager);
488
+ __publicField(this, "blobCache");
489
+ this.blobCache = /* @__PURE__ */ new Map();
490
+ }
491
+ setBlobUrl(originalUrl, blobUrl) {
492
+ this.blobCache.set(originalUrl, blobUrl);
493
+ }
494
+ getBlobUrl(originalUrl) {
495
+ return this.blobCache.get(originalUrl);
496
+ }
497
+ load(url, onLoad, onProgress, onError) {
498
+ const blobUrl = this.getBlobUrl(url);
499
+ if (blobUrl) {
500
+ console.log(`Loading cached ${url.split("/").pop()}`);
501
+ super.load(blobUrl, onLoad, onProgress, onError);
502
+ } else {
503
+ super.load(url, onLoad, onProgress, onError);
504
+ }
505
+ }
506
+ };
507
+ var LRUCache = class {
508
+ constructor(maxSize = 100) {
509
+ __publicField(this, "maxSize");
510
+ __publicField(this, "cache");
511
+ this.maxSize = maxSize;
512
+ this.cache = /* @__PURE__ */ new Map();
513
+ }
514
+ get(key) {
515
+ const item = this.cache.get(key);
516
+ if (item) {
517
+ this.cache.delete(key);
518
+ this.cache.set(key, item);
519
+ }
520
+ return item;
521
+ }
522
+ set(key, value) {
523
+ if (this.cache.size >= this.maxSize) {
524
+ const oldestKey = this.cache.keys().next().value;
525
+ this.cache.delete(oldestKey);
526
+ }
527
+ this.cache.set(key, value);
528
+ }
529
+ };
530
+ var _ModelLoader = class {
531
+ constructor(maxCacheSize = 100) {
331
532
  __publicField(this, "loadingManager");
332
- __publicField(this, "fbxLoader");
333
533
  __publicField(this, "gltfLoader");
534
+ __publicField(this, "modelCache");
535
+ __publicField(this, "ongoingLoads", /* @__PURE__ */ new Map());
334
536
  this.loadingManager = new LoadingManager();
335
- this.fbxLoader = new FBXLoader(this.loadingManager);
336
- this.gltfLoader = new GLTFLoader(this.loadingManager);
537
+ this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
538
+ this.modelCache = new LRUCache(maxCacheSize);
337
539
  }
338
- async load(fileUrl, fileType) {
339
- const extension = fileUrl.split(".").pop();
340
- if (typeof extension === "undefined") {
341
- console.error(`Unable to identify model type from ${fileUrl}`);
342
- return;
540
+ /* TODO: decide between below lazy initialization or eager on this file's bottom export */
541
+ static getInstance() {
542
+ if (!_ModelLoader.instance) {
543
+ _ModelLoader.instance = new _ModelLoader();
343
544
  }
344
- const name = fileUrl.split("/").pop().replace(`.${extension}`, "");
345
- if (this.debug) {
346
- console.log(`Loading ${extension} model ${name} from ${fileUrl}`);
545
+ return _ModelLoader.instance;
546
+ }
547
+ async load(fileUrl, fileType) {
548
+ const cachedModel = this.modelCache.get(fileUrl);
549
+ if (cachedModel) {
550
+ const blobURL = URL.createObjectURL(cachedModel.blob);
551
+ this.gltfLoader.setBlobUrl(fileUrl, blobURL);
552
+ return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
553
+ } else {
554
+ console.log(`Loading ${fileUrl} from server`);
555
+ const ongoingLoad = this.ongoingLoads.get(fileUrl);
556
+ if (ongoingLoad)
557
+ return ongoingLoad;
558
+ const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
559
+ const originalExtension = fileUrl.split(".").pop() || "";
560
+ this.modelCache.set(fileUrl, { blob, originalExtension });
561
+ const blobURL = URL.createObjectURL(blob);
562
+ this.ongoingLoads.delete(fileUrl);
563
+ return this.loadFromUrl(blobURL, fileType, originalExtension);
564
+ });
565
+ this.ongoingLoads.set(fileUrl, loadPromise);
566
+ return loadPromise;
347
567
  }
568
+ }
569
+ async loadFromUrl(url, fileType, extension) {
348
570
  if (["gltf", "glb"].includes(extension)) {
349
571
  return new Promise((resolve, reject) => {
350
572
  this.gltfLoader.load(
351
- fileUrl,
573
+ url,
352
574
  (object) => {
353
575
  if (fileType === "model") {
354
576
  resolve(object.scene);
355
577
  } else if (fileType === "animation") {
356
578
  resolve(object.animations[0]);
357
579
  } else {
358
- const error = `Trying to load unknown ${fileType} type of element from file ${fileUrl}`;
580
+ const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
359
581
  console.error(error);
360
582
  reject(error);
361
583
  }
362
584
  },
363
585
  void 0,
364
586
  (error) => {
365
- console.error(`Error loading GL(B|TF) from ${fileUrl}: ${error}`);
366
- reject(error);
367
- }
368
- );
369
- });
370
- } else if (extension === "fbx") {
371
- return new Promise((resolve, reject) => {
372
- this.fbxLoader.load(
373
- fileUrl,
374
- (object) => {
375
- resolve(object);
376
- },
377
- void 0,
378
- (error) => {
379
- console.error(`Error loading FBX from ${fileUrl}: ${error}`);
587
+ console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
380
588
  reject(error);
381
589
  }
382
590
  );
383
591
  });
592
+ } else {
593
+ console.error(`Error: can't recognize ${url} extension: ${extension}`);
384
594
  }
385
595
  }
386
596
  };
597
+ var ModelLoader = _ModelLoader;
598
+ __publicField(ModelLoader, "instance", null);
599
+ var MODEL_LOADER = ModelLoader.getInstance();
600
+ var ModelLoader_default = MODEL_LOADER;
387
601
 
388
602
  // src/character/CharacterModel.ts
389
603
  var CharacterModel = class {
390
604
  constructor(characterDescription) {
391
605
  this.characterDescription = characterDescription;
392
- __publicField(this, "modelLoader", new ModelLoader());
606
+ /* TODO: pick between below eager instantiation or ModelLoader.getInstance() lazy one */
607
+ __publicField(this, "modelLoader", ModelLoader_default);
393
608
  __publicField(this, "mesh", null);
394
609
  __publicField(this, "material", new CharacterMaterial());
395
610
  __publicField(this, "animations", {});
@@ -410,6 +625,10 @@ var CharacterModel = class {
410
625
  this.characterDescription.sprintAnimationFileUrl,
411
626
  2 /* running */
412
627
  );
628
+ await this.setAnimationFromFile(
629
+ this.characterDescription.airAnimationFileUrl,
630
+ 4 /* air */
631
+ );
413
632
  this.applyMaterialToAllSkinnedMeshes(this.material);
414
633
  }
415
634
  updateAnimation(targetAnimation, deltaTime) {
@@ -462,7 +681,7 @@ var CharacterModel = class {
462
681
  if (typeof mainMesh !== "undefined") {
463
682
  this.mesh = new Object3D2();
464
683
  const model = mainMesh;
465
- model.position.set(0, -0.35, 0);
684
+ model.position.set(0, -0.4, 0);
466
685
  this.mesh.add(model);
467
686
  this.mesh.name = name;
468
687
  this.mesh.scale.set(scale, scale, scale);
@@ -485,7 +704,7 @@ var CharacterModel = class {
485
704
  }
486
705
  });
487
706
  }
488
- transitionToAnimation(targetAnimation, transitionDuration = 0.21) {
707
+ transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
489
708
  if (!this.mesh || this.currentAnimation === null)
490
709
  return;
491
710
  const currentAction = this.animations[this.currentAnimation];
@@ -506,7 +725,7 @@ var CharacterModel = class {
506
725
  };
507
726
 
508
727
  // src/character/LocalController.ts
509
- import { Box3, Line3, Matrix4, Quaternion, Vector3 as Vector33 } from "three";
728
+ import { Box3, Line3, Matrix4, Quaternion, Raycaster as Raycaster2, Vector3 as Vector33 } from "three";
510
729
  var LocalController = class {
511
730
  constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
512
731
  this.model = model;
@@ -520,10 +739,20 @@ var LocalController = class {
520
739
  radius: 0.4,
521
740
  segment: new Line3(new Vector33(), new Vector33(0, 1.05, 0))
522
741
  });
742
+ __publicField(this, "maxWalkSpeed", 6);
743
+ __publicField(this, "maxRunSpeed", 8.5);
744
+ __publicField(this, "gravity", -42);
745
+ __publicField(this, "jumpForce", 16);
746
+ __publicField(this, "coyoteTimeThreshold", 290);
747
+ __publicField(this, "coyoteTime", false);
748
+ __publicField(this, "canJump", true);
523
749
  __publicField(this, "characterOnGround", false);
750
+ __publicField(this, "characterWasOnGround", false);
751
+ __publicField(this, "characterAirborneSince", 0);
752
+ __publicField(this, "currentHeight", 0);
524
753
  __publicField(this, "characterVelocity", new Vector33());
525
- __publicField(this, "gravity", -20);
526
- __publicField(this, "upVector", new Vector33(0, 1, 0));
754
+ __publicField(this, "vectorUp", new Vector33(0, 1, 0));
755
+ __publicField(this, "vectorDown", new Vector33(0, -1, 0));
527
756
  __publicField(this, "rotationOffset", 0);
528
757
  __publicField(this, "azimuthalAngle", 0);
529
758
  __publicField(this, "tempBox", new Box3());
@@ -531,16 +760,15 @@ var LocalController = class {
531
760
  __publicField(this, "tempSegment", new Line3());
532
761
  __publicField(this, "tempVector", new Vector33());
533
762
  __publicField(this, "tempVector2", new Vector33());
534
- __publicField(this, "jumpInput", false);
535
- __publicField(this, "jumpForce", 10);
536
- __publicField(this, "canJump", true);
537
- __publicField(this, "inputDirections", {
538
- forward: false,
539
- backward: false,
540
- left: false,
541
- right: false
542
- });
543
- __publicField(this, "runInput", false);
763
+ __publicField(this, "rayCaster", new Raycaster2());
764
+ __publicField(this, "forward");
765
+ __publicField(this, "backward");
766
+ __publicField(this, "left");
767
+ __publicField(this, "right");
768
+ __publicField(this, "run");
769
+ __publicField(this, "jump");
770
+ __publicField(this, "anyDirection");
771
+ __publicField(this, "conflictingDirections");
544
772
  __publicField(this, "thirdPersonCamera", null);
545
773
  __publicField(this, "speed", 0);
546
774
  __publicField(this, "targetSpeed", 0);
@@ -550,75 +778,90 @@ var LocalController = class {
550
778
  rotation: { quaternionY: 0, quaternionW: 0 },
551
779
  state: 0 /* idle */
552
780
  });
781
+ setInterval(() => this.update.bind(this), 3e3);
553
782
  }
554
783
  update() {
555
784
  if (!this.model?.mesh || !this.model?.animationMixer)
556
785
  return;
557
786
  if (!this.thirdPersonCamera)
558
787
  this.thirdPersonCamera = this.cameraManager.camera;
559
- const movementKeysPressed = this.keyInputManager.isMovementKeyPressed();
560
- const forward = this.keyInputManager.isKeyPressed("w");
561
- const backward = this.keyInputManager.isKeyPressed("s");
562
- const left = this.keyInputManager.isKeyPressed("a");
563
- const right = this.keyInputManager.isKeyPressed("d");
564
- this.inputDirections = { forward, backward, left, right };
565
- this.jumpInput = this.keyInputManager.isJumping();
566
- this.runInput = this.keyInputManager.isShiftPressed();
567
- if (movementKeysPressed) {
788
+ const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
789
+ this.forward = forward;
790
+ this.backward = backward;
791
+ this.left = left;
792
+ this.right = right;
793
+ this.run = run;
794
+ this.jump = jump;
795
+ this.anyDirection = anyDirection;
796
+ this.conflictingDirections = conflictingDirection;
797
+ this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
798
+ this.speed += ease(this.targetSpeed, this.speed, 0.07);
799
+ this.rayCaster.set(this.model.mesh.position, this.vectorDown);
800
+ const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
801
+ if (minimumDistance !== null) {
802
+ this.currentHeight = minimumDistance;
803
+ }
804
+ if (anyDirection || !this.characterOnGround) {
568
805
  const targetAnimation = this.getTargetAnimation();
569
806
  this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
570
807
  } else {
571
808
  this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
572
809
  }
573
- if (Object.values(this.inputDirections).some((v) => v)) {
810
+ if (this.anyDirection)
574
811
  this.updateRotation();
575
- }
576
812
  for (let i = 0; i < this.collisionDetectionSteps; i++) {
577
813
  this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
578
814
  }
579
- if (this.model.mesh.position.y < 0) {
815
+ if (this.model.mesh.position.y < 0)
580
816
  this.resetPosition();
581
- }
582
817
  this.updateNetworkState();
583
818
  }
584
819
  getTargetAnimation() {
585
- const { forward, backward, left, right } = this.inputDirections;
586
- const hasAnyDirection = forward || backward || left || right;
587
- const isRunning = this.runInput && hasAnyDirection;
588
- const conflictingDirections = forward && backward || left && right;
589
- if (conflictingDirections)
820
+ if (!this.model.mesh)
821
+ return 0 /* idle */;
822
+ if (this.conflictingDirections)
590
823
  return 0 /* idle */;
591
- return hasAnyDirection ? isRunning ? 2 /* running */ : 1 /* walking */ : 0 /* idle */;
824
+ const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
825
+ if (this.currentHeight > jumpHeight && !this.characterOnGround) {
826
+ return 4 /* air */;
827
+ }
828
+ return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
592
829
  }
593
830
  updateRotationOffset() {
594
- const { forward, backward, left, right } = this.inputDirections;
595
- if (left && right || forward && backward)
831
+ if (this.conflictingDirections)
596
832
  return;
597
- if (forward) {
833
+ if (this.forward) {
598
834
  this.rotationOffset = Math.PI;
599
- if (left)
835
+ if (this.left)
600
836
  this.rotationOffset = Math.PI + Math.PI / 4;
601
- if (right)
837
+ if (this.right)
602
838
  this.rotationOffset = Math.PI - Math.PI / 4;
603
- } else if (backward) {
839
+ } else if (this.backward) {
604
840
  this.rotationOffset = Math.PI * 2;
605
- if (left)
841
+ if (this.left)
606
842
  this.rotationOffset = -Math.PI * 2 - Math.PI / 4;
607
- if (right)
843
+ if (this.right)
608
844
  this.rotationOffset = Math.PI * 2 + Math.PI / 4;
609
- } else if (left) {
845
+ } else if (this.left) {
610
846
  this.rotationOffset = Math.PI * -0.5;
611
- } else if (right) {
847
+ } else if (this.right) {
612
848
  this.rotationOffset = Math.PI * 0.5;
613
849
  }
614
850
  }
615
851
  updateAzimuthalAngle() {
616
852
  if (!this.thirdPersonCamera || !this.model?.mesh)
617
853
  return;
618
- this.azimuthalAngle = Math.atan2(
619
- this.thirdPersonCamera.position.x - this.model.mesh.position.x,
620
- this.thirdPersonCamera.position.z - this.model.mesh.position.z
621
- );
854
+ const camToModelDistance = this.thirdPersonCamera.position.distanceTo(this.model.mesh.position);
855
+ const isCameraFirstPerson = camToModelDistance < 2;
856
+ if (isCameraFirstPerson) {
857
+ const cameraForward = new Vector33(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
858
+ this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
859
+ } else {
860
+ this.azimuthalAngle = Math.atan2(
861
+ this.thirdPersonCamera.position.x - this.model.mesh.position.x,
862
+ this.thirdPersonCamera.position.z - this.model.mesh.position.z
863
+ );
864
+ }
622
865
  }
623
866
  updateRotation() {
624
867
  if (!this.thirdPersonCamera || !this.model?.mesh)
@@ -626,7 +869,7 @@ var LocalController = class {
626
869
  this.updateRotationOffset();
627
870
  this.updateAzimuthalAngle();
628
871
  const rotationQuaternion = new Quaternion();
629
- rotationQuaternion.setFromAxisAngle(this.upVector, this.azimuthalAngle + this.rotationOffset);
872
+ rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
630
873
  this.model.mesh.quaternion.rotateTowards(rotationQuaternion, 0.07);
631
874
  }
632
875
  addScaledVectorToCharacter(deltaTime) {
@@ -637,36 +880,43 @@ var LocalController = class {
637
880
  updatePosition(deltaTime, _iter) {
638
881
  if (!this.model?.mesh)
639
882
  return;
640
- const { forward, backward, left, right } = this.inputDirections;
641
- this.targetSpeed = this.runInput ? 14 : 8;
642
- this.speed += ease(this.targetSpeed, this.speed, 0.07);
643
883
  if (this.characterOnGround) {
644
- this.canJump = true;
645
- if (this.jumpInput && this.canJump) {
884
+ if (!this.jump)
885
+ this.canJump = true;
886
+ if (this.jump && this.canJump) {
646
887
  this.characterVelocity.y += this.jumpForce;
647
888
  this.canJump = false;
648
889
  } else {
649
890
  this.characterVelocity.y = deltaTime * this.gravity;
650
891
  }
892
+ } else if (this.jump && this.coyoteTime) {
893
+ console.log("coyoteJump");
894
+ this.characterVelocity.y = this.jumpForce;
895
+ this.canJump = false;
651
896
  } else {
652
897
  this.characterVelocity.y += deltaTime * this.gravity;
653
898
  this.canJump = false;
654
899
  }
655
900
  this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
656
- if (forward) {
657
- this.tempVector.set(0, 0, -1).applyAxisAngle(this.upVector, this.azimuthalAngle);
658
- this.addScaledVectorToCharacter(deltaTime);
901
+ this.tempVector.set(0, 0, 0);
902
+ if (this.forward) {
903
+ const forward = new Vector33(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
904
+ this.tempVector.add(forward);
659
905
  }
660
- if (backward) {
661
- this.tempVector.set(0, 0, 1).applyAxisAngle(this.upVector, this.azimuthalAngle);
662
- this.addScaledVectorToCharacter(deltaTime);
906
+ if (this.backward) {
907
+ const backward = new Vector33(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
908
+ this.tempVector.add(backward);
663
909
  }
664
- if (left) {
665
- this.tempVector.set(-1, 0, 0).applyAxisAngle(this.upVector, this.azimuthalAngle);
666
- this.addScaledVectorToCharacter(deltaTime);
910
+ if (this.left) {
911
+ const left = new Vector33(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
912
+ this.tempVector.add(left);
913
+ }
914
+ if (this.right) {
915
+ const right = new Vector33(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
916
+ this.tempVector.add(right);
667
917
  }
668
- if (right) {
669
- this.tempVector.set(1, 0, 0).applyAxisAngle(this.upVector, this.azimuthalAngle);
918
+ if (this.tempVector.length() > 0) {
919
+ this.tempVector.normalize();
670
920
  this.addScaledVectorToCharacter(deltaTime);
671
921
  }
672
922
  this.model.mesh.updateMatrixWorld();
@@ -687,6 +937,11 @@ var LocalController = class {
687
937
  deltaVector.normalize().multiplyScalar(offset);
688
938
  this.model.mesh.position.add(deltaVector);
689
939
  this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
940
+ if (this.characterWasOnGround && !this.characterOnGround) {
941
+ this.characterAirborneSince = Date.now();
942
+ }
943
+ this.coyoteTime = this.characterVelocity.y < 0 && this.characterOnGround === false && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
944
+ this.characterWasOnGround = this.characterOnGround;
690
945
  if (this.characterOnGround) {
691
946
  this.characterVelocity.set(0, 0, 0);
692
947
  } else {
@@ -714,7 +969,7 @@ var LocalController = class {
714
969
  if (!this.model?.mesh)
715
970
  return;
716
971
  this.characterVelocity.y = 0;
717
- this.model.mesh.position.y = 5;
972
+ this.model.mesh.position.y = 3;
718
973
  this.characterOnGround = false;
719
974
  }
720
975
  };
@@ -760,6 +1015,7 @@ var Character = class {
760
1015
  if (typeof this.model.material.uniforms.time !== "undefined") {
761
1016
  this.model.material.uniforms.time.value = time;
762
1017
  this.model.material.uniforms.diffuseRandomColor.value = this.color;
1018
+ this.model.material.update();
763
1019
  }
764
1020
  }
765
1021
  };
@@ -770,24 +1026,19 @@ import { Group, Vector3 as Vector36 } from "three";
770
1026
  // src/character/RemoteController.ts
771
1027
  import {
772
1028
  AnimationMixer as AnimationMixer2,
773
- LoadingManager as LoadingManager2,
774
1029
  Object3D as Object3D3,
775
1030
  Quaternion as Quaternion2,
776
1031
  Vector3 as Vector35
777
1032
  } from "three";
778
- import { FBXLoader as FBXLoader2 } from "three/examples/jsm/loaders/FBXLoader.js";
779
- import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
780
1033
  var RemoteController = class {
781
1034
  constructor(character, id) {
782
1035
  this.character = character;
783
1036
  this.id = id;
1037
+ __publicField(this, "modelLoader", ModelLoader_default);
784
1038
  __publicField(this, "characterModel", null);
785
- __publicField(this, "loadManager", new LoadingManager2());
786
1039
  __publicField(this, "animationMixer", new AnimationMixer2(new Object3D3()));
787
1040
  __publicField(this, "animations", /* @__PURE__ */ new Map());
788
1041
  __publicField(this, "currentAnimation", 0 /* idle */);
789
- __publicField(this, "fbxLoader", new FBXLoader2(this.loadManager));
790
- __publicField(this, "gltfLoader", new GLTFLoader2(this.loadManager));
791
1042
  __publicField(this, "networkState", {
792
1043
  id: 0,
793
1044
  position: { x: 0, y: 0, z: 0 },
@@ -795,6 +1046,7 @@ var RemoteController = class {
795
1046
  state: this.currentAnimation
796
1047
  });
797
1048
  this.characterModel = this.character.model.mesh;
1049
+ this.characterModel.updateMatrixWorld();
798
1050
  this.animationMixer = new AnimationMixer2(this.characterModel);
799
1051
  }
800
1052
  update(clientUpdate, time, deltaTime) {
@@ -804,44 +1056,15 @@ var RemoteController = class {
804
1056
  this.updateFromNetwork(clientUpdate);
805
1057
  this.animationMixer.update(deltaTime);
806
1058
  }
807
- setAnimationFromFile(animationType, fileName) {
808
- const animationFile = `${fileName}`;
809
- const extension = fileName.split(".").pop();
810
- if (typeof extension !== "string") {
811
- console.error(`Error: could not recognize extension of animation: ${animationFile}`);
812
- return;
813
- }
814
- if (["gltf", "glb"].includes(extension)) {
815
- this.gltfLoader.load(
816
- animationFile,
817
- (anim) => {
818
- const animation = anim.animations[0];
819
- const animationAction = this.animationMixer.clipAction(animation);
820
- this.animations.set(animationType, animationAction);
821
- if (animationType === 0 /* idle */) {
822
- animationAction.play();
823
- }
824
- },
825
- void 0,
826
- (error) => console.error(`Error loading ${animationFile}: ${error}`)
827
- );
828
- } else if (["fbx"].includes(extension)) {
829
- this.fbxLoader.load(
830
- animationFile,
831
- (anim) => {
832
- const animation = anim.animations[0];
833
- const animationAction = this.animationMixer.clipAction(animation);
834
- this.animations.set(animationType, animationAction);
835
- if (animationType === 0 /* idle */) {
836
- animationAction.play();
837
- }
838
- },
839
- void 0,
840
- (error) => console.error(`Error loading ${animationFile}: ${error}`)
841
- );
1059
+ async setAnimationFromFile(animationType, fileName) {
1060
+ const animation = await this.modelLoader.load(fileName, "animation");
1061
+ const animationAction = this.animationMixer.clipAction(animation);
1062
+ this.animations.set(animationType, animationAction);
1063
+ if (animationType === 0 /* idle */) {
1064
+ animationAction.play();
842
1065
  }
843
1066
  }
844
- transitionToAnimation(targetAnimation, transitionDuration = 0.21) {
1067
+ transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
845
1068
  if (this.currentAnimation === targetAnimation)
846
1069
  return;
847
1070
  const currentAction = this.animations.get(this.currentAnimation);
@@ -861,9 +1084,9 @@ var RemoteController = class {
861
1084
  if (!this.characterModel)
862
1085
  return;
863
1086
  const { position, rotation, state } = clientUpdate;
864
- this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.2);
1087
+ this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.15);
865
1088
  const rotationQuaternion = new Quaternion2(0, rotation.quaternionY, 0, rotation.quaternionW);
866
- this.characterModel.quaternion.slerp(rotationQuaternion, 0.2);
1089
+ this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
867
1090
  if (state !== this.currentAnimation) {
868
1091
  this.transitionToAnimation(state);
869
1092
  }
@@ -905,10 +1128,19 @@ var CharacterManager = class {
905
1128
  __publicField(this, "remoteCharacterControllers", /* @__PURE__ */ new Map());
906
1129
  __publicField(this, "characterDescription", null);
907
1130
  __publicField(this, "character", null);
1131
+ __publicField(this, "cameraOffsetTarget", 0);
1132
+ __publicField(this, "cameraOffset", 0);
908
1133
  __publicField(this, "group");
909
1134
  this.group = new Group();
910
- }
911
- spawnCharacter(characterDescription, id, isLocal = false) {
1135
+ setInterval(() => this.update.bind(this), 3e3);
1136
+ }
1137
+ /* TODO:
1138
+ 1) Separate this method into spawnLocalCharacter and spawnRemoteCharacter
1139
+ 2) Make this synchronous to avoid having loadingCharacters and instead manage
1140
+ the mesh loading async (would allow us to show a nameplate where a remote
1141
+ user is before the asset loads).
1142
+ */
1143
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector36()) {
912
1144
  this.characterDescription = characterDescription;
913
1145
  const characterLoadingPromise = new Promise((resolve) => {
914
1146
  const character = new Character(
@@ -923,15 +1155,26 @@ var CharacterManager = class {
923
1155
  this.cameraManager.camera
924
1156
  );
925
1157
  } else {
926
- const spawnPosition = getSpawnPositionInsideCircle(3, 30, id);
1158
+ spawnPosition = getSpawnPositionInsideCircle(3, 30, id, 0.4);
927
1159
  character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
928
- this.cameraManager.camera.position.set(
929
- spawnPosition.x,
930
- spawnPosition.y + 1.5,
931
- spawnPosition.z + 3
932
- );
1160
+ character.model.mesh.updateMatrixWorld();
1161
+ this.sendUpdate({
1162
+ id,
1163
+ position: {
1164
+ x: spawnPosition.x,
1165
+ y: spawnPosition.y,
1166
+ z: spawnPosition.z
1167
+ },
1168
+ rotation: { quaternionY: 0, quaternionW: 0 },
1169
+ state: 0 /* idle */
1170
+ });
1171
+ }
1172
+ character.model.hideMaterialByMeshName("SK_Mannequin_2");
1173
+ if (!isLocal) {
1174
+ character.model?.mesh?.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1175
+ character.model?.mesh?.updateMatrixWorld();
1176
+ character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
933
1177
  }
934
- character.model.hideMaterialByMeshName("SK_UE5Mannequin_1");
935
1178
  this.group.add(character.model.mesh);
936
1179
  if (isLocal) {
937
1180
  this.character = character;
@@ -950,6 +1193,15 @@ var CharacterManager = class {
950
1193
  2 /* running */,
951
1194
  characterDescription.sprintAnimationFileUrl
952
1195
  );
1196
+ remoteController.setAnimationFromFile(
1197
+ 4 /* air */,
1198
+ characterDescription.airAnimationFileUrl
1199
+ );
1200
+ remoteController.characterModel?.position.set(
1201
+ spawnPosition.x,
1202
+ spawnPosition.y,
1203
+ spawnPosition.z
1204
+ );
953
1205
  this.remoteCharacterControllers.set(id, remoteController);
954
1206
  }
955
1207
  resolve(character);
@@ -990,7 +1242,13 @@ var CharacterManager = class {
990
1242
  update() {
991
1243
  if (this.character) {
992
1244
  this.character.update(this.timeManager.time);
993
- this.cameraManager.setTarget(this.character.position.add(new Vector36(0, 1.3, 0)));
1245
+ if (this.character.model?.mesh) {
1246
+ this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.6 : 0;
1247
+ this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1248
+ const targetOffset = new Vector36(0, 1.3, this.cameraOffset);
1249
+ targetOffset.applyQuaternion(this.character.model.mesh.quaternion);
1250
+ this.cameraManager.setTarget(this.character.position.add(targetOffset));
1251
+ }
994
1252
  if (this.character.controller) {
995
1253
  this.character.controller.update();
996
1254
  if (this.timeManager.frame % 2 === 0) {
@@ -998,8 +1256,14 @@ var CharacterManager = class {
998
1256
  }
999
1257
  }
1000
1258
  for (const [id, update] of this.clientStates) {
1259
+ const { position } = update;
1001
1260
  if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1002
- this.spawnCharacter(this.characterDescription, id).then(() => {
1261
+ this.spawnCharacter(
1262
+ this.characterDescription,
1263
+ id,
1264
+ false,
1265
+ new Vector36(position.x, position.y, position.z)
1266
+ ).then((_character) => {
1003
1267
  this.loadingCharacters.delete(id);
1004
1268
  });
1005
1269
  }
@@ -1031,6 +1295,10 @@ var KeyInputManager = class {
1031
1295
  __publicField(this, "keys", /* @__PURE__ */ new Map());
1032
1296
  document.addEventListener("keydown", this.onKeyDown.bind(this));
1033
1297
  document.addEventListener("keyup", this.onKeyUp.bind(this));
1298
+ window.addEventListener("blur", this.handleUnfocus.bind(this));
1299
+ }
1300
+ handleUnfocus(_event) {
1301
+ this.keys.clear();
1034
1302
  }
1035
1303
  onKeyDown(event) {
1036
1304
  this.keys.set(event.key.toLowerCase(), true);
@@ -1044,15 +1312,34 @@ var KeyInputManager = class {
1044
1312
  isMovementKeyPressed() {
1045
1313
  return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
1046
1314
  }
1047
- isShiftPressed() {
1315
+ get forward() {
1316
+ return this.isKeyPressed("w");
1317
+ }
1318
+ get backward() {
1319
+ return this.isKeyPressed("s");
1320
+ }
1321
+ get left() {
1322
+ return this.isKeyPressed("a");
1323
+ }
1324
+ get right() {
1325
+ return this.isKeyPressed("d");
1326
+ }
1327
+ get run() {
1048
1328
  return this.isKeyPressed("shift");
1049
1329
  }
1050
- isJumping() {
1330
+ get jump() {
1051
1331
  return this.isKeyPressed(" ");
1052
1332
  }
1333
+ get anyDirection() {
1334
+ return this.isMovementKeyPressed();
1335
+ }
1336
+ get conflictingDirection() {
1337
+ return this.isKeyPressed("w") && this.isKeyPressed("s") || this.isKeyPressed("a") && this.isKeyPressed("d");
1338
+ }
1053
1339
  dispose() {
1054
1340
  document.removeEventListener("keydown", this.onKeyDown.bind(this));
1055
1341
  document.removeEventListener("keyup", this.onKeyDown.bind(this));
1342
+ window.removeEventListener("blur", this.handleUnfocus.bind(this));
1056
1343
  }
1057
1344
  };
1058
1345
 
@@ -1128,22 +1415,606 @@ var MMLCompositionScene = class {
1128
1415
 
1129
1416
  // src/rendering/composer.ts
1130
1417
  import {
1131
- EffectComposer,
1418
+ EffectComposer as EffectComposer2,
1132
1419
  RenderPass,
1133
- EffectPass,
1420
+ EffectPass as EffectPass2,
1134
1421
  FXAAEffect,
1135
1422
  ShaderPass,
1136
- BloomEffect
1423
+ BloomEffect as BloomEffect2,
1424
+ SSAOEffect as SSAOEffect2,
1425
+ BlendFunction as BlendFunction3,
1426
+ TextureEffect,
1427
+ ToneMappingEffect as ToneMappingEffect2,
1428
+ SMAAEffect,
1429
+ SMAAPreset,
1430
+ EdgeDetectionMode,
1431
+ PredicationMode,
1432
+ NormalPass
1137
1433
  } from "postprocessing";
1138
1434
  import {
1139
- ACESFilmicToneMapping,
1140
- PCFSoftShadowMap,
1435
+ Color as Color4,
1436
+ HalfFloatType,
1437
+ LinearSRGBColorSpace,
1438
+ LoadingManager as LoadingManager2,
1439
+ PMREMGenerator,
1141
1440
  Vector2 as Vector22,
1142
- WebGLRenderer as WebGLRenderer2
1441
+ WebGLRenderer as WebGLRenderer3
1143
1442
  } from "three";
1443
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
1144
1444
 
1145
- // src/rendering/post-effects/gauss-grain.ts
1146
- import { ShaderMaterial, Uniform, Vector2 } from "three";
1445
+ // src/tweakpane/composerSettings.ts
1446
+ import { BlendFunction } from "postprocessing";
1447
+ var composerValues = {
1448
+ renderer: {
1449
+ shadowMap: 2,
1450
+ toneMapping: 4,
1451
+ exposure: 1,
1452
+ bgIntensity: 0.6,
1453
+ bgBlurriness: 0
1454
+ },
1455
+ ssao: {
1456
+ blendFunction: BlendFunction.MULTIPLY,
1457
+ distanceScaling: true,
1458
+ depthAwareUpsampling: true,
1459
+ samples: 17,
1460
+ rings: 7,
1461
+ luminanceInfluence: 0.7,
1462
+ radius: 0.03,
1463
+ intensity: 2.5,
1464
+ bias: 0.05,
1465
+ fade: 0.03,
1466
+ resolutionScale: 0.75,
1467
+ color: { r: 0, g: 0, b: 0 },
1468
+ worldDistanceThreshold: 30,
1469
+ worldDistanceFalloff: 7,
1470
+ worldProximityThreshold: 0.5,
1471
+ worldProximityFalloff: 0.3
1472
+ },
1473
+ toneMapping: {
1474
+ mode: 2,
1475
+ resolution: 512,
1476
+ whitePoint: 32,
1477
+ middleGrey: 21,
1478
+ minLuminance: 0.01,
1479
+ averageLuminance: 0.01,
1480
+ adaptationRate: 2
1481
+ },
1482
+ brightness: -0.03,
1483
+ contrast: 1.3,
1484
+ saturation: 0.95,
1485
+ grain: 0.055,
1486
+ bloom: 0.4
1487
+ };
1488
+ var composerOptions = {
1489
+ renderer: {
1490
+ shadowMap: { min: 0, max: 2, step: 1 },
1491
+ toneMapping: { min: 0, max: 5, step: 1 },
1492
+ exposure: { min: 0, max: 3, step: 0.01 },
1493
+ bgIntensity: { min: 0, max: 1, step: 0.01 },
1494
+ bgBlurriness: { min: 0, max: 0.1, step: 1e-3 }
1495
+ },
1496
+ ssao: {
1497
+ samples: { min: 1, max: 50, step: 1 },
1498
+ rings: { min: 1, max: 50, step: 1 },
1499
+ luminanceInfluence: { min: 0, max: 1, step: 0.01 },
1500
+ radius: { min: 0, max: 0.1, step: 1e-3 },
1501
+ intensity: { min: 0, max: 5, step: 0.1 },
1502
+ bias: { min: 0, max: 0.1, step: 1e-3 },
1503
+ fade: { min: 0, max: 0.1, step: 1e-3 },
1504
+ resolutionScale: { min: 0.25, max: 2, step: 0.25 },
1505
+ worldDistanceThreshold: { min: 0, max: 200, step: 1 },
1506
+ worldDistanceFalloff: { min: 0, max: 200, step: 1 },
1507
+ worldProximityThreshold: { min: 0, max: 2, step: 0.01 },
1508
+ worldProximityFalloff: { min: 0, max: 2, step: 0.01 }
1509
+ },
1510
+ toneMapping: {
1511
+ mode: { min: 0, max: 4, step: 1 },
1512
+ resolution: { min: 64, max: 512, step: 64 },
1513
+ whitePoint: { min: 0, max: 32, step: 0.01 },
1514
+ middleGrey: { min: 0, max: 32, step: 0.01 },
1515
+ minLuminance: { min: 0, max: 32, step: 1e-3 },
1516
+ averageLuminance: { min: 1e-3, max: 0.2, step: 1e-3 },
1517
+ adaptationRate: { min: 0.1, max: 2, step: 0.1 }
1518
+ },
1519
+ brightness: {
1520
+ amount: { min: -1, max: 1, step: 0.01 }
1521
+ },
1522
+ contrast: {
1523
+ amount: { min: 0, max: 2, step: 0.01 }
1524
+ },
1525
+ saturation: {
1526
+ amount: { min: 0, max: 2, step: 0.01 }
1527
+ },
1528
+ grain: {
1529
+ amount: { min: 0, max: 0.2, step: 2e-3 }
1530
+ },
1531
+ bloom: {
1532
+ amount: { min: 0, max: 4, step: 0.1 }
1533
+ }
1534
+ };
1535
+ var shadowMapTypes = {
1536
+ 0: "BasicShadowMap",
1537
+ 1: "PCFShadowMap",
1538
+ 2: "PCFSoftShadowMap"
1539
+ };
1540
+ var rendererToneMappingTypes = {
1541
+ 0: "NoToneMapping",
1542
+ 1: "LinearToneMapping",
1543
+ 2: "ReinhardToneMapping",
1544
+ 3: "CineonToneMapping",
1545
+ 4: "ACESFilmicToneMapping",
1546
+ 5: "CustomToneMapping"
1547
+ };
1548
+ var customToneMappingTypes = {
1549
+ 0: "REINHARD",
1550
+ 1: "REINHARD2",
1551
+ 2: "REINHARD2_ADAPTIVE",
1552
+ 3: "OPTIMIZED_CINEON",
1553
+ 4: "ACES_FILMIC"
1554
+ };
1555
+ var rendererBlades = {
1556
+ shadowMapType: shadowMapTypes[composerValues.renderer.shadowMap],
1557
+ toneMappingType: rendererToneMappingTypes[composerValues.renderer.toneMapping]
1558
+ };
1559
+ var setShadowMapType = (value) => {
1560
+ rendererBlades.shadowMapType = shadowMapTypes[value];
1561
+ };
1562
+ var setToneMappingType = (value) => {
1563
+ rendererBlades.toneMappingType = rendererToneMappingTypes[value];
1564
+ };
1565
+ var customToneMappingBlade = {
1566
+ customToneMappingType: customToneMappingTypes[composerValues.toneMapping.mode]
1567
+ };
1568
+ var setCustomToneMappingType = (value) => {
1569
+ customToneMappingBlade.customToneMappingType = customToneMappingTypes[value];
1570
+ };
1571
+ var ssaoMaterialParams = [
1572
+ "fade",
1573
+ "bias",
1574
+ "minRadiusScale",
1575
+ "worldDistanceThreshold",
1576
+ "worldDistanceFalloff",
1577
+ "worldProximityThreshold",
1578
+ "worldProximityFalloff"
1579
+ ];
1580
+ var statsData = {
1581
+ triangles: "0",
1582
+ geometries: "0",
1583
+ textures: "0",
1584
+ shaders: "0",
1585
+ postPasses: "0",
1586
+ drawCalls: "0",
1587
+ rawDeltaTime: "0",
1588
+ deltaTime: "0",
1589
+ FPS: "0"
1590
+ };
1591
+
1592
+ // src/tweakpane/TweakPane.ts
1593
+ import {
1594
+ BlendFunction as BlendFunction2
1595
+ } from "postprocessing";
1596
+ import { Color as Color3 } from "three";
1597
+ import { Pane } from "tweakpane";
1598
+ var TweakPane = class {
1599
+ constructor(renderer, scene, composer) {
1600
+ __publicField(this, "renderer");
1601
+ __publicField(this, "scene");
1602
+ __publicField(this, "composer");
1603
+ __publicField(this, "gui", new Pane());
1604
+ __publicField(this, "render");
1605
+ __publicField(this, "stats");
1606
+ __publicField(this, "renderOptions");
1607
+ __publicField(this, "ssao");
1608
+ __publicField(this, "toneMapping");
1609
+ __publicField(this, "post");
1610
+ __publicField(this, "export");
1611
+ __publicField(this, "characterMaterial");
1612
+ __publicField(this, "saveVisibilityInLocalStorage", true);
1613
+ __publicField(this, "guiVisible", false);
1614
+ if (this.saveVisibilityInLocalStorage) {
1615
+ const localStorageGuiVisible = localStorage.getItem("guiVisible");
1616
+ if (localStorageGuiVisible !== null) {
1617
+ if (localStorageGuiVisible === "true") {
1618
+ this.guiVisible = true;
1619
+ } else if (localStorageGuiVisible === "false") {
1620
+ this.guiVisible = false;
1621
+ }
1622
+ }
1623
+ }
1624
+ this.renderer = renderer;
1625
+ this.scene = scene;
1626
+ this.composer = composer;
1627
+ this.render = this.gui.addFolder({ title: "rendering", expanded: true });
1628
+ this.stats = this.render.addFolder({ title: "stats", expanded: true });
1629
+ this.renderOptions = this.render.addFolder({ title: "renderOptions", expanded: false });
1630
+ this.toneMapping = this.render.addFolder({ title: "customToneMapping", expanded: false });
1631
+ this.ssao = this.render.addFolder({ title: "ambientOcclusion", expanded: false });
1632
+ this.post = this.render.addFolder({ title: "post", expanded: false });
1633
+ this.toneMapping.hidden = composerValues.renderer.toneMapping === 5 ? false : true;
1634
+ this.characterMaterial = this.gui.addFolder({ title: "characterMaterial", expanded: false });
1635
+ this.characterMaterial.addInput(
1636
+ characterValues.material,
1637
+ "transmission",
1638
+ characterOptions.material.transmission
1639
+ );
1640
+ this.characterMaterial.addInput(
1641
+ characterValues.material,
1642
+ "metalness",
1643
+ characterOptions.material.metalness
1644
+ );
1645
+ this.characterMaterial.addInput(
1646
+ characterValues.material,
1647
+ "roughness",
1648
+ characterOptions.material.roughness
1649
+ );
1650
+ this.characterMaterial.addInput(characterValues.material, "ior", characterOptions.material.ior);
1651
+ this.characterMaterial.addInput(
1652
+ characterValues.material,
1653
+ "thickness",
1654
+ characterOptions.material.thickness
1655
+ );
1656
+ this.characterMaterial.addInput(characterValues.material, "specularColor", {
1657
+ color: { type: "float" }
1658
+ });
1659
+ this.characterMaterial.addInput(
1660
+ characterValues.material,
1661
+ "specularIntensity",
1662
+ characterOptions.material.specularIntensity
1663
+ );
1664
+ this.characterMaterial.addInput(characterValues.material, "emissive", {
1665
+ color: { type: "float" }
1666
+ });
1667
+ this.characterMaterial.addInput(
1668
+ characterValues.material,
1669
+ "emissiveIntensity",
1670
+ characterOptions.material.emissiveIntensity
1671
+ );
1672
+ this.characterMaterial.addInput(
1673
+ characterValues.material,
1674
+ "envMapIntensity",
1675
+ characterOptions.material.envMapIntensity
1676
+ );
1677
+ this.characterMaterial.addInput(characterValues.material, "sheenColor", {
1678
+ color: { type: "float" }
1679
+ });
1680
+ this.characterMaterial.addInput(
1681
+ characterValues.material,
1682
+ "sheen",
1683
+ characterOptions.material.sheen
1684
+ );
1685
+ this.characterMaterial.addInput(
1686
+ characterValues.material,
1687
+ "clearcoat",
1688
+ characterOptions.material.clearcoat
1689
+ );
1690
+ this.characterMaterial.addInput(
1691
+ characterValues.material,
1692
+ "clearcoatRoughness",
1693
+ characterOptions.material.clearcoatRoughness
1694
+ );
1695
+ this.characterMaterial.on("change", (e) => {
1696
+ if (!e.presetKey) {
1697
+ return;
1698
+ }
1699
+ if (e.presetKey === "specularColor") {
1700
+ characterValues.material.specularColor = {
1701
+ r: e.value.r,
1702
+ g: e.value.g,
1703
+ b: e.value.b
1704
+ };
1705
+ return;
1706
+ }
1707
+ if (e.presetKey === "emissive") {
1708
+ characterValues.material.emissive = {
1709
+ r: e.value.r,
1710
+ g: e.value.g,
1711
+ b: e.value.b
1712
+ };
1713
+ return;
1714
+ }
1715
+ if (e.presetKey === "sheenColor") {
1716
+ characterValues.material.sheenColor = {
1717
+ r: e.value.r,
1718
+ g: e.value.g,
1719
+ b: e.value.b
1720
+ };
1721
+ return;
1722
+ }
1723
+ });
1724
+ this.export = this.gui.addFolder({ title: "import/export", expanded: false });
1725
+ window.addEventListener("keydown", this.processKey.bind(this));
1726
+ this.setupGUIListeners.bind(this)();
1727
+ this.setupRenderPane = this.setupRenderPane.bind(this);
1728
+ }
1729
+ processKey(e) {
1730
+ if (e.key === "p")
1731
+ this.toggleGUI();
1732
+ }
1733
+ setupGUIListeners() {
1734
+ const gui = this.gui;
1735
+ const paneElement = gui.containerElem_;
1736
+ paneElement.style.display = this.guiVisible ? "unset" : "none";
1737
+ this.gui.element.addEventListener("mousedown", () => setTweakpaneActive(true));
1738
+ this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
1739
+ this.gui.element.addEventListener("mouseleave", () => setTweakpaneActive(false));
1740
+ }
1741
+ setupRenderPane(ssaoEffect, toneMappingEffect, toneMappingPass, brightnessContrastSaturation, bloomEffect, gaussGrainEffect) {
1742
+ this.stats.addMonitor(statsData, "triangles");
1743
+ this.stats.addMonitor(statsData, "geometries");
1744
+ this.stats.addMonitor(statsData, "textures");
1745
+ this.stats.addMonitor(statsData, "shaders");
1746
+ this.stats.addMonitor(statsData, "postPasses");
1747
+ this.stats.addMonitor(statsData, "drawCalls");
1748
+ this.stats.addMonitor(statsData, "rawDeltaTime");
1749
+ this.stats.addMonitor(statsData, "deltaTime");
1750
+ this.stats.addMonitor(statsData, "FPS");
1751
+ this.renderOptions.addInput(
1752
+ composerValues.renderer,
1753
+ "shadowMap",
1754
+ composerOptions.renderer.shadowMap
1755
+ );
1756
+ this.renderOptions.addMonitor(rendererBlades, "shadowMapType");
1757
+ this.renderOptions.addInput(
1758
+ composerValues.renderer,
1759
+ "toneMapping",
1760
+ composerOptions.renderer.toneMapping
1761
+ );
1762
+ this.renderOptions.addMonitor(rendererBlades, "toneMappingType");
1763
+ this.renderOptions.addInput(
1764
+ composerValues.renderer,
1765
+ "exposure",
1766
+ composerOptions.renderer.exposure
1767
+ );
1768
+ this.renderOptions.addInput(
1769
+ composerValues.renderer,
1770
+ "bgIntensity",
1771
+ composerOptions.renderer.bgIntensity
1772
+ );
1773
+ this.renderOptions.addInput(
1774
+ composerValues.renderer,
1775
+ "bgBlurriness",
1776
+ composerOptions.renderer.bgBlurriness
1777
+ );
1778
+ this.renderOptions.on("change", (e) => {
1779
+ const target = e.target;
1780
+ switch (target.label) {
1781
+ case "shadowMap":
1782
+ this.renderer.shadowMap.type = e.value;
1783
+ setShadowMapType(e.value);
1784
+ break;
1785
+ case "toneMapping":
1786
+ this.renderer.toneMapping = e.value;
1787
+ if (e.value !== 5 && e.value !== 0) {
1788
+ this.toneMapping.hidden = true;
1789
+ } else {
1790
+ this.toneMapping.hidden = false;
1791
+ }
1792
+ toneMappingPass.enabled = e.value === 5 || e.value === 0 ? true : false;
1793
+ setToneMappingType(e.value);
1794
+ break;
1795
+ case "exposure":
1796
+ this.renderer.toneMappingExposure = e.value;
1797
+ break;
1798
+ case "bgIntensity":
1799
+ this.scene.backgroundIntensity = e.value;
1800
+ break;
1801
+ case "bgBlurriness":
1802
+ this.scene.backgroundBlurriness = e.value;
1803
+ break;
1804
+ default:
1805
+ break;
1806
+ }
1807
+ });
1808
+ this.ssao.addInput({ showEffectOnly: false }, "showEffectOnly");
1809
+ this.ssao.addInput(composerValues.ssao, "samples", composerOptions.ssao.samples);
1810
+ this.ssao.addInput(composerValues.ssao, "rings", composerOptions.ssao.rings);
1811
+ this.ssao.addInput(
1812
+ composerValues.ssao,
1813
+ "luminanceInfluence",
1814
+ composerOptions.ssao.luminanceInfluence
1815
+ );
1816
+ this.ssao.addInput(composerValues.ssao, "radius", composerOptions.ssao.radius);
1817
+ this.ssao.addInput(composerValues.ssao, "intensity", composerOptions.ssao.intensity);
1818
+ this.ssao.addInput(composerValues.ssao, "bias", composerOptions.ssao.bias);
1819
+ this.ssao.addInput(composerValues.ssao, "fade", composerOptions.ssao.fade);
1820
+ this.ssao.addInput(
1821
+ composerValues.ssao,
1822
+ "resolutionScale",
1823
+ composerOptions.ssao.resolutionScale
1824
+ );
1825
+ this.ssao.addInput(
1826
+ composerValues.ssao,
1827
+ "worldDistanceThreshold",
1828
+ composerOptions.ssao.worldDistanceThreshold
1829
+ );
1830
+ this.ssao.addInput(
1831
+ composerValues.ssao,
1832
+ "worldDistanceFalloff",
1833
+ composerOptions.ssao.worldDistanceFalloff
1834
+ );
1835
+ this.ssao.addInput(
1836
+ composerValues.ssao,
1837
+ "worldProximityThreshold",
1838
+ composerOptions.ssao.worldProximityThreshold
1839
+ );
1840
+ this.ssao.addInput(
1841
+ composerValues.ssao,
1842
+ "worldProximityFalloff",
1843
+ composerOptions.ssao.worldProximityFalloff
1844
+ );
1845
+ this.ssao.addInput(composerValues.ssao, "color", {
1846
+ color: { alpha: false, type: "float" }
1847
+ });
1848
+ this.ssao.on("change", (e) => {
1849
+ if (!e.presetKey) {
1850
+ return;
1851
+ }
1852
+ const preset = e.presetKey;
1853
+ if (preset === "showEffectOnly") {
1854
+ ssaoEffect.blendMode.blendFunction = e.value === true ? BlendFunction2.NORMAL : BlendFunction2.MULTIPLY;
1855
+ return;
1856
+ }
1857
+ if (preset === "resolutionScale") {
1858
+ ssaoEffect.resolution.scale = e.value;
1859
+ return;
1860
+ }
1861
+ if (ssaoMaterialParams.includes(e.presetKey)) {
1862
+ ssaoEffect.ssaoMaterial[preset] = e.value;
1863
+ return;
1864
+ }
1865
+ if (e.presetKey === "color") {
1866
+ ssaoEffect.color = new Color3().setRGB(e.value.r, e.value.g, e.value.b);
1867
+ return;
1868
+ }
1869
+ ssaoEffect[preset] = e.value;
1870
+ });
1871
+ this.toneMapping.addInput(composerValues.toneMapping, "mode", composerOptions.toneMapping.mode);
1872
+ this.toneMapping.addMonitor(customToneMappingBlade, "customToneMappingType");
1873
+ this.toneMapping.addInput(
1874
+ composerValues.toneMapping,
1875
+ "whitePoint",
1876
+ composerOptions.toneMapping.whitePoint
1877
+ );
1878
+ this.toneMapping.addInput(
1879
+ composerValues.toneMapping,
1880
+ "middleGrey",
1881
+ composerOptions.toneMapping.middleGrey
1882
+ );
1883
+ const minLuminance = this.toneMapping.addInput(
1884
+ composerValues.toneMapping,
1885
+ "minLuminance",
1886
+ composerOptions.toneMapping.minLuminance
1887
+ );
1888
+ minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1889
+ const averageLuminance = this.toneMapping.addInput(
1890
+ composerValues.toneMapping,
1891
+ "averageLuminance",
1892
+ composerOptions.toneMapping.averageLuminance
1893
+ );
1894
+ averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1895
+ this.toneMapping.addInput(
1896
+ composerValues.toneMapping,
1897
+ "adaptationRate",
1898
+ composerOptions.toneMapping.adaptationRate
1899
+ );
1900
+ this.toneMapping.on("change", (e) => {
1901
+ if (!e.presetKey) {
1902
+ return;
1903
+ }
1904
+ const preset = e.presetKey;
1905
+ if (preset === "mode") {
1906
+ minLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1907
+ averageLuminance.hidden = composerValues.toneMapping.mode === 2 ? true : false;
1908
+ setCustomToneMappingType(e.value);
1909
+ }
1910
+ toneMappingEffect[preset] = e.value;
1911
+ return;
1912
+ });
1913
+ this.post.addInput(composerValues, "brightness", composerOptions.brightness.amount);
1914
+ this.post.addInput(composerValues, "contrast", composerOptions.contrast.amount);
1915
+ this.post.addInput(composerValues, "saturation", composerOptions.saturation.amount);
1916
+ this.post.addInput(composerValues, "bloom", composerOptions.bloom.amount);
1917
+ this.post.addInput(composerValues, "grain", composerOptions.grain.amount);
1918
+ this.post.on("change", (e) => {
1919
+ const target = e.presetKey;
1920
+ switch (target) {
1921
+ case "brightness":
1922
+ brightnessContrastSaturation.uniforms.brightness.value = e.value;
1923
+ break;
1924
+ case "contrast":
1925
+ brightnessContrastSaturation.uniforms.contrast.value = e.value;
1926
+ break;
1927
+ case "saturation":
1928
+ brightnessContrastSaturation.uniforms.saturation.value = e.value;
1929
+ break;
1930
+ case "bloom":
1931
+ bloomEffect.intensity = e.value;
1932
+ break;
1933
+ case "grain":
1934
+ gaussGrainEffect.uniforms.amount.value = e.value;
1935
+ break;
1936
+ default:
1937
+ break;
1938
+ }
1939
+ });
1940
+ const exportButton = this.export.addButton({ title: "export" });
1941
+ exportButton.on("click", () => {
1942
+ this.downloadSettingsAsJSON(this.gui.exportPreset());
1943
+ });
1944
+ const importButton = this.export.addButton({ title: "import" });
1945
+ importButton.on("click", () => {
1946
+ this.importSettingsFromJSON((settings) => {
1947
+ this.gui.importPreset(settings);
1948
+ });
1949
+ });
1950
+ }
1951
+ formatDateForFilename() {
1952
+ const date = /* @__PURE__ */ new Date();
1953
+ const year = date.getFullYear();
1954
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1955
+ const day = String(date.getDate()).padStart(2, "0");
1956
+ const hours = String(date.getHours()).padStart(2, "0");
1957
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1958
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1959
+ return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`;
1960
+ }
1961
+ downloadSettingsAsJSON(settings) {
1962
+ const jsonString = JSON.stringify(settings, null, 2);
1963
+ const blob = new Blob([jsonString], { type: "application/json" });
1964
+ const url = URL.createObjectURL(blob);
1965
+ const a = document.createElement("a");
1966
+ a.download = `settings ${this.formatDateForFilename()}.json`;
1967
+ a.href = url;
1968
+ a.click();
1969
+ URL.revokeObjectURL(url);
1970
+ }
1971
+ importSettingsFromJSON(callback) {
1972
+ const input = document.createElement("input");
1973
+ input.type = "file";
1974
+ input.accept = ".json";
1975
+ input.addEventListener("change", (event) => {
1976
+ const file = event.target.files?.[0];
1977
+ if (file) {
1978
+ const reader = new FileReader();
1979
+ reader.onload = (loadEvent) => {
1980
+ try {
1981
+ const settings = JSON.parse(loadEvent.target?.result);
1982
+ callback(settings);
1983
+ } catch (err) {
1984
+ console.error("Error parsing JSON:", err);
1985
+ }
1986
+ };
1987
+ reader.readAsText(file);
1988
+ }
1989
+ });
1990
+ input.click();
1991
+ }
1992
+ updateStats(timeManager) {
1993
+ const { geometries, textures } = this.renderer.info.memory;
1994
+ const { triangles, calls } = this.renderer.info.render;
1995
+ statsData.triangles = triangles.toString();
1996
+ statsData.geometries = geometries.toString();
1997
+ statsData.textures = textures.toString();
1998
+ statsData.shaders = this.renderer.info.programs.length.toString();
1999
+ statsData.postPasses = this.composer.passes.length.toString();
2000
+ statsData.drawCalls = calls.toString();
2001
+ statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2002
+ statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2003
+ statsData.FPS = timeManager.fps.toString();
2004
+ }
2005
+ toggleGUI() {
2006
+ const gui = this.gui;
2007
+ const paneElement = gui.containerElem_;
2008
+ paneElement.style.display = this.guiVisible ? "none" : "unset";
2009
+ this.guiVisible = !this.guiVisible;
2010
+ if (this.saveVisibilityInLocalStorage) {
2011
+ localStorage.setItem("guiVisible", this.guiVisible === true ? "true" : "false");
2012
+ }
2013
+ }
2014
+ };
2015
+
2016
+ // src/rendering/post-effects/bright-contrast-sat.ts
2017
+ import { ShaderMaterial, Uniform } from "three";
1147
2018
 
1148
2019
  // src/rendering/shaders/vertex-shader.ts
1149
2020
  var vertexShader = (
@@ -1159,14 +2030,86 @@ var vertexShader = (
1159
2030
  `
1160
2031
  );
1161
2032
 
1162
- // src/rendering/post-effects/gauss-grain.ts
1163
- var GaussGrainEffect = new ShaderMaterial({
2033
+ // src/rendering/post-effects/bright-contrast-sat.ts
2034
+ var BrightnessContrastSaturation = new ShaderMaterial({
1164
2035
  uniforms: {
1165
2036
  tDiffuse: new Uniform(null),
1166
- resolution: new Uniform(new Vector2()),
1167
- time: new Uniform(0),
1168
- amount: new Uniform(0),
1169
- alpha: new Uniform(0)
2037
+ brightness: new Uniform(0),
2038
+ contrast: new Uniform(1),
2039
+ saturation: new Uniform(1)
2040
+ },
2041
+ vertexShader,
2042
+ fragmentShader: (
2043
+ /* glsl */
2044
+ `
2045
+ precision highp float;
2046
+ in vec2 vUv;
2047
+
2048
+ uniform sampler2D tDiffuse;
2049
+ uniform float brightness;
2050
+ uniform float contrast;
2051
+ uniform float saturation;
2052
+
2053
+ mat4 brightnessMatrix(float brightness) {
2054
+ return mat4(
2055
+ 1, 0, 0, 0,
2056
+ 0, 1, 0, 0,
2057
+ 0, 0, 1, 0,
2058
+ brightness, brightness, brightness, 1
2059
+ );
2060
+ }
2061
+
2062
+ mat4 contrastMatrix(float contrast) {
2063
+ float t = (1.0 - contrast) / 2.0;
2064
+
2065
+ return mat4(
2066
+ contrast, 0, 0, 0,
2067
+ 0, contrast, 0, 0,
2068
+ 0, 0, contrast, 0,
2069
+ t, t, t, 1
2070
+ );
2071
+ }
2072
+
2073
+ mat4 saturationMatrix(float saturation) {
2074
+ vec3 luminance = vec3(0.3086, 0.6094, 0.0820);
2075
+ float oneMinusSat = 1.0 - saturation;
2076
+ vec3 red = vec3(luminance.x * oneMinusSat);
2077
+ red += vec3(saturation, 0, 0);
2078
+ vec3 green = vec3(luminance.y * oneMinusSat);
2079
+ green += vec3(0, saturation, 0);
2080
+ vec3 blue = vec3(luminance.z * oneMinusSat);
2081
+ blue += vec3(0, 0, saturation);
2082
+ return mat4(
2083
+ red, 0,
2084
+ green, 0,
2085
+ blue, 0,
2086
+ 0, 0, 0, 1
2087
+ );
2088
+ }
2089
+
2090
+ void main(void) {
2091
+ vec4 color = texture(tDiffuse, vUv);
2092
+ gl_FragColor = (
2093
+ brightnessMatrix(brightness) *
2094
+ contrastMatrix(contrast) *
2095
+ saturationMatrix(saturation) *
2096
+ color
2097
+ );
2098
+ }
2099
+ `
2100
+ ),
2101
+ dithering: true
2102
+ });
2103
+
2104
+ // src/rendering/post-effects/gauss-grain.ts
2105
+ import { ShaderMaterial as ShaderMaterial2, Uniform as Uniform2, Vector2 } from "three";
2106
+ var GaussGrainEffect = new ShaderMaterial2({
2107
+ uniforms: {
2108
+ tDiffuse: new Uniform2(null),
2109
+ resolution: new Uniform2(new Vector2()),
2110
+ time: new Uniform2(0),
2111
+ amount: new Uniform2(0),
2112
+ alpha: new Uniform2(0)
1170
2113
  },
1171
2114
  vertexShader,
1172
2115
  fragmentShader: (
@@ -1193,7 +2136,7 @@ var GaussGrainEffect = new ShaderMaterial({
1193
2136
  }
1194
2137
 
1195
2138
  vec3 gaussgrain() {
1196
- vec2 ps = vec2(1.01) / resolution.xy;
2139
+ vec2 ps = vec2(1.0) / resolution.xy;
1197
2140
  vec2 uv = gl_FragCoord.xy * ps;
1198
2141
  float t = time;
1199
2142
  float seed = dot(uv, vec2(12.9898, 78.233));
@@ -1210,7 +2153,8 @@ var GaussGrainEffect = new ShaderMaterial({
1210
2153
  gl_FragColor = vec4(clamp(col, 0.0, 1.0), alpha);
1211
2154
  }
1212
2155
  `
1213
- )
2156
+ ),
2157
+ dithering: true
1214
2158
  });
1215
2159
 
1216
2160
  // src/rendering/composer.ts
@@ -1219,6 +2163,7 @@ var Composer = class {
1219
2163
  __publicField(this, "width", window.innerWidth);
1220
2164
  __publicField(this, "height", window.innerHeight);
1221
2165
  __publicField(this, "resolution", new Vector22(this.width, this.height));
2166
+ __publicField(this, "isEnvHDRI", false);
1222
2167
  __publicField(this, "scene");
1223
2168
  __publicField(this, "camera");
1224
2169
  __publicField(this, "renderer");
@@ -1228,57 +2173,165 @@ var Composer = class {
1228
2173
  __publicField(this, "fxaaPass");
1229
2174
  __publicField(this, "bloomEffect");
1230
2175
  __publicField(this, "bloomPass");
2176
+ __publicField(this, "toneMappingEffect");
2177
+ __publicField(this, "smaaEffect");
2178
+ __publicField(this, "normalPass");
2179
+ __publicField(this, "normalTextureEffect");
2180
+ __publicField(this, "ssaoEffect");
2181
+ __publicField(this, "ssaoPass");
2182
+ __publicField(this, "toneMappingPass");
2183
+ __publicField(this, "smaaPass");
2184
+ __publicField(this, "bcs", BrightnessContrastSaturation);
2185
+ __publicField(this, "bcsPass");
1231
2186
  __publicField(this, "gaussGrainEffect", GaussGrainEffect);
1232
2187
  __publicField(this, "gaussGrainPass");
2188
+ __publicField(this, "tweakPane");
1233
2189
  this.scene = scene;
1234
2190
  this.camera = camera;
1235
- this.renderer = new WebGLRenderer2({
2191
+ this.renderer = new WebGLRenderer3({
1236
2192
  powerPreference: "high-performance",
1237
2193
  antialias: false,
1238
2194
  stencil: false,
1239
2195
  depth: false
1240
2196
  });
2197
+ this.renderer.info.autoReset = false;
1241
2198
  this.renderer.setSize(this.width, this.height);
1242
2199
  this.renderer.shadowMap.enabled = true;
1243
- this.renderer.shadowMap.type = PCFSoftShadowMap;
1244
- this.renderer.toneMapping = ACESFilmicToneMapping;
1245
- this.renderer.toneMappingExposure = 0.5;
2200
+ this.renderer.shadowMap.type = composerValues.renderer.shadowMap;
2201
+ this.renderer.toneMapping = composerValues.renderer.toneMapping;
2202
+ this.renderer.toneMappingExposure = composerValues.renderer.exposure;
1246
2203
  document.body.appendChild(this.renderer.domElement);
1247
- this.composer = new EffectComposer(this.renderer);
2204
+ this.composer = new EffectComposer2(this.renderer, {
2205
+ frameBufferType: HalfFloatType
2206
+ });
2207
+ this.tweakPane = new TweakPane(this.renderer, this.scene, this.composer);
1248
2208
  this.renderPass = new RenderPass(this.scene, this.camera);
2209
+ this.normalPass = new NormalPass(this.scene, this.camera);
2210
+ this.normalTextureEffect = new TextureEffect({
2211
+ blendFunction: BlendFunction3.SKIP,
2212
+ texture: this.normalPass.texture
2213
+ });
1249
2214
  this.fxaaEffect = new FXAAEffect();
1250
- this.fxaaPass = new EffectPass(this.camera, this.fxaaEffect);
1251
- this.bloomEffect = new BloomEffect();
1252
- this.bloomPass = new EffectPass(this.camera, this.bloomEffect);
2215
+ this.bloomEffect = new BloomEffect2({
2216
+ intensity: composerValues.bloom
2217
+ });
2218
+ this.ssaoEffect = new SSAOEffect2(this.camera, this.normalPass.texture, {
2219
+ blendFunction: composerValues.ssao.blendFunction,
2220
+ distanceScaling: composerValues.ssao.distanceScaling,
2221
+ depthAwareUpsampling: composerValues.ssao.depthAwareUpsampling,
2222
+ samples: composerValues.ssao.samples,
2223
+ rings: composerValues.ssao.rings,
2224
+ luminanceInfluence: composerValues.ssao.luminanceInfluence,
2225
+ radius: composerValues.ssao.radius,
2226
+ intensity: composerValues.ssao.intensity,
2227
+ bias: composerValues.ssao.bias,
2228
+ fade: composerValues.ssao.fade,
2229
+ resolutionScale: composerValues.ssao.resolutionScale,
2230
+ color: new Color4().setRGB(composerValues.ssao.color.r, composerValues.ssao.color.g, composerValues.ssao.color.b),
2231
+ worldDistanceThreshold: composerValues.ssao.worldDistanceThreshold,
2232
+ worldDistanceFalloff: composerValues.ssao.worldDistanceFalloff,
2233
+ worldProximityThreshold: composerValues.ssao.worldProximityThreshold,
2234
+ worldProximityFalloff: composerValues.ssao.worldProximityFalloff
2235
+ });
2236
+ this.fxaaPass = new EffectPass2(this.camera, this.fxaaEffect);
2237
+ this.bloomPass = new EffectPass2(this.camera, this.bloomEffect);
2238
+ this.ssaoPass = new EffectPass2(this.camera, this.ssaoEffect, this.normalTextureEffect);
2239
+ this.toneMappingEffect = new ToneMappingEffect2({
2240
+ mode: composerValues.toneMapping.mode,
2241
+ resolution: composerValues.toneMapping.resolution,
2242
+ whitePoint: composerValues.toneMapping.whitePoint,
2243
+ middleGrey: composerValues.toneMapping.middleGrey,
2244
+ minLuminance: composerValues.toneMapping.minLuminance,
2245
+ averageLuminance: composerValues.toneMapping.averageLuminance,
2246
+ adaptationRate: composerValues.toneMapping.adaptationRate
2247
+ });
2248
+ this.smaaEffect = new SMAAEffect({
2249
+ preset: SMAAPreset.ULTRA,
2250
+ edgeDetectionMode: EdgeDetectionMode.COLOR,
2251
+ predicationMode: PredicationMode.DEPTH
2252
+ });
2253
+ this.toneMappingPass = new EffectPass2(this.camera, this.toneMappingEffect);
2254
+ this.toneMappingPass.enabled = composerValues.renderer.toneMapping === 5 || composerValues.renderer.toneMapping === 0 ? true : false;
2255
+ this.bcsPass = new ShaderPass(this.bcs, "tDiffuse");
2256
+ this.bcs.uniforms.brightness.value = composerValues.brightness;
2257
+ this.bcs.uniforms.contrast.value = composerValues.contrast;
2258
+ this.bcs.uniforms.saturation.value = composerValues.saturation;
1253
2259
  this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
2260
+ this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
1254
2261
  this.composer.addPass(this.renderPass);
2262
+ this.composer.addPass(this.normalPass);
2263
+ this.composer.addPass(this.ssaoPass);
1255
2264
  this.composer.addPass(this.fxaaPass);
2265
+ this.composer.addPass(this.smaaPass);
1256
2266
  this.composer.addPass(this.bloomPass);
2267
+ this.composer.addPass(this.toneMappingPass);
2268
+ this.composer.addPass(this.bcsPass);
1257
2269
  this.composer.addPass(this.gaussGrainPass);
1258
- window.addEventListener("resize", () => {
1259
- this.updateProjection();
1260
- });
2270
+ this.tweakPane.setupRenderPane(
2271
+ this.ssaoEffect,
2272
+ this.toneMappingEffect,
2273
+ this.toneMappingPass,
2274
+ this.bcs,
2275
+ this.bloomEffect,
2276
+ this.gaussGrainEffect
2277
+ );
2278
+ window.addEventListener("resize", () => this.updateProjection());
1261
2279
  this.updateProjection();
1262
2280
  }
1263
2281
  updateProjection() {
1264
2282
  this.width = window.innerWidth;
1265
2283
  this.height = innerHeight;
1266
2284
  this.resolution = new Vector22(this.width, this.height);
1267
- if (this.composer)
1268
- this.composer.setSize(this.width, this.height);
1269
- if (this.fxaaPass)
1270
- this.fxaaPass.setSize(this.width, this.height);
1271
- if (this.renderPass)
1272
- this.renderPass.setSize(this.width, this.height);
2285
+ this.composer.setSize(this.width, this.height);
2286
+ this.renderPass.setSize(this.width, this.height);
2287
+ this.normalPass.setSize(this.width, this.height);
2288
+ this.ssaoPass.setSize(this.width, this.height);
2289
+ this.fxaaPass.setSize(this.width, this.height);
2290
+ this.smaaPass.setSize(this.width, this.height);
2291
+ this.bloomPass.setSize(this.width, this.height);
2292
+ this.toneMappingPass.setSize(this.width, this.height);
2293
+ this.gaussGrainPass.setSize(this.width, this.height);
1273
2294
  this.renderer.setSize(this.width, this.height);
1274
2295
  }
1275
- render(time) {
1276
- this.composer.render();
2296
+ isTweakPaneVisible() {
2297
+ return this.tweakPane.guiVisible;
2298
+ }
2299
+ render(timeManager) {
2300
+ this.renderer.info.reset();
2301
+ this.normalPass.texture.needsUpdate = true;
1277
2302
  this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
1278
- this.gaussGrainEffect.uniforms.time.value = time;
2303
+ this.gaussGrainEffect.uniforms.time.value = timeManager.time;
1279
2304
  this.gaussGrainEffect.uniforms.alpha.value = 1;
1280
- this.gaussGrainEffect.uniforms.amount.value = 0.035;
1281
- this.bloomEffect.intensity = 1;
2305
+ this.composer.render();
2306
+ if (this.tweakPane.guiVisible) {
2307
+ this.tweakPane.updateStats(timeManager);
2308
+ }
2309
+ }
2310
+ useHDRI(url) {
2311
+ if (this.isEnvHDRI || !this.renderer)
2312
+ return;
2313
+ const pmremGenerator = new PMREMGenerator(this.renderer);
2314
+ new RGBELoader(new LoadingManager2()).load(
2315
+ url,
2316
+ (texture) => {
2317
+ const envMap = pmremGenerator.fromEquirectangular(texture).texture;
2318
+ if (envMap) {
2319
+ envMap.colorSpace = LinearSRGBColorSpace;
2320
+ envMap.needsUpdate = true;
2321
+ this.scene.environment = envMap;
2322
+ this.scene.background = envMap;
2323
+ this.scene.backgroundIntensity = composerValues.renderer.bgIntensity;
2324
+ this.isEnvHDRI = true;
2325
+ texture.dispose();
2326
+ pmremGenerator.dispose();
2327
+ }
2328
+ },
2329
+ () => {
2330
+ },
2331
+ (error) => {
2332
+ console.error(`Can't load ${url}: ${JSON.stringify(error)}`);
2333
+ }
2334
+ );
1282
2335
  }
1283
2336
  };
1284
2337
 
@@ -1288,7 +2341,7 @@ var TimeManager = class {
1288
2341
  constructor() {
1289
2342
  __publicField(this, "clock", new Clock());
1290
2343
  __publicField(this, "roundMagnitude", 2e5);
1291
- __publicField(this, "maxAverageFrames", 300);
2344
+ __publicField(this, "maxAverageFrames", 150);
1292
2345
  __publicField(this, "deltaTimes", []);
1293
2346
  __publicField(this, "targetAverageDeltaTime", 0);
1294
2347
  __publicField(this, "lerpedAverageMagDelta", 0);
@@ -1297,17 +2350,18 @@ var TimeManager = class {
1297
2350
  __publicField(this, "time", 0);
1298
2351
  __publicField(this, "deltaTime", 0);
1299
2352
  __publicField(this, "rawDeltaTime", 0);
2353
+ __publicField(this, "smoothDeltaTime", 0);
1300
2354
  __publicField(this, "frame", 0);
1301
2355
  __publicField(this, "fps", 0);
2356
+ __publicField(this, "averageFPS", 0);
1302
2357
  }
1303
2358
  update() {
1304
2359
  this.rawDeltaTime = this.clock.getDelta();
1305
2360
  this.frame++;
1306
2361
  this.time += this.rawDeltaTime;
1307
2362
  this.deltaTimes.push(this.rawDeltaTime);
1308
- if (this.deltaTimes.length > this.maxAverageFrames) {
2363
+ if (this.deltaTimes.length > this.maxAverageFrames)
1309
2364
  this.deltaTimes.shift();
1310
- }
1311
2365
  this.targetAverageDeltaTime = this.deltaTimes.reduce((prev, curr) => prev + curr, 0) / this.deltaTimes.length;
1312
2366
  this.lerpedAverageMagDelta += ease(
1313
2367
  this.targetAverageDeltaTime * this.roundMagnitude,
@@ -1316,9 +2370,10 @@ var TimeManager = class {
1316
2370
  );
1317
2371
  const revertMagnitude = this.lerpedAverageMagDelta / this.roundMagnitude;
1318
2372
  const smoothDT = Math.round(revertMagnitude * this.roundMagnitude) / this.roundMagnitude;
1319
- this.deltaTime = smoothDT > this.rawDeltaTime * 1.75 ? this.rawDeltaTime : smoothDT;
2373
+ this.smoothDeltaTime = smoothDT > this.rawDeltaTime * 1.75 ? this.rawDeltaTime : smoothDT;
2374
+ this.deltaTime = this.smoothDeltaTime;
1320
2375
  this.framesSinceLastFPSUpdate++;
1321
- if (this.framesSinceLastFPSUpdate >= this.maxAverageFrames) {
2376
+ if (this.framesSinceLastFPSUpdate >= 60) {
1322
2377
  this.fps = Math.round(this.framesSinceLastFPSUpdate / (this.time - this.fpsUpdateTime) * 100) / 100;
1323
2378
  this.fpsUpdateTime = this.time;
1324
2379
  this.framesSinceLastFPSUpdate = 0;
@@ -1332,7 +2387,8 @@ import {
1332
2387
  getRelativePositionAndRotationRelativeToObject
1333
2388
  } from "mml-web";
1334
2389
  import {
1335
- Color as Color3,
2390
+ Color as Color5,
2391
+ DoubleSide,
1336
2392
  Euler,
1337
2393
  FrontSide,
1338
2394
  Mesh as Mesh2,
@@ -1352,6 +2408,18 @@ var CollisionsManager = class {
1352
2408
  this.scene = scene;
1353
2409
  this.collisionTrigger = MMLCollisionTrigger.init();
1354
2410
  }
2411
+ raycastFirstDistance(ray) {
2412
+ let minimumDistance = Infinity;
2413
+ for (const [, value] of this.collisionMeshState) {
2414
+ const hit = value.meshBVH.raycast(ray, DoubleSide);
2415
+ if (hit.length > 0) {
2416
+ if (hit[0].distance < minimumDistance) {
2417
+ minimumDistance = hit[0].distance;
2418
+ }
2419
+ }
2420
+ }
2421
+ return minimumDistance;
2422
+ }
1355
2423
  createCollisionMeshState(group) {
1356
2424
  const geometries = [];
1357
2425
  group.traverse((child) => {
@@ -1384,7 +2452,7 @@ var CollisionsManager = class {
1384
2452
  );
1385
2453
  mergedMesh.geometry.boundsTree = meshBVH;
1386
2454
  const visualizer = new MeshBVHVisualizer(mergedMesh, 3);
1387
- visualizer.edgeMaterial.color = new Color3(255);
2455
+ visualizer.edgeMaterial.color = new Color5(255);
1388
2456
  visualizer.update();
1389
2457
  return { source: group, visualizer, meshBVH };
1390
2458
  }