@mml-io/3d-web-client-core 0.8.0 → 0.10.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
@@ -34,13 +34,51 @@ var round = (n, digits) => {
34
34
  var ease = (target, n, factor) => {
35
35
  return round((target - n) * factor, 5);
36
36
  };
37
- function clamp(value, min, max) {
38
- return Math.min(Math.max(value, min), max);
39
- }
40
37
  var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
41
38
  return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
42
39
  };
43
40
 
41
+ // src/input/EventHandlerCollection.ts
42
+ var EventHandlerCollection = class _EventHandlerCollection {
43
+ constructor() {
44
+ this.eventsByTarget = /* @__PURE__ */ new Map();
45
+ }
46
+ add(target, key, listener, options) {
47
+ target.addEventListener(key, listener, options);
48
+ let existingTarget = this.eventsByTarget.get(target);
49
+ if (existingTarget === void 0) {
50
+ existingTarget = /* @__PURE__ */ new Map();
51
+ this.eventsByTarget.set(target, existingTarget);
52
+ }
53
+ let existingKey = existingTarget.get(key);
54
+ if (existingKey === void 0) {
55
+ existingKey = /* @__PURE__ */ new Set();
56
+ existingTarget.set(key, existingKey);
57
+ }
58
+ existingKey.add(listener);
59
+ return this;
60
+ }
61
+ clear() {
62
+ this.eventsByTarget.forEach((keyMap, target) => {
63
+ keyMap.forEach((listenerSet, key) => {
64
+ listenerSet.forEach((listenerFunc) => {
65
+ target.removeEventListener(key, listenerFunc);
66
+ });
67
+ });
68
+ });
69
+ this.eventsByTarget.clear();
70
+ }
71
+ static create(initial) {
72
+ const instance = new _EventHandlerCollection();
73
+ if (initial !== void 0) {
74
+ initial.forEach(([target, key, listenerFunc, options]) => {
75
+ instance.add(target, key, listenerFunc, options);
76
+ });
77
+ }
78
+ return instance;
79
+ }
80
+ };
81
+
44
82
  // src/tweakpane/tweakPaneActivity.ts
45
83
  var isTweakpaneActive = false;
46
84
  function setTweakpaneActive(status) {
@@ -52,7 +90,7 @@ function getTweakpaneActive() {
52
90
 
53
91
  // src/camera/CameraManager.ts
54
92
  var CameraManager = class {
55
- constructor(collisionsManager) {
93
+ constructor(targetElement, collisionsManager, initialPhi = Math.PI / 2, initialTheta = -Math.PI / 2) {
56
94
  this.collisionsManager = collisionsManager;
57
95
  this.initialDistance = 3.3;
58
96
  this.minDistance = 0.1;
@@ -68,42 +106,43 @@ var CameraManager = class {
68
106
  this.targetDistance = this.initialDistance;
69
107
  this.distance = this.initialDistance;
70
108
  this.desiredDistance = this.initialDistance;
71
- this.targetPhi = Math.PI / 2;
72
- this.phi = Math.PI / 2;
73
- this.targetTheta = -Math.PI / 2;
74
- this.theta = -Math.PI / 2;
75
109
  this.dragging = false;
76
110
  this.target = new Vector32(0, 1.55, 0);
77
111
  this.hadTarget = false;
112
+ this.isLerping = false;
113
+ this.finalTarget = new Vector32();
114
+ this.lerpTarget = new Vector32();
115
+ this.lerpFactor = 0;
116
+ this.lerpDuration = 2.1;
117
+ this.phi = initialPhi;
118
+ this.targetPhi = initialPhi;
119
+ this.theta = initialTheta;
120
+ this.targetTheta = initialTheta;
78
121
  this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
79
122
  this.camera.position.set(0, 1.4, -this.initialDistance);
80
123
  this.rayCaster = new Raycaster();
81
- document.addEventListener("mousedown", this.onMouseDown.bind(this));
82
- document.addEventListener("mouseup", this.onMouseUp.bind(this));
83
- document.addEventListener("mousemove", this.onMouseMove.bind(this));
84
- document.addEventListener("wheel", this.onMouseWheel.bind(this));
85
- window.addEventListener("resize", this.onResize.bind(this));
86
- }
87
- onResize() {
88
- const width = window.innerWidth;
89
- const height = window.innerHeight;
90
- this.camera.aspect = width / height;
91
- this.camera.updateProjectionMatrix();
92
- }
93
- onMouseDown(_event) {
124
+ this.eventHandlerCollection = EventHandlerCollection.create([
125
+ [targetElement, "mousedown", this.onMouseDown.bind(this)],
126
+ [document, "mouseup", this.onMouseUp.bind(this)],
127
+ [document, "mousemove", this.onMouseMove.bind(this)],
128
+ [targetElement, "wheel", this.onMouseWheel.bind(this)]
129
+ ]);
130
+ }
131
+ onMouseDown() {
94
132
  this.dragging = true;
95
133
  }
96
134
  onMouseUp(_event) {
97
135
  this.dragging = false;
98
136
  }
99
137
  onMouseMove(event) {
100
- if (!this.dragging || getTweakpaneActive() === true)
138
+ if (!this.dragging || getTweakpaneActive())
101
139
  return;
102
140
  if (this.targetTheta === null || this.targetPhi === null)
103
141
  return;
104
142
  this.targetTheta += event.movementX * 0.01;
105
143
  this.targetPhi -= event.movementY * 0.01;
106
144
  this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
145
+ event.preventDefault();
107
146
  }
108
147
  onMouseWheel(event) {
109
148
  const scrollAmount = event.deltaY * 1e-3;
@@ -113,14 +152,27 @@ var CameraManager = class {
113
152
  Math.min(this.maxDistance, this.targetDistance)
114
153
  );
115
154
  this.desiredDistance = this.targetDistance;
155
+ event.preventDefault();
116
156
  }
117
157
  setTarget(target) {
118
- this.target.copy(target);
158
+ if (!this.isLerping) {
159
+ this.target.copy(target);
160
+ } else {
161
+ this.finalTarget.copy(target);
162
+ this.lerpTarget.copy(this.target);
163
+ this.lerpFactor = 0;
164
+ }
119
165
  if (!this.hadTarget) {
120
166
  this.hadTarget = true;
121
167
  this.reverseUpdateFromPositions();
122
168
  }
123
169
  }
170
+ setLerpedTarget(target, targetDistance) {
171
+ this.isLerping = true;
172
+ this.targetDistance = targetDistance;
173
+ this.desiredDistance = targetDistance;
174
+ this.setTarget(target);
175
+ }
124
176
  reverseUpdateFromPositions() {
125
177
  if (this.phi === null || this.theta == null)
126
178
  return;
@@ -148,9 +200,23 @@ var CameraManager = class {
148
200
  this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
149
201
  }
150
202
  }
203
+ dispose() {
204
+ this.eventHandlerCollection.clear();
205
+ }
206
+ easeOutExpo(x) {
207
+ return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
208
+ }
209
+ updateAspect(aspect) {
210
+ this.camera.aspect = aspect;
211
+ }
151
212
  update() {
152
- if (this.target === null)
153
- return;
213
+ if (this.isLerping && this.lerpFactor < 1) {
214
+ this.lerpFactor += 0.01 / this.lerpDuration;
215
+ this.lerpFactor = Math.min(1, this.lerpFactor);
216
+ this.target.lerpVectors(this.lerpTarget, this.finalTarget, this.easeOutExpo(this.lerpFactor));
217
+ } else {
218
+ this.adjustCameraPosition();
219
+ }
154
220
  if (this.phi !== null && this.targetPhi !== null && this.theta !== null && this.targetTheta !== null) {
155
221
  this.distance += (this.targetDistance - this.distance) * this.dampingFactor * 0.21;
156
222
  this.phi += (this.targetPhi - this.phi) * this.dampingFactor;
@@ -165,13 +231,14 @@ var CameraManager = class {
165
231
  this.minFOV,
166
232
  this.maxFOV
167
233
  );
168
- this.fov += ease(this.targetFOV, this.fov, 0.07);
234
+ this.fov += (this.targetFOV - this.fov) * this.dampingFactor;
169
235
  this.camera.fov = this.fov;
170
236
  this.camera.updateProjectionMatrix();
171
- this.camera.updateMatrixWorld();
172
- this.camera.position.set(x, clamp(y, 0.1, Infinity), z);
173
- this.adjustCameraPosition();
237
+ this.camera.position.set(x, y, z);
174
238
  this.camera.lookAt(this.target);
239
+ if (this.isLerping && this.lerpFactor >= 1) {
240
+ this.isLerping = false;
241
+ }
175
242
  }
176
243
  }
177
244
  };
@@ -181,11 +248,11 @@ import { Color as Color3, Vector3 as Vector35 } from "three";
181
248
 
182
249
  // src/character/CharacterModel.ts
183
250
  import {
184
- AnimationClip as AnimationClip2,
251
+ AnimationClip,
185
252
  AnimationMixer,
186
253
  LoopRepeat,
187
254
  MeshStandardMaterial,
188
- Object3D as Object3D2
255
+ Object3D
189
256
  } from "three";
190
257
 
191
258
  // src/character/CharacterMaterial.ts
@@ -560,126 +627,11 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
560
627
  return AnimationState2;
561
628
  })(AnimationState || {});
562
629
 
563
- // src/character/ModelLoader.ts
564
- import { LoadingManager } from "three";
565
- import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
566
- var CachedGLTFLoader = class extends ThreeGLTFLoader {
567
- constructor(manager) {
568
- super(manager);
569
- this.blobCache = /* @__PURE__ */ new Map();
570
- }
571
- setBlobUrl(originalUrl, blobUrl) {
572
- this.blobCache.set(originalUrl, blobUrl);
573
- }
574
- getBlobUrl(originalUrl) {
575
- return this.blobCache.get(originalUrl);
576
- }
577
- load(url, onLoad, onProgress, onError) {
578
- const blobUrl = this.getBlobUrl(url);
579
- if (blobUrl) {
580
- console.log(`Loading cached ${url.split("/").pop()}`);
581
- super.load(blobUrl, onLoad, onProgress, onError);
582
- } else {
583
- super.load(url, onLoad, onProgress, onError);
584
- }
585
- }
586
- };
587
- var LRUCache = class {
588
- constructor(maxSize = 100) {
589
- this.maxSize = maxSize;
590
- this.cache = /* @__PURE__ */ new Map();
591
- }
592
- get(key) {
593
- const item = this.cache.get(key);
594
- if (item) {
595
- this.cache.delete(key);
596
- this.cache.set(key, item);
597
- }
598
- return item;
599
- }
600
- set(key, value) {
601
- if (this.cache.size >= this.maxSize) {
602
- const oldestKey = this.cache.keys().next().value;
603
- this.cache.delete(oldestKey);
604
- }
605
- this.cache.set(key, value);
606
- }
607
- };
608
- var _ModelLoader = class _ModelLoader {
609
- constructor(maxCacheSize = 100) {
610
- this.ongoingLoads = /* @__PURE__ */ new Map();
611
- this.loadingManager = new LoadingManager();
612
- this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
613
- this.modelCache = new LRUCache(maxCacheSize);
614
- }
615
- /* TODO: decide between below lazy initialization or eager on this file's bottom export */
616
- static getInstance() {
617
- if (!_ModelLoader.instance) {
618
- _ModelLoader.instance = new _ModelLoader();
619
- }
620
- return _ModelLoader.instance;
621
- }
622
- async load(fileUrl, fileType) {
623
- const cachedModel = this.modelCache.get(fileUrl);
624
- if (cachedModel) {
625
- const blobURL = URL.createObjectURL(cachedModel.blob);
626
- this.gltfLoader.setBlobUrl(fileUrl, blobURL);
627
- return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
628
- } else {
629
- console.log(`Loading ${fileUrl} from server`);
630
- const ongoingLoad = this.ongoingLoads.get(fileUrl);
631
- if (ongoingLoad)
632
- return ongoingLoad;
633
- const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
634
- const originalExtension = fileUrl.split(".").pop() || "";
635
- this.modelCache.set(fileUrl, { blob, originalExtension });
636
- const blobURL = URL.createObjectURL(blob);
637
- this.ongoingLoads.delete(fileUrl);
638
- return this.loadFromUrl(blobURL, fileType, originalExtension);
639
- });
640
- this.ongoingLoads.set(fileUrl, loadPromise);
641
- return loadPromise;
642
- }
643
- }
644
- async loadFromUrl(url, fileType, extension) {
645
- if (["gltf", "glb"].includes(extension)) {
646
- return new Promise((resolve, reject) => {
647
- this.gltfLoader.load(
648
- url,
649
- (object) => {
650
- if (fileType === "model") {
651
- resolve(object.scene);
652
- } else if (fileType === "animation") {
653
- resolve(object.animations[0]);
654
- } else {
655
- const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
656
- console.error(error);
657
- reject(error);
658
- }
659
- },
660
- void 0,
661
- (error) => {
662
- console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
663
- reject(error);
664
- }
665
- );
666
- });
667
- } else {
668
- console.error(`Error: can't recognize ${url} extension: ${extension}`);
669
- }
670
- }
671
- };
672
- _ModelLoader.instance = null;
673
- var ModelLoader = _ModelLoader;
674
- var MODEL_LOADER = ModelLoader.getInstance();
675
- var ModelLoader_default = MODEL_LOADER;
676
-
677
630
  // src/character/CharacterModel.ts
678
631
  var CharacterModel = class {
679
- constructor(characterDescription) {
632
+ constructor(characterDescription, characterModelLoader) {
680
633
  this.characterDescription = characterDescription;
681
- /* TODO: pick between below eager instantiation or ModelLoader.getInstance() lazy one */
682
- this.modelLoader = ModelLoader_default;
634
+ this.characterModelLoader = characterModelLoader;
683
635
  this.mesh = null;
684
636
  this.material = new CharacterMaterial();
685
637
  this.headBone = null;
@@ -757,9 +709,9 @@ var CharacterModel = class {
757
709
  const scale = this.characterDescription.modelScale;
758
710
  const extension = mainMeshUrl.split(".").pop();
759
711
  const name = mainMeshUrl.split("/").pop().replace(`.${extension}`, "");
760
- const mainMesh = await this.modelLoader.load(mainMeshUrl, "model");
712
+ const mainMesh = await this.characterModelLoader.load(mainMeshUrl, "model");
761
713
  if (typeof mainMesh !== "undefined") {
762
- this.mesh = new Object3D2();
714
+ this.mesh = new Object3D();
763
715
  const model = mainMesh;
764
716
  model.position.set(0, -0.4, 0);
765
717
  this.mesh.add(model);
@@ -771,8 +723,8 @@ var CharacterModel = class {
771
723
  async setAnimationFromFile(animationFileUrl, animationType) {
772
724
  return new Promise(async (resolve, reject) => {
773
725
  this.initAnimationMixer();
774
- const animation = await this.modelLoader.load(animationFileUrl, "animation");
775
- if (typeof animation !== "undefined" && animation instanceof AnimationClip2) {
726
+ const animation = await this.characterModelLoader.load(animationFileUrl, "animation");
727
+ if (typeof animation !== "undefined" && animation instanceof AnimationClip) {
776
728
  this.animations[animationType] = this.animationMixer.clipAction(animation);
777
729
  this.animations[animationType].stop();
778
730
  if (animationType === 0 /* idle */) {
@@ -1304,7 +1256,6 @@ var LocalController = class {
1304
1256
  this.characterVelocity.y = deltaTime * this.gravity;
1305
1257
  }
1306
1258
  } else if (this.jump && this.coyoteTime) {
1307
- console.log("coyoteJump");
1308
1259
  this.characterVelocity.y = this.jumpForce;
1309
1260
  this.canJump = false;
1310
1261
  } else {
@@ -1394,8 +1345,9 @@ var LocalController = class {
1394
1345
 
1395
1346
  // src/character/Character.ts
1396
1347
  var Character = class {
1397
- constructor(characterDescription, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager, composer) {
1348
+ constructor(characterDescription, characterModelLoader, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager, composer) {
1398
1349
  this.characterDescription = characterDescription;
1350
+ this.characterModelLoader = characterModelLoader;
1399
1351
  this.id = id;
1400
1352
  this.isLocal = isLocal;
1401
1353
  this.modelLoadedCallback = modelLoadedCallback;
@@ -1414,7 +1366,7 @@ var Character = class {
1414
1366
  this.load();
1415
1367
  }
1416
1368
  async load() {
1417
- this.model = new CharacterModel(this.characterDescription);
1369
+ this.model = new CharacterModel(this.characterDescription, this.characterModelLoader);
1418
1370
  await this.model.init();
1419
1371
  if (this.tooltip === null) {
1420
1372
  this.tooltip = new CharacterTooltip(this.model.mesh);
@@ -1461,22 +1413,22 @@ var Character = class {
1461
1413
  };
1462
1414
 
1463
1415
  // src/character/CharacterManager.ts
1464
- import { Group, Vector3 as Vector37 } from "three";
1416
+ import { Euler, Group, Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1465
1417
 
1466
1418
  // src/character/RemoteController.ts
1467
1419
  import {
1468
1420
  AnimationMixer as AnimationMixer2,
1469
- Object3D as Object3D5,
1421
+ Object3D as Object3D4,
1470
1422
  Quaternion as Quaternion3,
1471
1423
  Vector3 as Vector36
1472
1424
  } from "three";
1473
1425
  var RemoteController = class {
1474
- constructor(character, id) {
1426
+ constructor(character, characterModelLoader, id) {
1475
1427
  this.character = character;
1428
+ this.characterModelLoader = characterModelLoader;
1476
1429
  this.id = id;
1477
- this.modelLoader = ModelLoader_default;
1478
1430
  this.characterModel = null;
1479
- this.animationMixer = new AnimationMixer2(new Object3D5());
1431
+ this.animationMixer = new AnimationMixer2(new Object3D4());
1480
1432
  this.animations = /* @__PURE__ */ new Map();
1481
1433
  this.currentAnimation = 0 /* idle */;
1482
1434
  this.characterModel = this.character.model.mesh;
@@ -1497,7 +1449,7 @@ var RemoteController = class {
1497
1449
  this.animationMixer.update(deltaTime);
1498
1450
  }
1499
1451
  async setAnimationFromFile(animationType, fileName) {
1500
- const animation = await this.modelLoader.load(fileName, "animation");
1452
+ const animation = await this.characterModelLoader.load(fileName, "animation");
1501
1453
  const animationAction = this.animationMixer.clipAction(animation);
1502
1454
  this.animations.set(animationType, animationAction);
1503
1455
  if (animationType === 0 /* idle */) {
@@ -1550,8 +1502,9 @@ function decodeCharacterAndCamera(hash, character, camera) {
1550
1502
  camera.quaternion.fromArray(values.slice(10, 14));
1551
1503
  }
1552
1504
  var CharacterManager = class {
1553
- constructor(composer, collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1505
+ constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1554
1506
  this.composer = composer;
1507
+ this.characterModelLoader = characterModelLoader;
1555
1508
  this.collisionsManager = collisionsManager;
1556
1509
  this.cameraManager = cameraManager;
1557
1510
  this.timeManager = timeManager;
@@ -1581,11 +1534,12 @@ var CharacterManager = class {
1581
1534
  the mesh loading async (would allow us to show a nameplate where a remote
1582
1535
  user is before the asset loads).
1583
1536
  */
1584
- spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector37()) {
1537
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector37(), spawnRotation = new Euler()) {
1585
1538
  this.characterDescription = characterDescription;
1586
1539
  const characterLoadingPromise = new Promise((resolve) => {
1587
1540
  const character = new Character(
1588
1541
  characterDescription,
1542
+ this.characterModelLoader,
1589
1543
  id,
1590
1544
  isLocal,
1591
1545
  () => {
@@ -1597,9 +1551,12 @@ var CharacterManager = class {
1597
1551
  this.cameraManager.camera
1598
1552
  );
1599
1553
  } else {
1600
- spawnPosition = getSpawnPositionInsideCircle(3, 30, id, 0.4);
1554
+ spawnPosition = spawnPosition || getSpawnPositionInsideCircle(3, 30, id, 0.4);
1601
1555
  character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1556
+ character.model.mesh.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1557
+ character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1602
1558
  character.model.mesh.updateMatrixWorld();
1559
+ const quaternion = new Quaternion4().setFromEuler(character.model.mesh.rotation);
1603
1560
  this.sendUpdate({
1604
1561
  id,
1605
1562
  position: {
@@ -1607,7 +1564,7 @@ var CharacterManager = class {
1607
1564
  y: spawnPosition.y,
1608
1565
  z: spawnPosition.z
1609
1566
  },
1610
- rotation: { quaternionY: 0, quaternionW: 1 },
1567
+ rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1611
1568
  state: 0 /* idle */
1612
1569
  });
1613
1570
  }
@@ -1624,7 +1581,7 @@ var CharacterManager = class {
1624
1581
  (_e = this.character.tooltip) == null ? void 0 : _e.setText(`${id}`);
1625
1582
  } else {
1626
1583
  this.remoteCharacters.set(id, character);
1627
- const remoteController = new RemoteController(character, id);
1584
+ const remoteController = new RemoteController(character, this.characterModelLoader, id);
1628
1585
  remoteController.setAnimationFromFile(
1629
1586
  0 /* idle */,
1630
1587
  characterDescription.idleAnimationFileUrl
@@ -1747,19 +1704,143 @@ var CharacterManager = class {
1747
1704
  }
1748
1705
  };
1749
1706
 
1707
+ // src/character/CharacterModelLoader.ts
1708
+ import { LoadingManager } from "three";
1709
+ import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
1710
+ var CachedGLTFLoader = class extends ThreeGLTFLoader {
1711
+ constructor(manager) {
1712
+ super(manager);
1713
+ this.blobCache = /* @__PURE__ */ new Map();
1714
+ }
1715
+ setBlobUrl(originalUrl, blobUrl) {
1716
+ this.blobCache.set(originalUrl, blobUrl);
1717
+ }
1718
+ getBlobUrl(originalUrl) {
1719
+ return this.blobCache.get(originalUrl);
1720
+ }
1721
+ load(url, onLoad, onProgress, onError) {
1722
+ const blobUrl = this.getBlobUrl(url);
1723
+ if (blobUrl) {
1724
+ console.log(`Loading cached ${url.split("/").pop()}`);
1725
+ super.load(blobUrl, onLoad, onProgress, onError);
1726
+ } else {
1727
+ super.load(url, onLoad, onProgress, onError);
1728
+ }
1729
+ }
1730
+ };
1731
+ var LRUCache = class {
1732
+ constructor(maxSize = 100) {
1733
+ this.maxSize = maxSize;
1734
+ this.cache = /* @__PURE__ */ new Map();
1735
+ }
1736
+ get(key) {
1737
+ const item = this.cache.get(key);
1738
+ if (item) {
1739
+ this.cache.delete(key);
1740
+ this.cache.set(key, item);
1741
+ }
1742
+ return item;
1743
+ }
1744
+ set(key, value) {
1745
+ if (this.cache.size >= this.maxSize) {
1746
+ const oldestKey = this.cache.keys().next().value;
1747
+ this.cache.delete(oldestKey);
1748
+ }
1749
+ this.cache.set(key, value);
1750
+ }
1751
+ };
1752
+ var _CharacterModelLoader = class _CharacterModelLoader {
1753
+ constructor(maxCacheSize = 100) {
1754
+ this.ongoingLoads = /* @__PURE__ */ new Map();
1755
+ this.loadingManager = new LoadingManager();
1756
+ this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
1757
+ this.modelCache = new LRUCache(maxCacheSize);
1758
+ }
1759
+ /* TODO: decide between below lazy initialization or eager on this file's bottom export */
1760
+ static getInstance() {
1761
+ if (!_CharacterModelLoader.instance) {
1762
+ _CharacterModelLoader.instance = new _CharacterModelLoader();
1763
+ }
1764
+ return _CharacterModelLoader.instance;
1765
+ }
1766
+ async load(fileUrl, fileType) {
1767
+ const cachedModel = this.modelCache.get(fileUrl);
1768
+ if (cachedModel) {
1769
+ const blobURL = URL.createObjectURL(cachedModel.blob);
1770
+ this.gltfLoader.setBlobUrl(fileUrl, blobURL);
1771
+ return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
1772
+ } else {
1773
+ console.log(`Loading ${fileUrl} from server`);
1774
+ const ongoingLoad = this.ongoingLoads.get(fileUrl);
1775
+ if (ongoingLoad)
1776
+ return ongoingLoad;
1777
+ const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
1778
+ const originalExtension = fileUrl.split(".").pop() || "";
1779
+ this.modelCache.set(fileUrl, { blob, originalExtension });
1780
+ const blobURL = URL.createObjectURL(blob);
1781
+ this.ongoingLoads.delete(fileUrl);
1782
+ return this.loadFromUrl(blobURL, fileType, originalExtension);
1783
+ });
1784
+ this.ongoingLoads.set(fileUrl, loadPromise);
1785
+ return loadPromise;
1786
+ }
1787
+ }
1788
+ async loadFromUrl(url, fileType, extension) {
1789
+ if (["gltf", "glb"].includes(extension)) {
1790
+ return new Promise((resolve, reject) => {
1791
+ this.gltfLoader.load(
1792
+ url,
1793
+ (object) => {
1794
+ if (fileType === "model") {
1795
+ resolve(object.scene);
1796
+ } else if (fileType === "animation") {
1797
+ resolve(object.animations[0]);
1798
+ } else {
1799
+ const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
1800
+ console.error(error);
1801
+ reject(error);
1802
+ }
1803
+ },
1804
+ void 0,
1805
+ (error) => {
1806
+ console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
1807
+ reject(error);
1808
+ }
1809
+ );
1810
+ });
1811
+ } else {
1812
+ console.error(`Error: can't recognize ${url} extension: ${extension}`);
1813
+ }
1814
+ }
1815
+ };
1816
+ _CharacterModelLoader.instance = null;
1817
+ var CharacterModelLoader = _CharacterModelLoader;
1818
+ var MODEL_LOADER = CharacterModelLoader.getInstance();
1819
+
1750
1820
  // src/input/KeyInputManager.ts
1751
1821
  var KeyInputManager = class {
1752
- constructor() {
1822
+ constructor(shouldCaptureKeyPress = () => true) {
1823
+ this.shouldCaptureKeyPress = shouldCaptureKeyPress;
1753
1824
  this.keys = /* @__PURE__ */ new Map();
1754
- document.addEventListener("keydown", this.onKeyDown.bind(this));
1755
- document.addEventListener("keyup", this.onKeyUp.bind(this));
1756
- window.addEventListener("blur", this.handleUnfocus.bind(this));
1825
+ this.eventHandlerCollection = new EventHandlerCollection();
1826
+ this.eventHandlerCollection.add(document, "keydown", this.onKeyDown.bind(this));
1827
+ this.eventHandlerCollection.add(document, "keyup", this.onKeyUp.bind(this));
1828
+ this.eventHandlerCollection.add(window, "blur", this.handleUnfocus.bind(this));
1757
1829
  }
1758
1830
  handleUnfocus(_event) {
1759
1831
  this.keys.clear();
1760
1832
  }
1761
1833
  onKeyDown(event) {
1762
- this.keys.set(event.key.toLowerCase(), true);
1834
+ if (this.shouldCaptureKeyPress()) {
1835
+ if (event.key.length === 2 && event.key[0] === "F") {
1836
+ return;
1837
+ }
1838
+ if (event.metaKey) {
1839
+ return;
1840
+ }
1841
+ this.keys.set(event.key.toLowerCase(), true);
1842
+ event.preventDefault();
1843
+ }
1763
1844
  }
1764
1845
  onKeyUp(event) {
1765
1846
  this.keys.set(event.key.toLowerCase(), false);
@@ -1768,36 +1849,34 @@ var KeyInputManager = class {
1768
1849
  return this.keys.get(key) || false;
1769
1850
  }
1770
1851
  isMovementKeyPressed() {
1771
- return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
1852
+ return ["w" /* W */, "a" /* A */, "s" /* S */, "d" /* D */].some((key) => this.isKeyPressed(key));
1772
1853
  }
1773
1854
  get forward() {
1774
- return this.isKeyPressed("w");
1855
+ return this.isKeyPressed("w" /* W */);
1775
1856
  }
1776
1857
  get backward() {
1777
- return this.isKeyPressed("s");
1858
+ return this.isKeyPressed("s" /* S */);
1778
1859
  }
1779
1860
  get left() {
1780
- return this.isKeyPressed("a");
1861
+ return this.isKeyPressed("a" /* A */);
1781
1862
  }
1782
1863
  get right() {
1783
- return this.isKeyPressed("d");
1864
+ return this.isKeyPressed("d" /* D */);
1784
1865
  }
1785
1866
  get run() {
1786
- return this.isKeyPressed("shift");
1867
+ return this.isKeyPressed("shift" /* SHIFT */);
1787
1868
  }
1788
1869
  get jump() {
1789
- return this.isKeyPressed(" ");
1870
+ return this.isKeyPressed(" " /* SPACE */);
1790
1871
  }
1791
1872
  get anyDirection() {
1792
1873
  return this.isMovementKeyPressed();
1793
1874
  }
1794
1875
  get conflictingDirection() {
1795
- return this.isKeyPressed("w") && this.isKeyPressed("s") || this.isKeyPressed("a") && this.isKeyPressed("d");
1876
+ return this.isKeyPressed("w" /* W */) && this.isKeyPressed("s" /* S */) || this.isKeyPressed("a" /* A */) && this.isKeyPressed("d" /* D */);
1796
1877
  }
1797
1878
  dispose() {
1798
- document.removeEventListener("keydown", this.onKeyDown.bind(this));
1799
- document.removeEventListener("keyup", this.onKeyDown.bind(this));
1800
- window.removeEventListener("blur", this.handleUnfocus.bind(this));
1879
+ this.eventHandlerCollection.clear();
1801
1880
  }
1802
1881
  };
1803
1882
 
@@ -1805,23 +1884,26 @@ var KeyInputManager = class {
1805
1884
  import {
1806
1885
  InteractionManager,
1807
1886
  MMLClickTrigger,
1808
- PromptManager,
1809
- registerCustomElementsToWindow,
1810
- setGlobalMScene
1887
+ PromptManager
1811
1888
  } from "mml-web";
1812
1889
  import { Group as Group2 } from "three";
1813
1890
  var MMLCompositionScene = class {
1814
- constructor(renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation, documentAddresses) {
1891
+ constructor(targetElement, renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation) {
1815
1892
  this.renderer = renderer;
1816
1893
  this.scene = scene;
1817
1894
  this.camera = camera;
1818
1895
  this.audioListener = audioListener;
1819
1896
  this.collisionsManager = collisionsManager;
1820
1897
  this.getUserPositionAndRotation = getUserPositionAndRotation;
1821
- this.debug = false;
1898
+ this.chatProbes = /* @__PURE__ */ new Set();
1822
1899
  this.group = new Group2();
1823
- this.promptManager = PromptManager.init(document.body);
1824
- const { interactionListener } = InteractionManager.init(document.body, this.camera, this.scene);
1900
+ this.promptManager = PromptManager.init(targetElement);
1901
+ const { interactionListener, interactionManager } = InteractionManager.init(
1902
+ targetElement,
1903
+ this.camera,
1904
+ this.scene
1905
+ );
1906
+ this.interactionManager = interactionManager;
1825
1907
  this.interactionListener = interactionListener;
1826
1908
  this.mmlScene = {
1827
1909
  getAudioListener: () => this.audioListener,
@@ -1848,59 +1930,82 @@ var MMLCompositionScene = class {
1848
1930
  removeInteraction: (interaction) => {
1849
1931
  this.interactionListener.removeInteraction(interaction);
1850
1932
  },
1933
+ addChatProbe: (chatProbe) => {
1934
+ this.chatProbes.add(chatProbe);
1935
+ },
1936
+ updateChatProbe: () => {
1937
+ },
1938
+ removeChatProbe: (chatProbe) => {
1939
+ this.chatProbes.delete(chatProbe);
1940
+ },
1851
1941
  prompt: (promptProps, callback) => {
1852
1942
  this.promptManager.prompt(promptProps, callback);
1853
1943
  }
1854
1944
  };
1855
- setGlobalMScene(this.mmlScene);
1856
- registerCustomElementsToWindow(window);
1857
- this.clickTrigger = MMLClickTrigger.init(document, this.mmlScene);
1858
- if (this.debug) {
1859
- console.log(this.clickTrigger);
1860
- }
1861
- for (const address of documentAddresses) {
1862
- const frameElement = document.createElement("m-frame");
1863
- frameElement.setAttribute("src", address);
1864
- document.body.appendChild(frameElement);
1945
+ this.clickTrigger = MMLClickTrigger.init(targetElement, this.mmlScene);
1946
+ }
1947
+ onChatMessage(message) {
1948
+ for (const chatProbe of this.chatProbes) {
1949
+ chatProbe.trigger(message);
1865
1950
  }
1866
1951
  }
1952
+ dispose() {
1953
+ this.promptManager.dispose();
1954
+ this.clickTrigger.dispose();
1955
+ this.interactionManager.dispose();
1956
+ }
1867
1957
  };
1868
1958
 
1869
- // src/rendering/composer.ts
1870
- import { N8AOPostPass } from "n8ao";
1871
- import {
1872
- EffectComposer as EffectComposer2,
1873
- RenderPass,
1874
- EffectPass as EffectPass2,
1875
- FXAAEffect,
1876
- ShaderPass,
1877
- BloomEffect,
1878
- SSAOEffect as SSAOEffect2,
1879
- BlendFunction as BlendFunction2,
1880
- TextureEffect,
1881
- ToneMappingEffect,
1882
- SMAAEffect,
1883
- SMAAPreset,
1884
- EdgeDetectionMode,
1885
- PredicationMode,
1886
- NormalPass as NormalPass2
1887
- } from "postprocessing";
1888
- import {
1889
- AmbientLight,
1890
- Color as Color6,
1891
- Fog,
1892
- HalfFloatType,
1893
- LinearSRGBColorSpace,
1894
- LoadingManager as LoadingManager2,
1895
- PMREMGenerator,
1896
- Scene as Scene3,
1897
- Vector2 as Vector22,
1898
- WebGLRenderer as WebGLRenderer2
1899
- } from "three";
1900
- import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
1959
+ // src/tweakpane/TweakPane.ts
1960
+ import * as EssentialsPlugin from "@tweakpane/plugin-essentials";
1961
+ import { Pane } from "tweakpane";
1901
1962
 
1902
- // src/sun/Sun.ts
1903
- import { CameraHelper, Color as Color4, DirectionalLight, Group as Group3, OrthographicCamera, Vector3 as Vector38 } from "three";
1963
+ // src/tweakpane/blades/bcsFolder.ts
1964
+ var bcsValues = {
1965
+ brightness: 0,
1966
+ contrast: 1.25,
1967
+ saturation: 1
1968
+ };
1969
+ var bcsOptions = {
1970
+ brightness: {
1971
+ amount: { min: -1, max: 1, step: 0.01 }
1972
+ },
1973
+ contrast: {
1974
+ amount: { min: 0, max: 2, step: 0.01 }
1975
+ },
1976
+ saturation: {
1977
+ amount: { min: 0, max: 2, step: 0.01 }
1978
+ }
1979
+ };
1980
+ var BrightnessContrastSaturationFolder = class {
1981
+ constructor(parentFolder, expand = false) {
1982
+ this.folder = parentFolder.addFolder({
1983
+ title: "brightness / contrast / sat",
1984
+ expanded: expand
1985
+ });
1986
+ this.folder.addBinding(bcsValues, "brightness", bcsOptions.brightness.amount);
1987
+ this.folder.addBinding(bcsValues, "contrast", bcsOptions.contrast.amount);
1988
+ this.folder.addBinding(bcsValues, "saturation", bcsOptions.saturation.amount);
1989
+ }
1990
+ setupChangeEvent(brightnessContrastSaturation) {
1991
+ this.folder.on("change", (e) => {
1992
+ const target = e.target.key;
1993
+ if (!target)
1994
+ return;
1995
+ switch (target) {
1996
+ case "brightness":
1997
+ brightnessContrastSaturation.uniforms.brightness.value = e.value;
1998
+ break;
1999
+ case "contrast":
2000
+ brightnessContrastSaturation.uniforms.contrast.value = e.value;
2001
+ break;
2002
+ case "saturation":
2003
+ brightnessContrastSaturation.uniforms.saturation.value = e.value;
2004
+ break;
2005
+ }
2006
+ });
2007
+ }
2008
+ };
1904
2009
 
1905
2010
  // src/tweakpane/blades/environmentFolder.ts
1906
2011
  var sunValues = {
@@ -2057,135 +2162,6 @@ var EnvironmentFolder = class {
2057
2162
  }
2058
2163
  };
2059
2164
 
2060
- // src/sun/Sun.ts
2061
- var Sun = class extends Group3 {
2062
- constructor() {
2063
- super();
2064
- this.debug = false;
2065
- this.sunOffset = new Vector38(
2066
- 39 * (Math.PI / 180),
2067
- 50 * (Math.PI / 180),
2068
- 100
2069
- );
2070
- this.shadowResolution = 8192;
2071
- this.shadowCamFrustum = 50;
2072
- this.camHelper = null;
2073
- this.target = null;
2074
- this.shadowCamera = new OrthographicCamera(
2075
- -this.shadowCamFrustum,
2076
- this.shadowCamFrustum,
2077
- this.shadowCamFrustum,
2078
- -this.shadowCamFrustum,
2079
- 0.1,
2080
- 200
2081
- );
2082
- if (this.debug === true) {
2083
- this.camHelper = new CameraHelper(this.shadowCamera);
2084
- }
2085
- this.directionalLight = new DirectionalLight(16777215, 0.5);
2086
- this.directionalLight.shadow.normalBias = 0.05;
2087
- this.directionalLight.shadow.radius = 1.5;
2088
- this.directionalLight.shadow.camera = this.shadowCamera;
2089
- this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
2090
- this.directionalLight.castShadow = true;
2091
- this.setColor();
2092
- this.updateCharacterPosition(new Vector38(0, 0, 0));
2093
- this.add(this.directionalLight);
2094
- if (this.debug === true && this.camHelper instanceof CameraHelper) {
2095
- this.add(this.camHelper);
2096
- }
2097
- }
2098
- updateCharacterPosition(position) {
2099
- if (!position)
2100
- return;
2101
- this.target = position;
2102
- this.setSunPosition(this.sunOffset.x, this.sunOffset.y);
2103
- }
2104
- setAzimuthalAngle(angle) {
2105
- if (this.sunOffset)
2106
- this.sunOffset.x = angle;
2107
- if (this.target)
2108
- this.updateCharacterPosition(this.target);
2109
- }
2110
- setPolarAngle(angle) {
2111
- if (this.sunOffset)
2112
- this.sunOffset.y = angle;
2113
- if (this.target)
2114
- this.updateCharacterPosition(this.target);
2115
- }
2116
- setIntensity(intensity) {
2117
- this.directionalLight.intensity = intensity;
2118
- }
2119
- setColor() {
2120
- this.directionalLight.color = new Color4().setRGB(
2121
- sunValues.sunColor.r,
2122
- sunValues.sunColor.g,
2123
- sunValues.sunColor.b
2124
- );
2125
- }
2126
- setSunPosition(azimuthalAngle, polarAngle) {
2127
- if (!this.target)
2128
- return;
2129
- const distance = this.sunOffset.z;
2130
- const sphericalPosition = new Vector38(
2131
- distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
2132
- distance * Math.cos(polarAngle),
2133
- distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
2134
- );
2135
- const newSunPosition = this.target.clone().add(sphericalPosition);
2136
- this.directionalLight.position.set(newSunPosition.x, newSunPosition.y, newSunPosition.z);
2137
- this.directionalLight.target.position.copy(this.target.clone());
2138
- this.directionalLight.target.updateMatrixWorld();
2139
- }
2140
- };
2141
-
2142
- // src/tweakpane/blades/bcsFolder.ts
2143
- var bcsValues = {
2144
- brightness: 0,
2145
- contrast: 1.25,
2146
- saturation: 1
2147
- };
2148
- var bcsOptions = {
2149
- brightness: {
2150
- amount: { min: -1, max: 1, step: 0.01 }
2151
- },
2152
- contrast: {
2153
- amount: { min: 0, max: 2, step: 0.01 }
2154
- },
2155
- saturation: {
2156
- amount: { min: 0, max: 2, step: 0.01 }
2157
- }
2158
- };
2159
- var BrightnessContrastSaturationFolder = class {
2160
- constructor(parentFolder, expand = false) {
2161
- this.folder = parentFolder.addFolder({
2162
- title: "brightness / contrast / sat",
2163
- expanded: expand
2164
- });
2165
- this.folder.addBinding(bcsValues, "brightness", bcsOptions.brightness.amount);
2166
- this.folder.addBinding(bcsValues, "contrast", bcsOptions.contrast.amount);
2167
- this.folder.addBinding(bcsValues, "saturation", bcsOptions.saturation.amount);
2168
- }
2169
- setupChangeEvent(brightnessContrastSaturation) {
2170
- this.folder.on("change", (e) => {
2171
- const target = e.target.key;
2172
- if (!target)
2173
- return;
2174
- switch (target) {
2175
- case "brightness":
2176
- brightnessContrastSaturation.uniforms.brightness.value = e.value;
2177
- break;
2178
- case "contrast":
2179
- brightnessContrastSaturation.uniforms.contrast.value = e.value;
2180
- break;
2181
- case "saturation":
2182
- brightnessContrastSaturation.uniforms.saturation.value = e.value;
2183
- break;
2184
- }
2185
- });
2186
- }
2187
- };
2188
-
2189
2165
  // src/tweakpane/blades/postExtrasFolder.ts
2190
2166
  var extrasValues = {
2191
2167
  grain: 0.055,
@@ -2310,9 +2286,52 @@ var RendererFolder = class {
2310
2286
  }
2311
2287
  };
2312
2288
 
2289
+ // src/tweakpane/blades/rendererStatsFolder.ts
2290
+ var RendererStatsFolder = class {
2291
+ constructor(parentFolder, expanded = true) {
2292
+ this.statsData = {
2293
+ triangles: "0",
2294
+ geometries: "0",
2295
+ textures: "0",
2296
+ shaders: "0",
2297
+ postPasses: "0",
2298
+ drawCalls: "0",
2299
+ rawDeltaTime: "0",
2300
+ deltaTime: "0",
2301
+ FPS: "0"
2302
+ };
2303
+ this.folder = parentFolder.addFolder({ title: "renderStats", expanded });
2304
+ this.performance = this.folder.addFolder({ title: "performance", expanded: true });
2305
+ this.defails = this.folder.addFolder({ title: "pipeline details", expanded: false });
2306
+ this.folder.addBlade({ view: "separator" });
2307
+ this.performance.addBinding(this.statsData, "FPS", { readonly: true });
2308
+ this.performance.addBinding(this.statsData, "deltaTime", { readonly: true });
2309
+ this.performance.addBinding(this.statsData, "rawDeltaTime", { readonly: true });
2310
+ this.defails.addBinding(this.statsData, "triangles", { readonly: true });
2311
+ this.defails.addBinding(this.statsData, "geometries", { readonly: true });
2312
+ this.defails.addBinding(this.statsData, "textures", { readonly: true });
2313
+ this.defails.addBinding(this.statsData, "shaders", { readonly: true });
2314
+ this.defails.addBinding(this.statsData, "postPasses", { readonly: true });
2315
+ this.defails.addBinding(this.statsData, "drawCalls", { readonly: true });
2316
+ }
2317
+ update(renderer, composer, timeManager) {
2318
+ const { geometries, textures } = renderer.info.memory;
2319
+ const { triangles, calls } = renderer.info.render;
2320
+ this.statsData.triangles = triangles.toString();
2321
+ this.statsData.geometries = geometries.toString();
2322
+ this.statsData.textures = textures.toString();
2323
+ this.statsData.shaders = renderer.info.programs.length.toString();
2324
+ this.statsData.postPasses = composer.passes.length.toString();
2325
+ this.statsData.drawCalls = calls.toString();
2326
+ this.statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2327
+ this.statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2328
+ this.statsData.FPS = timeManager.fps.toString();
2329
+ }
2330
+ };
2331
+
2313
2332
  // src/tweakpane/blades/ssaoFolder.ts
2314
2333
  import { BlendFunction } from "postprocessing";
2315
- import { Color as Color5 } from "three";
2334
+ import { Color as Color4 } from "three";
2316
2335
  var ppssaoValues = {
2317
2336
  enabled: false,
2318
2337
  blendFunction: BlendFunction.MULTIPLY,
@@ -2504,7 +2523,7 @@ var SSAOFolder = class {
2504
2523
  }
2505
2524
  case "color": {
2506
2525
  const value = e.value;
2507
- ppssaoEffect.color = new Color5().setRGB(value.r, value.g, value.b);
2526
+ ppssaoEffect.color = new Color4().setRGB(value.r, value.g, value.b);
2508
2527
  break;
2509
2528
  }
2510
2529
  default: {
@@ -2544,7 +2563,7 @@ var SSAOFolder = class {
2544
2563
  break;
2545
2564
  case "color":
2546
2565
  const value = e.value;
2547
- n8aopass.configuration.color = new Color5().setRGB(value.r, value.g, value.b);
2566
+ n8aopass.configuration.color = new Color4().setRGB(value.r, value.g, value.b);
2548
2567
  break;
2549
2568
  default:
2550
2569
  break;
@@ -2637,53 +2656,6 @@ var ToneMappingFolder = class {
2637
2656
  }
2638
2657
  };
2639
2658
 
2640
- // src/tweakpane/TweakPane.ts
2641
- import * as EssentialsPlugin from "@tweakpane/plugin-essentials";
2642
- import { Pane } from "tweakpane";
2643
-
2644
- // src/tweakpane/blades/rendererStatsFolder.ts
2645
- var RendererStatsFolder = class {
2646
- constructor(parentFolder, expanded = true) {
2647
- this.statsData = {
2648
- triangles: "0",
2649
- geometries: "0",
2650
- textures: "0",
2651
- shaders: "0",
2652
- postPasses: "0",
2653
- drawCalls: "0",
2654
- rawDeltaTime: "0",
2655
- deltaTime: "0",
2656
- FPS: "0"
2657
- };
2658
- this.folder = parentFolder.addFolder({ title: "renderStats", expanded });
2659
- this.performance = this.folder.addFolder({ title: "performance", expanded: true });
2660
- this.defails = this.folder.addFolder({ title: "pipeline details", expanded: false });
2661
- this.folder.addBlade({ view: "separator" });
2662
- this.performance.addBinding(this.statsData, "FPS", { readonly: true });
2663
- this.performance.addBinding(this.statsData, "deltaTime", { readonly: true });
2664
- this.performance.addBinding(this.statsData, "rawDeltaTime", { readonly: true });
2665
- this.defails.addBinding(this.statsData, "triangles", { readonly: true });
2666
- this.defails.addBinding(this.statsData, "geometries", { readonly: true });
2667
- this.defails.addBinding(this.statsData, "textures", { readonly: true });
2668
- this.defails.addBinding(this.statsData, "shaders", { readonly: true });
2669
- this.defails.addBinding(this.statsData, "postPasses", { readonly: true });
2670
- this.defails.addBinding(this.statsData, "drawCalls", { readonly: true });
2671
- }
2672
- update(renderer, composer, timeManager) {
2673
- const { geometries, textures } = renderer.info.memory;
2674
- const { triangles, calls } = renderer.info.render;
2675
- this.statsData.triangles = triangles.toString();
2676
- this.statsData.geometries = geometries.toString();
2677
- this.statsData.textures = textures.toString();
2678
- this.statsData.shaders = renderer.info.programs.length.toString();
2679
- this.statsData.postPasses = composer.passes.length.toString();
2680
- this.statsData.drawCalls = calls.toString();
2681
- this.statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2682
- this.statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2683
- this.statsData.FPS = timeManager.fps.toString();
2684
- }
2685
- };
2686
-
2687
2659
  // src/tweakpane/tweakPaneStyle.ts
2688
2660
  var tweakPaneStyle = `
2689
2661
  :root {
@@ -2882,6 +2854,122 @@ var TweakPane = class {
2882
2854
  }
2883
2855
  };
2884
2856
 
2857
+ // src/rendering/composer.ts
2858
+ import { N8AOPostPass } from "n8ao";
2859
+ import {
2860
+ EffectComposer as EffectComposer2,
2861
+ RenderPass,
2862
+ EffectPass as EffectPass2,
2863
+ FXAAEffect,
2864
+ ShaderPass,
2865
+ BloomEffect,
2866
+ SSAOEffect as SSAOEffect2,
2867
+ BlendFunction as BlendFunction2,
2868
+ TextureEffect,
2869
+ ToneMappingEffect,
2870
+ SMAAEffect,
2871
+ SMAAPreset,
2872
+ EdgeDetectionMode,
2873
+ PredicationMode,
2874
+ NormalPass as NormalPass2
2875
+ } from "postprocessing";
2876
+ import {
2877
+ AmbientLight,
2878
+ Color as Color6,
2879
+ Fog,
2880
+ HalfFloatType,
2881
+ LinearSRGBColorSpace,
2882
+ LoadingManager as LoadingManager2,
2883
+ PMREMGenerator,
2884
+ Scene as Scene3,
2885
+ Vector2 as Vector22,
2886
+ WebGLRenderer as WebGLRenderer2
2887
+ } from "three";
2888
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
2889
+
2890
+ // src/sun/Sun.ts
2891
+ import { CameraHelper, Color as Color5, DirectionalLight, Group as Group3, OrthographicCamera, Vector3 as Vector38 } from "three";
2892
+ var Sun = class extends Group3 {
2893
+ constructor() {
2894
+ super();
2895
+ this.debug = false;
2896
+ this.sunOffset = new Vector38(
2897
+ 39 * (Math.PI / 180),
2898
+ 50 * (Math.PI / 180),
2899
+ 100
2900
+ );
2901
+ this.shadowResolution = 8192;
2902
+ this.shadowCamFrustum = 50;
2903
+ this.camHelper = null;
2904
+ this.target = null;
2905
+ this.shadowCamera = new OrthographicCamera(
2906
+ -this.shadowCamFrustum,
2907
+ this.shadowCamFrustum,
2908
+ this.shadowCamFrustum,
2909
+ -this.shadowCamFrustum,
2910
+ 0.1,
2911
+ 200
2912
+ );
2913
+ if (this.debug === true) {
2914
+ this.camHelper = new CameraHelper(this.shadowCamera);
2915
+ }
2916
+ this.directionalLight = new DirectionalLight(16777215, 0.5);
2917
+ this.directionalLight.shadow.normalBias = 0.05;
2918
+ this.directionalLight.shadow.radius = 1.5;
2919
+ this.directionalLight.shadow.camera = this.shadowCamera;
2920
+ this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
2921
+ this.directionalLight.castShadow = true;
2922
+ this.setColor();
2923
+ this.updateCharacterPosition(new Vector38(0, 0, 0));
2924
+ this.add(this.directionalLight);
2925
+ if (this.debug === true && this.camHelper instanceof CameraHelper) {
2926
+ this.add(this.camHelper);
2927
+ }
2928
+ }
2929
+ updateCharacterPosition(position) {
2930
+ if (!position)
2931
+ return;
2932
+ this.target = position;
2933
+ this.setSunPosition(this.sunOffset.x, this.sunOffset.y);
2934
+ }
2935
+ setAzimuthalAngle(angle) {
2936
+ if (this.sunOffset)
2937
+ this.sunOffset.x = angle;
2938
+ if (this.target)
2939
+ this.updateCharacterPosition(this.target);
2940
+ }
2941
+ setPolarAngle(angle) {
2942
+ if (this.sunOffset)
2943
+ this.sunOffset.y = angle;
2944
+ if (this.target)
2945
+ this.updateCharacterPosition(this.target);
2946
+ }
2947
+ setIntensity(intensity) {
2948
+ this.directionalLight.intensity = intensity;
2949
+ }
2950
+ setColor() {
2951
+ this.directionalLight.color = new Color5().setRGB(
2952
+ sunValues.sunColor.r,
2953
+ sunValues.sunColor.g,
2954
+ sunValues.sunColor.b
2955
+ );
2956
+ }
2957
+ setSunPosition(azimuthalAngle, polarAngle) {
2958
+ if (!this.target)
2959
+ return;
2960
+ const distance = this.sunOffset.z;
2961
+ const sphericalPosition = new Vector38(
2962
+ distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
2963
+ distance * Math.cos(polarAngle),
2964
+ distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
2965
+ );
2966
+ const newSunPosition = this.target.clone().add(sphericalPosition);
2967
+ this.directionalLight.position.set(newSunPosition.x, newSunPosition.y, newSunPosition.z);
2968
+ this.directionalLight.target.position.copy(this.target.clone());
2969
+ this.directionalLight.target.updateMatrixWorld();
2970
+ }
2971
+ };
2972
+
2885
2973
  // src/rendering/post-effects/bright-contrast-sat.ts
2886
2974
  import { ShaderMaterial, Uniform } from "three";
2887
2975
 
@@ -3029,8 +3117,8 @@ var GaussGrainEffect = new ShaderMaterial2({
3029
3117
  // src/rendering/composer.ts
3030
3118
  var Composer = class {
3031
3119
  constructor(scene, camera, spawnSun = false) {
3032
- this.width = window.innerWidth;
3033
- this.height = window.innerHeight;
3120
+ this.width = 1;
3121
+ this.height = 1;
3034
3122
  this.resolution = new Vector22(this.width, this.height);
3035
3123
  this.isEnvHDRI = false;
3036
3124
  this.bcs = BrightnessContrastSaturation;
@@ -3055,11 +3143,9 @@ var Composer = class {
3055
3143
  this.renderer.toneMappingExposure = rendererValues.exposure;
3056
3144
  this.setAmbientLight();
3057
3145
  this.setFog();
3058
- document.body.appendChild(this.renderer.domElement);
3059
- this.composer = new EffectComposer2(this.renderer, {
3146
+ this.effectComposer = new EffectComposer2(this.renderer, {
3060
3147
  frameBufferType: HalfFloatType
3061
3148
  });
3062
- this.tweakPane = new TweakPane(this.renderer, this.scene, this.composer);
3063
3149
  this.renderPass = new RenderPass(this.scene, this.camera);
3064
3150
  this.normalPass = new NormalPass2(this.scene, this.camera);
3065
3151
  this.normalPass.enabled = ppssaoValues.enabled;
@@ -3128,26 +3214,33 @@ var Composer = class {
3128
3214
  this.bcs.uniforms.saturation.value = bcsValues.saturation;
3129
3215
  this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
3130
3216
  this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
3131
- this.composer.addPass(this.renderPass);
3217
+ this.effectComposer.addPass(this.renderPass);
3132
3218
  if (ppssaoValues.enabled) {
3133
- this.composer.addPass(this.normalPass);
3134
- this.composer.addPass(this.ppssaoPass);
3219
+ this.effectComposer.addPass(this.normalPass);
3220
+ this.effectComposer.addPass(this.ppssaoPass);
3135
3221
  }
3136
3222
  if (n8ssaoValues.enabled) {
3137
- this.composer.addPass(this.n8aopass);
3138
- }
3139
- this.composer.addPass(this.fxaaPass);
3140
- this.composer.addPass(this.smaaPass);
3141
- this.composer.addPass(this.bloomPass);
3142
- this.composer.addPass(this.toneMappingPass);
3143
- this.composer.addPass(this.bcsPass);
3144
- this.composer.addPass(this.gaussGrainPass);
3223
+ this.effectComposer.addPass(this.n8aopass);
3224
+ }
3225
+ this.effectComposer.addPass(this.fxaaPass);
3226
+ this.effectComposer.addPass(this.smaaPass);
3227
+ this.effectComposer.addPass(this.bloomPass);
3228
+ this.effectComposer.addPass(this.toneMappingPass);
3229
+ this.effectComposer.addPass(this.bcsPass);
3230
+ this.effectComposer.addPass(this.gaussGrainPass);
3145
3231
  if (this.spawnSun === true) {
3146
3232
  this.sun = new Sun();
3147
3233
  this.scene.add(this.sun);
3148
3234
  }
3149
- this.tweakPane.setupRenderPane(
3150
- this.composer,
3235
+ this.resizeListener = () => {
3236
+ this.fitContainer();
3237
+ };
3238
+ window.addEventListener("resize", this.resizeListener, false);
3239
+ this.fitContainer();
3240
+ }
3241
+ setupTweakPane(tweakPane) {
3242
+ tweakPane.setupRenderPane(
3243
+ this.effectComposer,
3151
3244
  this.normalPass,
3152
3245
  this.ppssaoEffect,
3153
3246
  this.ppssaoPass,
@@ -3163,14 +3256,32 @@ var Composer = class {
3163
3256
  this.setAmbientLight.bind(this),
3164
3257
  this.setFog.bind(this)
3165
3258
  );
3166
- window.addEventListener("resize", () => this.updateProjection());
3167
- this.updateProjection();
3168
3259
  }
3169
- updateProjection() {
3170
- this.width = window.innerWidth;
3171
- this.height = innerHeight;
3172
- this.resolution = new Vector22(this.width, this.height);
3173
- this.composer.setSize(this.width, this.height);
3260
+ dispose() {
3261
+ window.removeEventListener("resize", this.resizeListener);
3262
+ }
3263
+ fitContainer() {
3264
+ if (!this) {
3265
+ console.error("Composer not initialized");
3266
+ return;
3267
+ }
3268
+ const parentElement = this.renderer.domElement.parentNode;
3269
+ if (!parentElement) {
3270
+ return;
3271
+ }
3272
+ this.width = parentElement.clientWidth;
3273
+ this.height = parentElement.clientHeight;
3274
+ this.camera.aspect = this.width / this.height;
3275
+ this.camera.updateProjectionMatrix();
3276
+ this.renderer.setPixelRatio(window.devicePixelRatio);
3277
+ this.resolution.set(
3278
+ this.width * window.devicePixelRatio,
3279
+ this.height * window.devicePixelRatio
3280
+ );
3281
+ this.effectComposer.setSize(
3282
+ this.width / window.devicePixelRatio,
3283
+ this.height / window.devicePixelRatio
3284
+ );
3174
3285
  this.renderPass.setSize(this.width, this.height);
3175
3286
  if (ppssaoValues.enabled) {
3176
3287
  this.normalPass.setSize(this.width, this.height);
@@ -3187,20 +3298,14 @@ var Composer = class {
3187
3298
  this.gaussGrainPass.setSize(this.width, this.height);
3188
3299
  this.renderer.setSize(this.width, this.height);
3189
3300
  }
3190
- isTweakPaneVisible() {
3191
- return this.tweakPane.guiVisible;
3192
- }
3193
3301
  render(timeManager) {
3194
3302
  this.renderer.info.reset();
3195
3303
  this.normalPass.texture.needsUpdate = true;
3196
3304
  this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
3197
3305
  this.gaussGrainEffect.uniforms.time.value = timeManager.time;
3198
3306
  this.gaussGrainEffect.uniforms.alpha.value = 1;
3199
- this.composer.render();
3307
+ this.effectComposer.render();
3200
3308
  this.renderer.render(this.postPostScene, this.camera);
3201
- if (this.tweakPane.guiVisible) {
3202
- this.tweakPane.updateStats(timeManager);
3203
- }
3204
3309
  }
3205
3310
  useHDRI(url, fromFile = false) {
3206
3311
  if (this.isEnvHDRI && fromFile === false || !this.renderer)
@@ -3332,7 +3437,7 @@ import {
3332
3437
  Box3,
3333
3438
  Color as Color7,
3334
3439
  DoubleSide,
3335
- Euler,
3440
+ Euler as Euler2,
3336
3441
  Group as Group4,
3337
3442
  Line3 as Line32,
3338
3443
  Matrix4 as Matrix42,
@@ -3354,7 +3459,7 @@ var CollisionsManager = class {
3354
3459
  this.tempMatrix = new Matrix42();
3355
3460
  this.tempMatrix2 = new Matrix42();
3356
3461
  this.tempBox = new Box3();
3357
- this.tempEuler = new Euler();
3462
+ this.tempEuler = new Euler2();
3358
3463
  this.tempSegment = new Line32();
3359
3464
  this.tempSegment2 = new Line32();
3360
3465
  this.collisionMeshState = /* @__PURE__ */ new Map();
@@ -3547,11 +3652,13 @@ export {
3547
3652
  AnimationState,
3548
3653
  CameraManager,
3549
3654
  CharacterManager,
3655
+ CharacterModelLoader,
3550
3656
  CollisionsManager,
3551
3657
  Composer,
3552
3658
  KeyInputManager,
3553
3659
  MMLCompositionScene,
3554
3660
  Sun,
3555
- TimeManager
3661
+ TimeManager,
3662
+ TweakPane
3556
3663
  };
3557
3664
  //# sourceMappingURL=index.js.map