@mml-io/3d-web-client-core 0.7.0 → 0.9.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
@@ -2,7 +2,21 @@
2
2
  import { PerspectiveCamera, Raycaster, Vector3 as Vector32 } from "three";
3
3
 
4
4
  // src/helpers/math-helpers.ts
5
- import { Vector3 } from "three";
5
+ import { Quaternion, Vector3, Vector4 } from "three";
6
+ var roundToDecimalPlaces = (value, decimalPlaces) => {
7
+ const mult = 10 ** decimalPlaces;
8
+ return Math.round(value * mult) / mult;
9
+ };
10
+ var toArray = (origin, precision = 3) => {
11
+ const array = [];
12
+ array[0] = roundToDecimalPlaces(origin.x, precision);
13
+ array[1] = roundToDecimalPlaces(origin.y, precision);
14
+ array[2] = roundToDecimalPlaces(origin.z, precision);
15
+ if (origin instanceof Vector4 || origin instanceof Quaternion) {
16
+ array[3] = roundToDecimalPlaces(origin.w, precision);
17
+ }
18
+ return array;
19
+ };
6
20
  var getSpawnPositionInsideCircle = (radius, positions, id, yPos = 0) => {
7
21
  if (id > 0)
8
22
  id += 3;
@@ -27,6 +41,47 @@ var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
27
41
  return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
28
42
  };
29
43
 
44
+ // src/input/EventHandlerCollection.ts
45
+ var EventHandlerCollection = class _EventHandlerCollection {
46
+ constructor() {
47
+ this.eventsByTarget = /* @__PURE__ */ new Map();
48
+ }
49
+ add(target, key, listener, options) {
50
+ target.addEventListener(key, listener, options);
51
+ let existingTarget = this.eventsByTarget.get(target);
52
+ if (existingTarget === void 0) {
53
+ existingTarget = /* @__PURE__ */ new Map();
54
+ this.eventsByTarget.set(target, existingTarget);
55
+ }
56
+ let existingKey = existingTarget.get(key);
57
+ if (existingKey === void 0) {
58
+ existingKey = /* @__PURE__ */ new Set();
59
+ existingTarget.set(key, existingKey);
60
+ }
61
+ existingKey.add(listener);
62
+ return this;
63
+ }
64
+ clear() {
65
+ this.eventsByTarget.forEach((keyMap, target) => {
66
+ keyMap.forEach((listenerSet, key) => {
67
+ listenerSet.forEach((listenerFunc) => {
68
+ target.removeEventListener(key, listenerFunc);
69
+ });
70
+ });
71
+ });
72
+ this.eventsByTarget.clear();
73
+ }
74
+ static create(initial) {
75
+ const instance = new _EventHandlerCollection();
76
+ if (initial !== void 0) {
77
+ initial.forEach(([target, key, listenerFunc, options]) => {
78
+ instance.add(target, key, listenerFunc, options);
79
+ });
80
+ }
81
+ return instance;
82
+ }
83
+ };
84
+
30
85
  // src/tweakpane/tweakPaneActivity.ts
31
86
  var isTweakpaneActive = false;
32
87
  function setTweakpaneActive(status) {
@@ -38,7 +93,7 @@ function getTweakpaneActive() {
38
93
 
39
94
  // src/camera/CameraManager.ts
40
95
  var CameraManager = class {
41
- constructor(collisionsManager) {
96
+ constructor(targetElement, collisionsManager, initialPhi = Math.PI / 2, initialTheta = -Math.PI / 2) {
42
97
  this.collisionsManager = collisionsManager;
43
98
  this.initialDistance = 3.3;
44
99
  this.minDistance = 0.1;
@@ -53,42 +108,39 @@ var CameraManager = class {
53
108
  this.dampingFactor = 0.091;
54
109
  this.targetDistance = this.initialDistance;
55
110
  this.distance = this.initialDistance;
56
- this.targetPhi = Math.PI / 2;
57
- this.phi = Math.PI / 2;
58
- this.targetTheta = -Math.PI / 2;
59
- this.theta = -Math.PI / 2;
111
+ this.desiredDistance = this.initialDistance;
60
112
  this.dragging = false;
61
113
  this.target = new Vector32(0, 1.55, 0);
62
114
  this.hadTarget = false;
115
+ this.phi = initialPhi;
116
+ this.targetPhi = initialPhi;
117
+ this.theta = initialTheta;
118
+ this.targetTheta = initialTheta;
63
119
  this.camera = new PerspectiveCamera(this.fov, window.innerWidth / window.innerHeight, 0.1, 400);
64
120
  this.camera.position.set(0, 1.4, -this.initialDistance);
65
121
  this.rayCaster = new Raycaster();
66
- document.addEventListener("mousedown", this.onMouseDown.bind(this));
67
- document.addEventListener("mouseup", this.onMouseUp.bind(this));
68
- document.addEventListener("mousemove", this.onMouseMove.bind(this));
69
- document.addEventListener("wheel", this.onMouseWheel.bind(this));
70
- window.addEventListener("resize", this.onResize.bind(this));
71
- }
72
- onResize() {
73
- const width = window.innerWidth;
74
- const height = window.innerHeight;
75
- this.camera.aspect = width / height;
76
- this.camera.updateProjectionMatrix();
77
- }
78
- onMouseDown(_event) {
122
+ this.eventHandlerCollection = EventHandlerCollection.create([
123
+ [targetElement, "mousedown", this.onMouseDown.bind(this)],
124
+ [document, "mouseup", this.onMouseUp.bind(this)],
125
+ [document, "mousemove", this.onMouseMove.bind(this)],
126
+ [targetElement, "wheel", this.onMouseWheel.bind(this)]
127
+ ]);
128
+ }
129
+ onMouseDown() {
79
130
  this.dragging = true;
80
131
  }
81
132
  onMouseUp(_event) {
82
133
  this.dragging = false;
83
134
  }
84
135
  onMouseMove(event) {
85
- if (!this.dragging || getTweakpaneActive() === true)
136
+ if (!this.dragging || getTweakpaneActive())
86
137
  return;
87
138
  if (this.targetTheta === null || this.targetPhi === null)
88
139
  return;
89
140
  this.targetTheta += event.movementX * 0.01;
90
141
  this.targetPhi -= event.movementY * 0.01;
91
142
  this.targetPhi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.targetPhi));
143
+ event.preventDefault();
92
144
  }
93
145
  onMouseWheel(event) {
94
146
  const scrollAmount = event.deltaY * 1e-3;
@@ -97,6 +149,8 @@ var CameraManager = class {
97
149
  this.minDistance,
98
150
  Math.min(this.maxDistance, this.targetDistance)
99
151
  );
152
+ this.desiredDistance = this.targetDistance;
153
+ event.preventDefault();
100
154
  }
101
155
  setTarget(target) {
102
156
  this.target.copy(target);
@@ -128,8 +182,13 @@ var CameraManager = class {
128
182
  if (minimumDistance !== null && minimumDistance <= cameraToPlayerDistance) {
129
183
  this.targetDistance = cameraToPlayerDistance - minimumDistance;
130
184
  this.distance = this.targetDistance;
185
+ } else {
186
+ this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
131
187
  }
132
188
  }
189
+ dispose() {
190
+ this.eventHandlerCollection.clear();
191
+ }
133
192
  update() {
134
193
  if (this.target === null)
135
194
  return;
@@ -159,15 +218,15 @@ var CameraManager = class {
159
218
  };
160
219
 
161
220
  // src/character/Character.ts
162
- import { Color as Color3, Vector3 as Vector34 } from "three";
221
+ import { Color as Color3, Vector3 as Vector35 } from "three";
163
222
 
164
223
  // src/character/CharacterModel.ts
165
224
  import {
166
- AnimationClip as AnimationClip2,
225
+ AnimationClip,
167
226
  AnimationMixer,
168
227
  LoopRepeat,
169
228
  MeshStandardMaterial,
170
- Object3D as Object3D2
229
+ Object3D
171
230
  } from "three";
172
231
 
173
232
  // src/character/CharacterMaterial.ts
@@ -542,128 +601,14 @@ var AnimationState = /* @__PURE__ */ ((AnimationState2) => {
542
601
  return AnimationState2;
543
602
  })(AnimationState || {});
544
603
 
545
- // src/character/ModelLoader.ts
546
- import { LoadingManager } from "three";
547
- import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
548
- var CachedGLTFLoader = class extends ThreeGLTFLoader {
549
- constructor(manager) {
550
- super(manager);
551
- this.blobCache = /* @__PURE__ */ new Map();
552
- }
553
- setBlobUrl(originalUrl, blobUrl) {
554
- this.blobCache.set(originalUrl, blobUrl);
555
- }
556
- getBlobUrl(originalUrl) {
557
- return this.blobCache.get(originalUrl);
558
- }
559
- load(url, onLoad, onProgress, onError) {
560
- const blobUrl = this.getBlobUrl(url);
561
- if (blobUrl) {
562
- console.log(`Loading cached ${url.split("/").pop()}`);
563
- super.load(blobUrl, onLoad, onProgress, onError);
564
- } else {
565
- super.load(url, onLoad, onProgress, onError);
566
- }
567
- }
568
- };
569
- var LRUCache = class {
570
- constructor(maxSize = 100) {
571
- this.maxSize = maxSize;
572
- this.cache = /* @__PURE__ */ new Map();
573
- }
574
- get(key) {
575
- const item = this.cache.get(key);
576
- if (item) {
577
- this.cache.delete(key);
578
- this.cache.set(key, item);
579
- }
580
- return item;
581
- }
582
- set(key, value) {
583
- if (this.cache.size >= this.maxSize) {
584
- const oldestKey = this.cache.keys().next().value;
585
- this.cache.delete(oldestKey);
586
- }
587
- this.cache.set(key, value);
588
- }
589
- };
590
- var _ModelLoader = class _ModelLoader {
591
- constructor(maxCacheSize = 100) {
592
- this.ongoingLoads = /* @__PURE__ */ new Map();
593
- this.loadingManager = new LoadingManager();
594
- this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
595
- this.modelCache = new LRUCache(maxCacheSize);
596
- }
597
- /* TODO: decide between below lazy initialization or eager on this file's bottom export */
598
- static getInstance() {
599
- if (!_ModelLoader.instance) {
600
- _ModelLoader.instance = new _ModelLoader();
601
- }
602
- return _ModelLoader.instance;
603
- }
604
- async load(fileUrl, fileType) {
605
- const cachedModel = this.modelCache.get(fileUrl);
606
- if (cachedModel) {
607
- const blobURL = URL.createObjectURL(cachedModel.blob);
608
- this.gltfLoader.setBlobUrl(fileUrl, blobURL);
609
- return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
610
- } else {
611
- console.log(`Loading ${fileUrl} from server`);
612
- const ongoingLoad = this.ongoingLoads.get(fileUrl);
613
- if (ongoingLoad)
614
- return ongoingLoad;
615
- const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
616
- const originalExtension = fileUrl.split(".").pop() || "";
617
- this.modelCache.set(fileUrl, { blob, originalExtension });
618
- const blobURL = URL.createObjectURL(blob);
619
- this.ongoingLoads.delete(fileUrl);
620
- return this.loadFromUrl(blobURL, fileType, originalExtension);
621
- });
622
- this.ongoingLoads.set(fileUrl, loadPromise);
623
- return loadPromise;
624
- }
625
- }
626
- async loadFromUrl(url, fileType, extension) {
627
- if (["gltf", "glb"].includes(extension)) {
628
- return new Promise((resolve, reject) => {
629
- this.gltfLoader.load(
630
- url,
631
- (object) => {
632
- if (fileType === "model") {
633
- resolve(object.scene);
634
- } else if (fileType === "animation") {
635
- resolve(object.animations[0]);
636
- } else {
637
- const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
638
- console.error(error);
639
- reject(error);
640
- }
641
- },
642
- void 0,
643
- (error) => {
644
- console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
645
- reject(error);
646
- }
647
- );
648
- });
649
- } else {
650
- console.error(`Error: can't recognize ${url} extension: ${extension}`);
651
- }
652
- }
653
- };
654
- _ModelLoader.instance = null;
655
- var ModelLoader = _ModelLoader;
656
- var MODEL_LOADER = ModelLoader.getInstance();
657
- var ModelLoader_default = MODEL_LOADER;
658
-
659
604
  // src/character/CharacterModel.ts
660
605
  var CharacterModel = class {
661
- constructor(characterDescription) {
606
+ constructor(characterDescription, characterModelLoader) {
662
607
  this.characterDescription = characterDescription;
663
- /* TODO: pick between below eager instantiation or ModelLoader.getInstance() lazy one */
664
- this.modelLoader = ModelLoader_default;
608
+ this.characterModelLoader = characterModelLoader;
665
609
  this.mesh = null;
666
610
  this.material = new CharacterMaterial();
611
+ this.headBone = null;
667
612
  this.animations = {};
668
613
  this.animationMixer = null;
669
614
  this.currentAnimation = 0 /* idle */;
@@ -699,6 +644,9 @@ var CharacterModel = class {
699
644
  if (!this.mesh)
700
645
  return;
701
646
  this.mesh.traverse((child) => {
647
+ if (child.type === "Bone" && child.name === "mixamorigHeadTop_End") {
648
+ this.headBone = child;
649
+ }
702
650
  if (child.type === "SkinnedMesh" && child.name === meshName) {
703
651
  child.material = new MeshStandardMaterial({
704
652
  color: 16711680,
@@ -735,9 +683,9 @@ var CharacterModel = class {
735
683
  const scale = this.characterDescription.modelScale;
736
684
  const extension = mainMeshUrl.split(".").pop();
737
685
  const name = mainMeshUrl.split("/").pop().replace(`.${extension}`, "");
738
- const mainMesh = await this.modelLoader.load(mainMeshUrl, "model");
686
+ const mainMesh = await this.characterModelLoader.load(mainMeshUrl, "model");
739
687
  if (typeof mainMesh !== "undefined") {
740
- this.mesh = new Object3D2();
688
+ this.mesh = new Object3D();
741
689
  const model = mainMesh;
742
690
  model.position.set(0, -0.4, 0);
743
691
  this.mesh.add(model);
@@ -749,8 +697,8 @@ var CharacterModel = class {
749
697
  async setAnimationFromFile(animationFileUrl, animationType) {
750
698
  return new Promise(async (resolve, reject) => {
751
699
  this.initAnimationMixer();
752
- const animation = await this.modelLoader.load(animationFileUrl, "animation");
753
- if (typeof animation !== "undefined" && animation instanceof AnimationClip2) {
700
+ const animation = await this.characterModelLoader.load(animationFileUrl, "animation");
701
+ if (typeof animation !== "undefined" && animation instanceof AnimationClip) {
754
702
  this.animations[animationType] = this.animationMixer.clipAction(animation);
755
703
  this.animations[animationType].stop();
756
704
  if (animationType === 0 /* idle */) {
@@ -782,12 +730,104 @@ var CharacterModel = class {
782
730
  }
783
731
  };
784
732
 
733
+ // src/character/CharacterSpeakingIndicator.ts
734
+ import {
735
+ CircleGeometry,
736
+ GLSL3,
737
+ Mesh as Mesh2,
738
+ RawShaderMaterial
739
+ } from "three";
740
+ var CharacterSpeakingIndicator = class {
741
+ constructor(scene) {
742
+ this.scene = scene;
743
+ this.vertexShader = /* glsl */
744
+ `
745
+ in vec3 position;
746
+ in vec2 uv;
747
+ uniform mat4 modelViewMatrix;
748
+ uniform mat4 projectionMatrix;
749
+ out vec2 vUv;
750
+ void main() {
751
+ vUv = uv;
752
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
753
+ }`;
754
+ this.fragmentShader = /* glsl */
755
+ `
756
+ precision highp float;
757
+
758
+ uniform float time;
759
+ uniform float alpha;
760
+ in vec2 vUv;
761
+ out vec4 fragColor;
762
+
763
+ const float size = 1.7;
764
+ const float distribution = 0.03;
765
+ const float speed = 0.2;
766
+ const float overdraw = 3.5;
767
+ const float shapeK = 0.25;
768
+
769
+ float sdHyperbola(vec2 p, float k, float wi) {
770
+ p = abs(p);
771
+ float k2 = k * k;
772
+ float a = p.x + p.y;
773
+ float i = 0.5 * (a - k2 / a) > wi ? -1.0 : 1.0;
774
+ float x = clamp(0.5 * (a - k2 / a), 0.0, wi);
775
+ vec2 q = vec2(x, sqrt(x * x + k2));
776
+ float s = sign(p.x * p.x - p.y * p.y + k2);
777
+ return s * length(p - q);
778
+ }
779
+
780
+ void main(void) {
781
+ vec2 uv = (vUv * 2.0 - 1.0);
782
+ float r = -(uv.x * uv.x + uv.y * uv.y);
783
+ float z = 0.5 + 0.5 * sin((r + time * speed) / distribution);
784
+ float a = clamp(smoothstep(-0.1, 0.2, size - length(uv * 2.0)), 0.0, 0.5);
785
+ float h = clamp(sdHyperbola(uv, shapeK, 1.0), 0.0, 1.0) * overdraw;
786
+ float fragAlpha = clamp(a * h, 0.0, 0.7);
787
+ fragColor = vec4(z * fragAlpha) * alpha;
788
+ }`;
789
+ this.uniforms = {
790
+ time: { value: 0 },
791
+ alpha: { value: 0 }
792
+ };
793
+ this.geometry = new CircleGeometry(0.35, 21);
794
+ this.material = new RawShaderMaterial({
795
+ vertexShader: this.vertexShader,
796
+ fragmentShader: this.fragmentShader,
797
+ uniforms: this.uniforms,
798
+ transparent: true,
799
+ glslVersion: GLSL3
800
+ });
801
+ this.mesh = new Mesh2(this.geometry, this.material);
802
+ this.currentAlpha = 0;
803
+ this.targetAlpha = 0;
804
+ this.scene.add(this.mesh);
805
+ }
806
+ setBillboarding(position, camera) {
807
+ this.mesh.position.set(position.x, position.y - 0.15, position.z);
808
+ this.mesh.lookAt(camera.position);
809
+ }
810
+ setTime(value) {
811
+ this.currentAlpha += ease(this.targetAlpha, this.currentAlpha, 0.06);
812
+ this.uniforms.time.value = value;
813
+ this.uniforms.alpha.value = this.currentAlpha;
814
+ }
815
+ setSpeaking(value) {
816
+ this.targetAlpha = value === true ? 1 : 0;
817
+ }
818
+ dispose() {
819
+ this.scene.remove(this.mesh);
820
+ this.mesh.material.dispose();
821
+ this.mesh.geometry.dispose();
822
+ }
823
+ };
824
+
785
825
  // src/character/CharacterTooltip.ts
786
826
  import {
787
827
  Color as Color2,
788
828
  FrontSide,
789
829
  LinearFilter as LinearFilter2,
790
- Mesh as Mesh2,
830
+ Mesh as Mesh3,
791
831
  MeshBasicMaterial as MeshBasicMaterial2,
792
832
  PlaneGeometry
793
833
  } from "three";
@@ -937,7 +977,7 @@ var CharacterTooltip = class {
937
977
  });
938
978
  this.material.side = FrontSide;
939
979
  this.geometry = new PlaneGeometry(1, 1, 1, 1);
940
- this.mesh = new Mesh2(this.geometry, this.material);
980
+ this.mesh = new Mesh3(this.geometry, this.material);
941
981
  this.mesh.position.set(0, 1.6, 0);
942
982
  this.mesh.visible = false;
943
983
  parentModel.add(this.mesh);
@@ -1020,7 +1060,7 @@ var CharacterTooltip = class {
1020
1060
  };
1021
1061
 
1022
1062
  // src/character/LocalController.ts
1023
- import { Line3, Matrix4, Quaternion, Raycaster as Raycaster2, Vector3 as Vector33 } from "three";
1063
+ import { Line3, Matrix4, Quaternion as Quaternion2, Raycaster as Raycaster2, Vector3 as Vector34 } from "three";
1024
1064
  var LocalController = class {
1025
1065
  constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1026
1066
  this.model = model;
@@ -1032,7 +1072,7 @@ var LocalController = class {
1032
1072
  this.collisionDetectionSteps = 15;
1033
1073
  this.capsuleInfo = {
1034
1074
  radius: 0.4,
1035
- segment: new Line3(new Vector33(), new Vector33(0, 1.05, 0))
1075
+ segment: new Line3(new Vector34(), new Vector34(0, 1.05, 0))
1036
1076
  };
1037
1077
  this.maxWalkSpeed = 6;
1038
1078
  this.maxRunSpeed = 8.5;
@@ -1045,15 +1085,15 @@ var LocalController = class {
1045
1085
  this.characterWasOnGround = false;
1046
1086
  this.characterAirborneSince = 0;
1047
1087
  this.currentHeight = 0;
1048
- this.characterVelocity = new Vector33();
1049
- this.vectorUp = new Vector33(0, 1, 0);
1050
- this.vectorDown = new Vector33(0, -1, 0);
1088
+ this.characterVelocity = new Vector34();
1089
+ this.vectorUp = new Vector34(0, 1, 0);
1090
+ this.vectorDown = new Vector34(0, -1, 0);
1051
1091
  this.rotationOffset = 0;
1052
1092
  this.azimuthalAngle = 0;
1053
1093
  this.tempMatrix = new Matrix4();
1054
1094
  this.tempSegment = new Line3();
1055
- this.tempVector = new Vector33();
1056
- this.tempVector2 = new Vector33();
1095
+ this.tempVector = new Vector34();
1096
+ this.tempVector2 = new Vector34();
1057
1097
  this.rayCaster = new Raycaster2();
1058
1098
  this.thirdPersonCamera = null;
1059
1099
  this.speed = 0;
@@ -1141,7 +1181,7 @@ var LocalController = class {
1141
1181
  const camToModelDistance = this.thirdPersonCamera.position.distanceTo(this.model.mesh.position);
1142
1182
  const isCameraFirstPerson = camToModelDistance < 2;
1143
1183
  if (isCameraFirstPerson) {
1144
- const cameraForward = new Vector33(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
1184
+ const cameraForward = new Vector34(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
1145
1185
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1146
1186
  } else {
1147
1187
  this.azimuthalAngle = Math.atan2(
@@ -1162,7 +1202,7 @@ var LocalController = class {
1162
1202
  return;
1163
1203
  this.updateRotationOffset();
1164
1204
  this.updateAzimuthalAngle();
1165
- const rotationQuaternion = new Quaternion();
1205
+ const rotationQuaternion = new Quaternion2();
1166
1206
  rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
1167
1207
  const angularDifference = this.computeAngularDifference(rotationQuaternion);
1168
1208
  const desiredTime = 0.07;
@@ -1190,7 +1230,6 @@ var LocalController = class {
1190
1230
  this.characterVelocity.y = deltaTime * this.gravity;
1191
1231
  }
1192
1232
  } else if (this.jump && this.coyoteTime) {
1193
- console.log("coyoteJump");
1194
1233
  this.characterVelocity.y = this.jumpForce;
1195
1234
  this.canJump = false;
1196
1235
  } else {
@@ -1200,19 +1239,19 @@ var LocalController = class {
1200
1239
  this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
1201
1240
  this.tempVector.set(0, 0, 0);
1202
1241
  if (this.forward) {
1203
- const forward = new Vector33(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1242
+ const forward = new Vector34(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1204
1243
  this.tempVector.add(forward);
1205
1244
  }
1206
1245
  if (this.backward) {
1207
- const backward = new Vector33(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1246
+ const backward = new Vector34(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1208
1247
  this.tempVector.add(backward);
1209
1248
  }
1210
1249
  if (this.left) {
1211
- const left = new Vector33(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1250
+ const left = new Vector34(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1212
1251
  this.tempVector.add(left);
1213
1252
  }
1214
1253
  if (this.right) {
1215
- const right = new Vector33(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1254
+ const right = new Vector34(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1216
1255
  this.tempVector.add(right);
1217
1256
  }
1218
1257
  if (this.tempVector.length() > 0) {
@@ -1249,13 +1288,13 @@ var LocalController = class {
1249
1288
  if (!((_a = this.model) == null ? void 0 : _a.mesh)) {
1250
1289
  this.networkState = {
1251
1290
  id: this.id,
1252
- position: new Vector33(),
1291
+ position: new Vector34(),
1253
1292
  rotation: { quaternionY: 0, quaternionW: 1 },
1254
1293
  state: 0 /* idle */
1255
1294
  };
1256
1295
  } else {
1257
- const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion());
1258
- const positionUpdate = new Vector33(
1296
+ const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion2());
1297
+ const positionUpdate = new Vector34(
1259
1298
  this.model.mesh.position.x,
1260
1299
  this.model.mesh.position.y,
1261
1300
  this.model.mesh.position.z
@@ -1280,8 +1319,9 @@ var LocalController = class {
1280
1319
 
1281
1320
  // src/character/Character.ts
1282
1321
  var Character = class {
1283
- constructor(characterDescription, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager) {
1322
+ constructor(characterDescription, characterModelLoader, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager, composer) {
1284
1323
  this.characterDescription = characterDescription;
1324
+ this.characterModelLoader = characterModelLoader;
1285
1325
  this.id = id;
1286
1326
  this.isLocal = isLocal;
1287
1327
  this.modelLoadedCallback = modelLoadedCallback;
@@ -1289,20 +1329,25 @@ var Character = class {
1289
1329
  this.keyInputManager = keyInputManager;
1290
1330
  this.cameraManager = cameraManager;
1291
1331
  this.timeManager = timeManager;
1332
+ this.composer = composer;
1292
1333
  this.controller = null;
1293
1334
  this.name = null;
1294
1335
  this.model = null;
1295
1336
  this.color = new Color3();
1296
- this.position = new Vector34();
1337
+ this.position = new Vector35();
1297
1338
  this.tooltip = null;
1339
+ this.speakingIndicator = null;
1298
1340
  this.load();
1299
1341
  }
1300
1342
  async load() {
1301
- this.model = new CharacterModel(this.characterDescription);
1343
+ this.model = new CharacterModel(this.characterDescription, this.characterModelLoader);
1302
1344
  await this.model.init();
1303
1345
  if (this.tooltip === null) {
1304
1346
  this.tooltip = new CharacterTooltip(this.model.mesh);
1305
1347
  }
1348
+ if (this.speakingIndicator === null) {
1349
+ this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1350
+ }
1306
1351
  this.color = this.model.material.colorsCube216[this.id];
1307
1352
  if (this.isLocal) {
1308
1353
  this.controller = new LocalController(
@@ -1317,11 +1362,21 @@ var Character = class {
1317
1362
  this.modelLoadedCallback();
1318
1363
  }
1319
1364
  update(time) {
1365
+ var _a;
1320
1366
  if (!this.model)
1321
1367
  return;
1322
1368
  if (this.tooltip) {
1323
1369
  this.tooltip.update(this.cameraManager.camera);
1324
1370
  }
1371
+ if (this.speakingIndicator) {
1372
+ this.speakingIndicator.setTime(time);
1373
+ if (this.model.mesh && this.model.headBone) {
1374
+ this.speakingIndicator.setBillboarding(
1375
+ (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector35()),
1376
+ this.cameraManager.camera
1377
+ );
1378
+ }
1379
+ }
1325
1380
  this.model.mesh.getWorldPosition(this.position);
1326
1381
  if (typeof this.model.material.uniforms.time !== "undefined") {
1327
1382
  this.model.material.uniforms.time.value = time;
@@ -1332,20 +1387,20 @@ var Character = class {
1332
1387
  };
1333
1388
 
1334
1389
  // src/character/CharacterManager.ts
1335
- import { Group, Vector3 as Vector36 } from "three";
1390
+ import { Euler, Group, Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1336
1391
 
1337
1392
  // src/character/RemoteController.ts
1338
1393
  import {
1339
1394
  AnimationMixer as AnimationMixer2,
1340
1395
  Object3D as Object3D4,
1341
- Quaternion as Quaternion2,
1342
- Vector3 as Vector35
1396
+ Quaternion as Quaternion3,
1397
+ Vector3 as Vector36
1343
1398
  } from "three";
1344
1399
  var RemoteController = class {
1345
- constructor(character, id) {
1400
+ constructor(character, characterModelLoader, id) {
1346
1401
  this.character = character;
1402
+ this.characterModelLoader = characterModelLoader;
1347
1403
  this.id = id;
1348
- this.modelLoader = ModelLoader_default;
1349
1404
  this.characterModel = null;
1350
1405
  this.animationMixer = new AnimationMixer2(new Object3D4());
1351
1406
  this.animations = /* @__PURE__ */ new Map();
@@ -1368,7 +1423,7 @@ var RemoteController = class {
1368
1423
  this.animationMixer.update(deltaTime);
1369
1424
  }
1370
1425
  async setAnimationFromFile(animationType, fileName) {
1371
- const animation = await this.modelLoader.load(fileName, "animation");
1426
+ const animation = await this.characterModelLoader.load(fileName, "animation");
1372
1427
  const animationAction = this.animationMixer.clipAction(animation);
1373
1428
  this.animations.set(animationType, animationAction);
1374
1429
  if (animationType === 0 /* idle */) {
@@ -1395,8 +1450,8 @@ var RemoteController = class {
1395
1450
  if (!this.characterModel)
1396
1451
  return;
1397
1452
  const { position, rotation, state } = clientUpdate;
1398
- this.characterModel.position.lerp(new Vector35(position.x, position.y, position.z), 0.15);
1399
- const rotationQuaternion = new Quaternion2(0, rotation.quaternionY, 0, rotation.quaternionW);
1453
+ this.characterModel.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1454
+ const rotationQuaternion = new Quaternion3(0, rotation.quaternionY, 0, rotation.quaternionW);
1400
1455
  this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
1401
1456
  if (state !== this.currentAnimation) {
1402
1457
  this.transitionToAnimation(state);
@@ -1407,10 +1462,10 @@ var RemoteController = class {
1407
1462
  // src/character/CharacterManager.ts
1408
1463
  function encodeCharacterAndCamera(character, camera) {
1409
1464
  return [
1410
- ...character.position.toArray(),
1411
- ...character.quaternion.toArray(),
1412
- ...camera.position.toArray(),
1413
- ...camera.quaternion.toArray()
1465
+ ...toArray(character.position),
1466
+ ...toArray(character.quaternion),
1467
+ ...toArray(camera.position),
1468
+ ...toArray(camera.quaternion)
1414
1469
  ].join(",");
1415
1470
  }
1416
1471
  function decodeCharacterAndCamera(hash, character, camera) {
@@ -1421,7 +1476,9 @@ function decodeCharacterAndCamera(hash, character, camera) {
1421
1476
  camera.quaternion.fromArray(values.slice(10, 14));
1422
1477
  }
1423
1478
  var CharacterManager = class {
1424
- constructor(collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1479
+ constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1480
+ this.composer = composer;
1481
+ this.characterModelLoader = characterModelLoader;
1425
1482
  this.collisionsManager = collisionsManager;
1426
1483
  this.cameraManager = cameraManager;
1427
1484
  this.timeManager = timeManager;
@@ -1434,6 +1491,7 @@ var CharacterManager = class {
1434
1491
  can be trapped).
1435
1492
  */
1436
1493
  this.updateLocationHash = false;
1494
+ this.id = 0;
1437
1495
  this.loadingCharacters = /* @__PURE__ */ new Map();
1438
1496
  this.remoteCharacters = /* @__PURE__ */ new Map();
1439
1497
  this.remoteCharacterControllers = /* @__PURE__ */ new Map();
@@ -1441,6 +1499,7 @@ var CharacterManager = class {
1441
1499
  this.character = null;
1442
1500
  this.cameraOffsetTarget = 0;
1443
1501
  this.cameraOffset = 0;
1502
+ this.speakingCharacters = /* @__PURE__ */ new Map();
1444
1503
  this.group = new Group();
1445
1504
  }
1446
1505
  /* TODO:
@@ -1449,11 +1508,12 @@ var CharacterManager = class {
1449
1508
  the mesh loading async (would allow us to show a nameplate where a remote
1450
1509
  user is before the asset loads).
1451
1510
  */
1452
- spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector36()) {
1511
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector37(), spawnRotation = new Euler()) {
1453
1512
  this.characterDescription = characterDescription;
1454
1513
  const characterLoadingPromise = new Promise((resolve) => {
1455
1514
  const character = new Character(
1456
1515
  characterDescription,
1516
+ this.characterModelLoader,
1457
1517
  id,
1458
1518
  isLocal,
1459
1519
  () => {
@@ -1465,9 +1525,12 @@ var CharacterManager = class {
1465
1525
  this.cameraManager.camera
1466
1526
  );
1467
1527
  } else {
1468
- spawnPosition = getSpawnPositionInsideCircle(3, 30, id, 0.4);
1528
+ spawnPosition = spawnPosition || getSpawnPositionInsideCircle(3, 30, id, 0.4);
1469
1529
  character.model.mesh.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1530
+ character.model.mesh.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1531
+ character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1470
1532
  character.model.mesh.updateMatrixWorld();
1533
+ const quaternion = new Quaternion4().setFromEuler(character.model.mesh.rotation);
1471
1534
  this.sendUpdate({
1472
1535
  id,
1473
1536
  position: {
@@ -1475,7 +1538,7 @@ var CharacterManager = class {
1475
1538
  y: spawnPosition.y,
1476
1539
  z: spawnPosition.z
1477
1540
  },
1478
- rotation: { quaternionY: 0, quaternionW: 1 },
1541
+ rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1479
1542
  state: 0 /* idle */
1480
1543
  });
1481
1544
  }
@@ -1487,11 +1550,12 @@ var CharacterManager = class {
1487
1550
  }
1488
1551
  this.group.add(character.model.mesh);
1489
1552
  if (isLocal) {
1553
+ this.id = id;
1490
1554
  this.character = character;
1491
1555
  (_e = this.character.tooltip) == null ? void 0 : _e.setText(`${id}`);
1492
1556
  } else {
1493
1557
  this.remoteCharacters.set(id, character);
1494
- const remoteController = new RemoteController(character, id);
1558
+ const remoteController = new RemoteController(character, this.characterModelLoader, id);
1495
1559
  remoteController.setAnimationFromFile(
1496
1560
  0 /* idle */,
1497
1561
  characterDescription.idleAnimationFileUrl
@@ -1521,7 +1585,8 @@ var CharacterManager = class {
1521
1585
  this.collisionsManager,
1522
1586
  this.inputManager,
1523
1587
  this.cameraManager,
1524
- this.timeManager
1588
+ this.timeManager,
1589
+ this.composer
1525
1590
  );
1526
1591
  });
1527
1592
  this.loadingCharacters.set(id, characterLoadingPromise);
@@ -1551,14 +1616,20 @@ var CharacterManager = class {
1551
1616
  }
1552
1617
  this.loadingCharacters.clear();
1553
1618
  }
1619
+ setSpeakingCharacter(id, value) {
1620
+ this.speakingCharacters.set(id, value);
1621
+ }
1554
1622
  update() {
1555
- var _a;
1623
+ var _a, _b, _c, _d;
1556
1624
  if (this.character) {
1557
1625
  this.character.update(this.timeManager.time);
1558
- if ((_a = this.character.model) == null ? void 0 : _a.mesh) {
1559
- this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.6 : 0;
1626
+ if (this.speakingCharacters.has(this.id)) {
1627
+ (_a = this.character.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1628
+ }
1629
+ if ((_b = this.character.model) == null ? void 0 : _b.mesh) {
1630
+ this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1560
1631
  this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1561
- const targetOffset = new Vector36(0, 1.3, this.cameraOffset);
1632
+ const targetOffset = new Vector37(0, 1.3, this.cameraOffset);
1562
1633
  targetOffset.applyQuaternion(this.character.model.mesh.quaternion);
1563
1634
  this.cameraManager.setTarget(this.character.position.add(targetOffset));
1564
1635
  }
@@ -1569,13 +1640,17 @@ var CharacterManager = class {
1569
1640
  }
1570
1641
  }
1571
1642
  for (const [id, update] of this.clientStates) {
1643
+ if (this.remoteCharacters.has(id) && this.speakingCharacters.has(id)) {
1644
+ const character = this.remoteCharacters.get(id);
1645
+ (_c = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _c.setSpeaking(this.speakingCharacters.get(id));
1646
+ }
1572
1647
  const { position } = update;
1573
1648
  if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1574
1649
  this.spawnCharacter(
1575
1650
  this.characterDescription,
1576
1651
  id,
1577
1652
  false,
1578
- new Vector36(position.x, position.y, position.z)
1653
+ new Vector37(position.x, position.y, position.z)
1579
1654
  ).then((_character) => {
1580
1655
  this.loadingCharacters.delete(id);
1581
1656
  });
@@ -1587,6 +1662,7 @@ var CharacterManager = class {
1587
1662
  }
1588
1663
  for (const [id, character] of this.remoteCharacters) {
1589
1664
  if (!this.clientStates.has(id)) {
1665
+ (_d = character.speakingIndicator) == null ? void 0 : _d.dispose();
1590
1666
  this.group.remove(character.model.mesh);
1591
1667
  this.remoteCharacters.delete(id);
1592
1668
  this.remoteCharacterControllers.delete(id);
@@ -1602,57 +1678,179 @@ var CharacterManager = class {
1602
1678
  }
1603
1679
  };
1604
1680
 
1605
- // src/input/KeyInputManager.ts
1606
- var KeyInputManager = class {
1607
- constructor() {
1608
- this.keys = /* @__PURE__ */ new Map();
1609
- document.addEventListener("keydown", this.onKeyDown.bind(this));
1610
- document.addEventListener("keyup", this.onKeyUp.bind(this));
1611
- window.addEventListener("blur", this.handleUnfocus.bind(this));
1612
- }
1613
- handleUnfocus(_event) {
1614
- this.keys.clear();
1615
- }
1616
- onKeyDown(event) {
1617
- this.keys.set(event.key.toLowerCase(), true);
1618
- }
1619
- onKeyUp(event) {
1620
- this.keys.set(event.key.toLowerCase(), false);
1621
- }
1622
- isKeyPressed(key) {
1623
- return this.keys.get(key) || false;
1681
+ // src/character/CharacterModelLoader.ts
1682
+ import { LoadingManager } from "three";
1683
+ import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
1684
+ var CachedGLTFLoader = class extends ThreeGLTFLoader {
1685
+ constructor(manager) {
1686
+ super(manager);
1687
+ this.blobCache = /* @__PURE__ */ new Map();
1624
1688
  }
1625
- isMovementKeyPressed() {
1626
- return ["w", "a", "s", "d"].some((key) => this.isKeyPressed(key));
1689
+ setBlobUrl(originalUrl, blobUrl) {
1690
+ this.blobCache.set(originalUrl, blobUrl);
1627
1691
  }
1628
- get forward() {
1629
- return this.isKeyPressed("w");
1692
+ getBlobUrl(originalUrl) {
1693
+ return this.blobCache.get(originalUrl);
1630
1694
  }
1631
- get backward() {
1632
- return this.isKeyPressed("s");
1695
+ load(url, onLoad, onProgress, onError) {
1696
+ const blobUrl = this.getBlobUrl(url);
1697
+ if (blobUrl) {
1698
+ console.log(`Loading cached ${url.split("/").pop()}`);
1699
+ super.load(blobUrl, onLoad, onProgress, onError);
1700
+ } else {
1701
+ super.load(url, onLoad, onProgress, onError);
1702
+ }
1633
1703
  }
1634
- get left() {
1635
- return this.isKeyPressed("a");
1704
+ };
1705
+ var LRUCache = class {
1706
+ constructor(maxSize = 100) {
1707
+ this.maxSize = maxSize;
1708
+ this.cache = /* @__PURE__ */ new Map();
1636
1709
  }
1637
- get right() {
1638
- return this.isKeyPressed("d");
1710
+ get(key) {
1711
+ const item = this.cache.get(key);
1712
+ if (item) {
1713
+ this.cache.delete(key);
1714
+ this.cache.set(key, item);
1715
+ }
1716
+ return item;
1639
1717
  }
1640
- get run() {
1641
- return this.isKeyPressed("shift");
1718
+ set(key, value) {
1719
+ if (this.cache.size >= this.maxSize) {
1720
+ const oldestKey = this.cache.keys().next().value;
1721
+ this.cache.delete(oldestKey);
1722
+ }
1723
+ this.cache.set(key, value);
1642
1724
  }
1643
- get jump() {
1644
- return this.isKeyPressed(" ");
1725
+ };
1726
+ var _CharacterModelLoader = class _CharacterModelLoader {
1727
+ constructor(maxCacheSize = 100) {
1728
+ this.ongoingLoads = /* @__PURE__ */ new Map();
1729
+ this.loadingManager = new LoadingManager();
1730
+ this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
1731
+ this.modelCache = new LRUCache(maxCacheSize);
1645
1732
  }
1646
- get anyDirection() {
1647
- return this.isMovementKeyPressed();
1733
+ /* TODO: decide between below lazy initialization or eager on this file's bottom export */
1734
+ static getInstance() {
1735
+ if (!_CharacterModelLoader.instance) {
1736
+ _CharacterModelLoader.instance = new _CharacterModelLoader();
1737
+ }
1738
+ return _CharacterModelLoader.instance;
1648
1739
  }
1649
- get conflictingDirection() {
1650
- return this.isKeyPressed("w") && this.isKeyPressed("s") || this.isKeyPressed("a") && this.isKeyPressed("d");
1740
+ async load(fileUrl, fileType) {
1741
+ const cachedModel = this.modelCache.get(fileUrl);
1742
+ if (cachedModel) {
1743
+ const blobURL = URL.createObjectURL(cachedModel.blob);
1744
+ this.gltfLoader.setBlobUrl(fileUrl, blobURL);
1745
+ return this.loadFromUrl(fileUrl, fileType, cachedModel.originalExtension);
1746
+ } else {
1747
+ console.log(`Loading ${fileUrl} from server`);
1748
+ const ongoingLoad = this.ongoingLoads.get(fileUrl);
1749
+ if (ongoingLoad)
1750
+ return ongoingLoad;
1751
+ const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
1752
+ const originalExtension = fileUrl.split(".").pop() || "";
1753
+ this.modelCache.set(fileUrl, { blob, originalExtension });
1754
+ const blobURL = URL.createObjectURL(blob);
1755
+ this.ongoingLoads.delete(fileUrl);
1756
+ return this.loadFromUrl(blobURL, fileType, originalExtension);
1757
+ });
1758
+ this.ongoingLoads.set(fileUrl, loadPromise);
1759
+ return loadPromise;
1760
+ }
1761
+ }
1762
+ async loadFromUrl(url, fileType, extension) {
1763
+ if (["gltf", "glb"].includes(extension)) {
1764
+ return new Promise((resolve, reject) => {
1765
+ this.gltfLoader.load(
1766
+ url,
1767
+ (object) => {
1768
+ if (fileType === "model") {
1769
+ resolve(object.scene);
1770
+ } else if (fileType === "animation") {
1771
+ resolve(object.animations[0]);
1772
+ } else {
1773
+ const error = `Trying to load unknown ${fileType} type of element from file ${url}`;
1774
+ console.error(error);
1775
+ reject(error);
1776
+ }
1777
+ },
1778
+ void 0,
1779
+ (error) => {
1780
+ console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
1781
+ reject(error);
1782
+ }
1783
+ );
1784
+ });
1785
+ } else {
1786
+ console.error(`Error: can't recognize ${url} extension: ${extension}`);
1787
+ }
1788
+ }
1789
+ };
1790
+ _CharacterModelLoader.instance = null;
1791
+ var CharacterModelLoader = _CharacterModelLoader;
1792
+ var MODEL_LOADER = CharacterModelLoader.getInstance();
1793
+
1794
+ // src/input/KeyInputManager.ts
1795
+ var KeyInputManager = class {
1796
+ constructor(shouldCaptureKeyPress = () => true) {
1797
+ this.shouldCaptureKeyPress = shouldCaptureKeyPress;
1798
+ this.keys = /* @__PURE__ */ new Map();
1799
+ this.eventHandlerCollection = new EventHandlerCollection();
1800
+ this.eventHandlerCollection.add(document, "keydown", this.onKeyDown.bind(this));
1801
+ this.eventHandlerCollection.add(document, "keyup", this.onKeyUp.bind(this));
1802
+ this.eventHandlerCollection.add(window, "blur", this.handleUnfocus.bind(this));
1803
+ }
1804
+ handleUnfocus(_event) {
1805
+ this.keys.clear();
1806
+ }
1807
+ onKeyDown(event) {
1808
+ if (this.shouldCaptureKeyPress()) {
1809
+ if (event.key.length === 2 && event.key[0] === "F") {
1810
+ return;
1811
+ }
1812
+ if (event.metaKey) {
1813
+ return;
1814
+ }
1815
+ this.keys.set(event.key.toLowerCase(), true);
1816
+ event.preventDefault();
1817
+ }
1818
+ }
1819
+ onKeyUp(event) {
1820
+ this.keys.set(event.key.toLowerCase(), false);
1821
+ }
1822
+ isKeyPressed(key) {
1823
+ return this.keys.get(key) || false;
1824
+ }
1825
+ isMovementKeyPressed() {
1826
+ return ["w" /* W */, "a" /* A */, "s" /* S */, "d" /* D */].some((key) => this.isKeyPressed(key));
1827
+ }
1828
+ get forward() {
1829
+ return this.isKeyPressed("w" /* W */);
1830
+ }
1831
+ get backward() {
1832
+ return this.isKeyPressed("s" /* S */);
1833
+ }
1834
+ get left() {
1835
+ return this.isKeyPressed("a" /* A */);
1836
+ }
1837
+ get right() {
1838
+ return this.isKeyPressed("d" /* D */);
1839
+ }
1840
+ get run() {
1841
+ return this.isKeyPressed("shift" /* SHIFT */);
1842
+ }
1843
+ get jump() {
1844
+ return this.isKeyPressed(" " /* SPACE */);
1845
+ }
1846
+ get anyDirection() {
1847
+ return this.isMovementKeyPressed();
1848
+ }
1849
+ get conflictingDirection() {
1850
+ return this.isKeyPressed("w" /* W */) && this.isKeyPressed("s" /* S */) || this.isKeyPressed("a" /* A */) && this.isKeyPressed("d" /* D */);
1651
1851
  }
1652
1852
  dispose() {
1653
- document.removeEventListener("keydown", this.onKeyDown.bind(this));
1654
- document.removeEventListener("keyup", this.onKeyDown.bind(this));
1655
- window.removeEventListener("blur", this.handleUnfocus.bind(this));
1853
+ this.eventHandlerCollection.clear();
1656
1854
  }
1657
1855
  };
1658
1856
 
@@ -1660,23 +1858,25 @@ var KeyInputManager = class {
1660
1858
  import {
1661
1859
  InteractionManager,
1662
1860
  MMLClickTrigger,
1663
- PromptManager,
1664
- registerCustomElementsToWindow,
1665
- setGlobalMScene
1861
+ PromptManager
1666
1862
  } from "mml-web";
1667
1863
  import { Group as Group2 } from "three";
1668
1864
  var MMLCompositionScene = class {
1669
- constructor(renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation, documentAddresses) {
1865
+ constructor(targetElement, renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation) {
1670
1866
  this.renderer = renderer;
1671
1867
  this.scene = scene;
1672
1868
  this.camera = camera;
1673
1869
  this.audioListener = audioListener;
1674
1870
  this.collisionsManager = collisionsManager;
1675
1871
  this.getUserPositionAndRotation = getUserPositionAndRotation;
1676
- this.debug = false;
1677
1872
  this.group = new Group2();
1678
- this.promptManager = PromptManager.init(document.body);
1679
- const { interactionListener } = InteractionManager.init(document.body, this.camera, this.scene);
1873
+ this.promptManager = PromptManager.init(targetElement);
1874
+ const { interactionListener, interactionManager } = InteractionManager.init(
1875
+ targetElement,
1876
+ this.camera,
1877
+ this.scene
1878
+ );
1879
+ this.interactionManager = interactionManager;
1680
1880
  this.interactionListener = interactionListener;
1681
1881
  this.mmlScene = {
1682
1882
  getAudioListener: () => this.audioListener,
@@ -1707,54 +1907,65 @@ var MMLCompositionScene = class {
1707
1907
  this.promptManager.prompt(promptProps, callback);
1708
1908
  }
1709
1909
  };
1710
- setGlobalMScene(this.mmlScene);
1711
- registerCustomElementsToWindow(window);
1712
- this.clickTrigger = MMLClickTrigger.init(document, this.mmlScene);
1713
- if (this.debug) {
1714
- console.log(this.clickTrigger);
1715
- }
1716
- for (const address of documentAddresses) {
1717
- const frameElement = document.createElement("m-frame");
1718
- frameElement.setAttribute("src", address);
1719
- document.body.appendChild(frameElement);
1720
- }
1910
+ this.clickTrigger = MMLClickTrigger.init(targetElement, this.mmlScene);
1911
+ }
1912
+ dispose() {
1913
+ this.promptManager.dispose();
1914
+ this.clickTrigger.dispose();
1915
+ this.interactionManager.dispose();
1721
1916
  }
1722
1917
  };
1723
1918
 
1724
- // src/rendering/composer.ts
1725
- import { N8AOPostPass } from "n8ao";
1726
- import {
1727
- EffectComposer as EffectComposer2,
1728
- RenderPass,
1729
- EffectPass as EffectPass2,
1730
- FXAAEffect,
1731
- ShaderPass,
1732
- BloomEffect,
1733
- SSAOEffect as SSAOEffect2,
1734
- BlendFunction as BlendFunction2,
1735
- TextureEffect,
1736
- ToneMappingEffect,
1737
- SMAAEffect,
1738
- SMAAPreset,
1739
- EdgeDetectionMode,
1740
- PredicationMode,
1741
- NormalPass as NormalPass2
1742
- } from "postprocessing";
1743
- import {
1744
- AmbientLight,
1745
- Color as Color6,
1746
- Fog,
1747
- HalfFloatType,
1748
- LinearSRGBColorSpace,
1749
- LoadingManager as LoadingManager2,
1750
- PMREMGenerator,
1751
- Vector2 as Vector22,
1752
- WebGLRenderer as WebGLRenderer2
1753
- } from "three";
1754
- import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
1919
+ // src/tweakpane/TweakPane.ts
1920
+ import * as EssentialsPlugin from "@tweakpane/plugin-essentials";
1921
+ import { Pane } from "tweakpane";
1755
1922
 
1756
- // src/sun/Sun.ts
1757
- import { CameraHelper, Color as Color4, DirectionalLight, Group as Group3, OrthographicCamera, Vector3 as Vector37 } from "three";
1923
+ // src/tweakpane/blades/bcsFolder.ts
1924
+ var bcsValues = {
1925
+ brightness: 0,
1926
+ contrast: 1.25,
1927
+ saturation: 1
1928
+ };
1929
+ var bcsOptions = {
1930
+ brightness: {
1931
+ amount: { min: -1, max: 1, step: 0.01 }
1932
+ },
1933
+ contrast: {
1934
+ amount: { min: 0, max: 2, step: 0.01 }
1935
+ },
1936
+ saturation: {
1937
+ amount: { min: 0, max: 2, step: 0.01 }
1938
+ }
1939
+ };
1940
+ var BrightnessContrastSaturationFolder = class {
1941
+ constructor(parentFolder, expand = false) {
1942
+ this.folder = parentFolder.addFolder({
1943
+ title: "brightness / contrast / sat",
1944
+ expanded: expand
1945
+ });
1946
+ this.folder.addBinding(bcsValues, "brightness", bcsOptions.brightness.amount);
1947
+ this.folder.addBinding(bcsValues, "contrast", bcsOptions.contrast.amount);
1948
+ this.folder.addBinding(bcsValues, "saturation", bcsOptions.saturation.amount);
1949
+ }
1950
+ setupChangeEvent(brightnessContrastSaturation) {
1951
+ this.folder.on("change", (e) => {
1952
+ const target = e.target.key;
1953
+ if (!target)
1954
+ return;
1955
+ switch (target) {
1956
+ case "brightness":
1957
+ brightnessContrastSaturation.uniforms.brightness.value = e.value;
1958
+ break;
1959
+ case "contrast":
1960
+ brightnessContrastSaturation.uniforms.contrast.value = e.value;
1961
+ break;
1962
+ case "saturation":
1963
+ brightnessContrastSaturation.uniforms.saturation.value = e.value;
1964
+ break;
1965
+ }
1966
+ });
1967
+ }
1968
+ };
1758
1969
 
1759
1970
  // src/tweakpane/blades/environmentFolder.ts
1760
1971
  var sunValues = {
@@ -1911,135 +2122,6 @@ var EnvironmentFolder = class {
1911
2122
  }
1912
2123
  };
1913
2124
 
1914
- // src/sun/Sun.ts
1915
- var Sun = class extends Group3 {
1916
- constructor() {
1917
- super();
1918
- this.debug = false;
1919
- this.sunOffset = new Vector37(
1920
- 39 * (Math.PI / 180),
1921
- 50 * (Math.PI / 180),
1922
- 100
1923
- );
1924
- this.shadowResolution = 8192;
1925
- this.shadowCamFrustum = 50;
1926
- this.camHelper = null;
1927
- this.target = null;
1928
- this.shadowCamera = new OrthographicCamera(
1929
- -this.shadowCamFrustum,
1930
- this.shadowCamFrustum,
1931
- this.shadowCamFrustum,
1932
- -this.shadowCamFrustum,
1933
- 0.1,
1934
- 200
1935
- );
1936
- if (this.debug === true) {
1937
- this.camHelper = new CameraHelper(this.shadowCamera);
1938
- }
1939
- this.directionalLight = new DirectionalLight(16777215, 0.5);
1940
- this.directionalLight.shadow.normalBias = 0.05;
1941
- this.directionalLight.shadow.radius = 1.5;
1942
- this.directionalLight.shadow.camera = this.shadowCamera;
1943
- this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
1944
- this.directionalLight.castShadow = true;
1945
- this.setColor();
1946
- this.updateCharacterPosition(new Vector37(0, 0, 0));
1947
- this.add(this.directionalLight);
1948
- if (this.debug === true && this.camHelper instanceof CameraHelper) {
1949
- this.add(this.camHelper);
1950
- }
1951
- }
1952
- updateCharacterPosition(position) {
1953
- if (!position)
1954
- return;
1955
- this.target = position;
1956
- this.setSunPosition(this.sunOffset.x, this.sunOffset.y);
1957
- }
1958
- setAzimuthalAngle(angle) {
1959
- if (this.sunOffset)
1960
- this.sunOffset.x = angle;
1961
- if (this.target)
1962
- this.updateCharacterPosition(this.target);
1963
- }
1964
- setPolarAngle(angle) {
1965
- if (this.sunOffset)
1966
- this.sunOffset.y = angle;
1967
- if (this.target)
1968
- this.updateCharacterPosition(this.target);
1969
- }
1970
- setIntensity(intensity) {
1971
- this.directionalLight.intensity = intensity;
1972
- }
1973
- setColor() {
1974
- this.directionalLight.color = new Color4().setRGB(
1975
- sunValues.sunColor.r,
1976
- sunValues.sunColor.g,
1977
- sunValues.sunColor.b
1978
- );
1979
- }
1980
- setSunPosition(azimuthalAngle, polarAngle) {
1981
- if (!this.target)
1982
- return;
1983
- const distance = this.sunOffset.z;
1984
- const sphericalPosition = new Vector37(
1985
- distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
1986
- distance * Math.cos(polarAngle),
1987
- distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
1988
- );
1989
- const newSunPosition = this.target.clone().add(sphericalPosition);
1990
- this.directionalLight.position.set(newSunPosition.x, newSunPosition.y, newSunPosition.z);
1991
- this.directionalLight.target.position.copy(this.target.clone());
1992
- this.directionalLight.target.updateMatrixWorld();
1993
- }
1994
- };
1995
-
1996
- // src/tweakpane/blades/bcsFolder.ts
1997
- var bcsValues = {
1998
- brightness: 0,
1999
- contrast: 1.25,
2000
- saturation: 1
2001
- };
2002
- var bcsOptions = {
2003
- brightness: {
2004
- amount: { min: -1, max: 1, step: 0.01 }
2005
- },
2006
- contrast: {
2007
- amount: { min: 0, max: 2, step: 0.01 }
2008
- },
2009
- saturation: {
2010
- amount: { min: 0, max: 2, step: 0.01 }
2011
- }
2012
- };
2013
- var BrightnessContrastSaturationFolder = class {
2014
- constructor(parentFolder, expand = false) {
2015
- this.folder = parentFolder.addFolder({
2016
- title: "brightness / contrast / sat",
2017
- expanded: expand
2018
- });
2019
- this.folder.addBinding(bcsValues, "brightness", bcsOptions.brightness.amount);
2020
- this.folder.addBinding(bcsValues, "contrast", bcsOptions.contrast.amount);
2021
- this.folder.addBinding(bcsValues, "saturation", bcsOptions.saturation.amount);
2022
- }
2023
- setupChangeEvent(brightnessContrastSaturation) {
2024
- this.folder.on("change", (e) => {
2025
- const target = e.target.key;
2026
- if (!target)
2027
- return;
2028
- switch (target) {
2029
- case "brightness":
2030
- brightnessContrastSaturation.uniforms.brightness.value = e.value;
2031
- break;
2032
- case "contrast":
2033
- brightnessContrastSaturation.uniforms.contrast.value = e.value;
2034
- break;
2035
- case "saturation":
2036
- brightnessContrastSaturation.uniforms.saturation.value = e.value;
2037
- break;
2038
- }
2039
- });
2040
- }
2041
- };
2042
-
2043
2125
  // src/tweakpane/blades/postExtrasFolder.ts
2044
2126
  var extrasValues = {
2045
2127
  grain: 0.055,
@@ -2164,9 +2246,52 @@ var RendererFolder = class {
2164
2246
  }
2165
2247
  };
2166
2248
 
2249
+ // src/tweakpane/blades/rendererStatsFolder.ts
2250
+ var RendererStatsFolder = class {
2251
+ constructor(parentFolder, expanded = true) {
2252
+ this.statsData = {
2253
+ triangles: "0",
2254
+ geometries: "0",
2255
+ textures: "0",
2256
+ shaders: "0",
2257
+ postPasses: "0",
2258
+ drawCalls: "0",
2259
+ rawDeltaTime: "0",
2260
+ deltaTime: "0",
2261
+ FPS: "0"
2262
+ };
2263
+ this.folder = parentFolder.addFolder({ title: "renderStats", expanded });
2264
+ this.performance = this.folder.addFolder({ title: "performance", expanded: true });
2265
+ this.defails = this.folder.addFolder({ title: "pipeline details", expanded: false });
2266
+ this.folder.addBlade({ view: "separator" });
2267
+ this.performance.addBinding(this.statsData, "FPS", { readonly: true });
2268
+ this.performance.addBinding(this.statsData, "deltaTime", { readonly: true });
2269
+ this.performance.addBinding(this.statsData, "rawDeltaTime", { readonly: true });
2270
+ this.defails.addBinding(this.statsData, "triangles", { readonly: true });
2271
+ this.defails.addBinding(this.statsData, "geometries", { readonly: true });
2272
+ this.defails.addBinding(this.statsData, "textures", { readonly: true });
2273
+ this.defails.addBinding(this.statsData, "shaders", { readonly: true });
2274
+ this.defails.addBinding(this.statsData, "postPasses", { readonly: true });
2275
+ this.defails.addBinding(this.statsData, "drawCalls", { readonly: true });
2276
+ }
2277
+ update(renderer, composer, timeManager) {
2278
+ const { geometries, textures } = renderer.info.memory;
2279
+ const { triangles, calls } = renderer.info.render;
2280
+ this.statsData.triangles = triangles.toString();
2281
+ this.statsData.geometries = geometries.toString();
2282
+ this.statsData.textures = textures.toString();
2283
+ this.statsData.shaders = renderer.info.programs.length.toString();
2284
+ this.statsData.postPasses = composer.passes.length.toString();
2285
+ this.statsData.drawCalls = calls.toString();
2286
+ this.statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2287
+ this.statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2288
+ this.statsData.FPS = timeManager.fps.toString();
2289
+ }
2290
+ };
2291
+
2167
2292
  // src/tweakpane/blades/ssaoFolder.ts
2168
2293
  import { BlendFunction } from "postprocessing";
2169
- import { Color as Color5 } from "three";
2294
+ import { Color as Color4 } from "three";
2170
2295
  var ppssaoValues = {
2171
2296
  enabled: false,
2172
2297
  blendFunction: BlendFunction.MULTIPLY,
@@ -2358,7 +2483,7 @@ var SSAOFolder = class {
2358
2483
  }
2359
2484
  case "color": {
2360
2485
  const value = e.value;
2361
- ppssaoEffect.color = new Color5().setRGB(value.r, value.g, value.b);
2486
+ ppssaoEffect.color = new Color4().setRGB(value.r, value.g, value.b);
2362
2487
  break;
2363
2488
  }
2364
2489
  default: {
@@ -2398,7 +2523,7 @@ var SSAOFolder = class {
2398
2523
  break;
2399
2524
  case "color":
2400
2525
  const value = e.value;
2401
- n8aopass.configuration.color = new Color5().setRGB(value.r, value.g, value.b);
2526
+ n8aopass.configuration.color = new Color4().setRGB(value.r, value.g, value.b);
2402
2527
  break;
2403
2528
  default:
2404
2529
  break;
@@ -2491,53 +2616,6 @@ var ToneMappingFolder = class {
2491
2616
  }
2492
2617
  };
2493
2618
 
2494
- // src/tweakpane/TweakPane.ts
2495
- import * as EssentialsPlugin from "@tweakpane/plugin-essentials";
2496
- import { Pane } from "tweakpane";
2497
-
2498
- // src/tweakpane/blades/rendererStatsFolder.ts
2499
- var RendererStatsFolder = class {
2500
- constructor(parentFolder, expanded = true) {
2501
- this.statsData = {
2502
- triangles: "0",
2503
- geometries: "0",
2504
- textures: "0",
2505
- shaders: "0",
2506
- postPasses: "0",
2507
- drawCalls: "0",
2508
- rawDeltaTime: "0",
2509
- deltaTime: "0",
2510
- FPS: "0"
2511
- };
2512
- this.folder = parentFolder.addFolder({ title: "renderStats", expanded });
2513
- this.performance = this.folder.addFolder({ title: "performance", expanded: true });
2514
- this.defails = this.folder.addFolder({ title: "pipeline details", expanded: false });
2515
- this.folder.addBlade({ view: "separator" });
2516
- this.performance.addBinding(this.statsData, "FPS", { readonly: true });
2517
- this.performance.addBinding(this.statsData, "deltaTime", { readonly: true });
2518
- this.performance.addBinding(this.statsData, "rawDeltaTime", { readonly: true });
2519
- this.defails.addBinding(this.statsData, "triangles", { readonly: true });
2520
- this.defails.addBinding(this.statsData, "geometries", { readonly: true });
2521
- this.defails.addBinding(this.statsData, "textures", { readonly: true });
2522
- this.defails.addBinding(this.statsData, "shaders", { readonly: true });
2523
- this.defails.addBinding(this.statsData, "postPasses", { readonly: true });
2524
- this.defails.addBinding(this.statsData, "drawCalls", { readonly: true });
2525
- }
2526
- update(renderer, composer, timeManager) {
2527
- const { geometries, textures } = renderer.info.memory;
2528
- const { triangles, calls } = renderer.info.render;
2529
- this.statsData.triangles = triangles.toString();
2530
- this.statsData.geometries = geometries.toString();
2531
- this.statsData.textures = textures.toString();
2532
- this.statsData.shaders = renderer.info.programs.length.toString();
2533
- this.statsData.postPasses = composer.passes.length.toString();
2534
- this.statsData.drawCalls = calls.toString();
2535
- this.statsData.rawDeltaTime = (Math.round(timeManager.rawDeltaTime * 1e5) / 1e5).toString();
2536
- this.statsData.deltaTime = (Math.round(timeManager.deltaTime * 1e5) / 1e5).toString();
2537
- this.statsData.FPS = timeManager.fps.toString();
2538
- }
2539
- };
2540
-
2541
2619
  // src/tweakpane/tweakPaneStyle.ts
2542
2620
  var tweakPaneStyle = `
2543
2621
  :root {
@@ -2736,6 +2814,122 @@ var TweakPane = class {
2736
2814
  }
2737
2815
  };
2738
2816
 
2817
+ // src/rendering/composer.ts
2818
+ import { N8AOPostPass } from "n8ao";
2819
+ import {
2820
+ EffectComposer as EffectComposer2,
2821
+ RenderPass,
2822
+ EffectPass as EffectPass2,
2823
+ FXAAEffect,
2824
+ ShaderPass,
2825
+ BloomEffect,
2826
+ SSAOEffect as SSAOEffect2,
2827
+ BlendFunction as BlendFunction2,
2828
+ TextureEffect,
2829
+ ToneMappingEffect,
2830
+ SMAAEffect,
2831
+ SMAAPreset,
2832
+ EdgeDetectionMode,
2833
+ PredicationMode,
2834
+ NormalPass as NormalPass2
2835
+ } from "postprocessing";
2836
+ import {
2837
+ AmbientLight,
2838
+ Color as Color6,
2839
+ Fog,
2840
+ HalfFloatType,
2841
+ LinearSRGBColorSpace,
2842
+ LoadingManager as LoadingManager2,
2843
+ PMREMGenerator,
2844
+ Scene as Scene3,
2845
+ Vector2 as Vector22,
2846
+ WebGLRenderer as WebGLRenderer2
2847
+ } from "three";
2848
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
2849
+
2850
+ // src/sun/Sun.ts
2851
+ import { CameraHelper, Color as Color5, DirectionalLight, Group as Group3, OrthographicCamera, Vector3 as Vector38 } from "three";
2852
+ var Sun = class extends Group3 {
2853
+ constructor() {
2854
+ super();
2855
+ this.debug = false;
2856
+ this.sunOffset = new Vector38(
2857
+ 39 * (Math.PI / 180),
2858
+ 50 * (Math.PI / 180),
2859
+ 100
2860
+ );
2861
+ this.shadowResolution = 8192;
2862
+ this.shadowCamFrustum = 50;
2863
+ this.camHelper = null;
2864
+ this.target = null;
2865
+ this.shadowCamera = new OrthographicCamera(
2866
+ -this.shadowCamFrustum,
2867
+ this.shadowCamFrustum,
2868
+ this.shadowCamFrustum,
2869
+ -this.shadowCamFrustum,
2870
+ 0.1,
2871
+ 200
2872
+ );
2873
+ if (this.debug === true) {
2874
+ this.camHelper = new CameraHelper(this.shadowCamera);
2875
+ }
2876
+ this.directionalLight = new DirectionalLight(16777215, 0.5);
2877
+ this.directionalLight.shadow.normalBias = 0.05;
2878
+ this.directionalLight.shadow.radius = 1.5;
2879
+ this.directionalLight.shadow.camera = this.shadowCamera;
2880
+ this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
2881
+ this.directionalLight.castShadow = true;
2882
+ this.setColor();
2883
+ this.updateCharacterPosition(new Vector38(0, 0, 0));
2884
+ this.add(this.directionalLight);
2885
+ if (this.debug === true && this.camHelper instanceof CameraHelper) {
2886
+ this.add(this.camHelper);
2887
+ }
2888
+ }
2889
+ updateCharacterPosition(position) {
2890
+ if (!position)
2891
+ return;
2892
+ this.target = position;
2893
+ this.setSunPosition(this.sunOffset.x, this.sunOffset.y);
2894
+ }
2895
+ setAzimuthalAngle(angle) {
2896
+ if (this.sunOffset)
2897
+ this.sunOffset.x = angle;
2898
+ if (this.target)
2899
+ this.updateCharacterPosition(this.target);
2900
+ }
2901
+ setPolarAngle(angle) {
2902
+ if (this.sunOffset)
2903
+ this.sunOffset.y = angle;
2904
+ if (this.target)
2905
+ this.updateCharacterPosition(this.target);
2906
+ }
2907
+ setIntensity(intensity) {
2908
+ this.directionalLight.intensity = intensity;
2909
+ }
2910
+ setColor() {
2911
+ this.directionalLight.color = new Color5().setRGB(
2912
+ sunValues.sunColor.r,
2913
+ sunValues.sunColor.g,
2914
+ sunValues.sunColor.b
2915
+ );
2916
+ }
2917
+ setSunPosition(azimuthalAngle, polarAngle) {
2918
+ if (!this.target)
2919
+ return;
2920
+ const distance = this.sunOffset.z;
2921
+ const sphericalPosition = new Vector38(
2922
+ distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
2923
+ distance * Math.cos(polarAngle),
2924
+ distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
2925
+ );
2926
+ const newSunPosition = this.target.clone().add(sphericalPosition);
2927
+ this.directionalLight.position.set(newSunPosition.x, newSunPosition.y, newSunPosition.z);
2928
+ this.directionalLight.target.position.copy(this.target.clone());
2929
+ this.directionalLight.target.updateMatrixWorld();
2930
+ }
2931
+ };
2932
+
2739
2933
  // src/rendering/post-effects/bright-contrast-sat.ts
2740
2934
  import { ShaderMaterial, Uniform } from "three";
2741
2935
 
@@ -2883,8 +3077,8 @@ var GaussGrainEffect = new ShaderMaterial2({
2883
3077
  // src/rendering/composer.ts
2884
3078
  var Composer = class {
2885
3079
  constructor(scene, camera, spawnSun = false) {
2886
- this.width = window.innerWidth;
2887
- this.height = window.innerHeight;
3080
+ this.width = 1;
3081
+ this.height = 1;
2888
3082
  this.resolution = new Vector22(this.width, this.height);
2889
3083
  this.isEnvHDRI = false;
2890
3084
  this.bcs = BrightnessContrastSaturation;
@@ -2892,6 +3086,7 @@ var Composer = class {
2892
3086
  this.ambientLight = null;
2893
3087
  this.sun = null;
2894
3088
  this.scene = scene;
3089
+ this.postPostScene = new Scene3();
2895
3090
  this.camera = camera;
2896
3091
  this.spawnSun = spawnSun;
2897
3092
  this.renderer = new WebGLRenderer2({
@@ -2908,11 +3103,9 @@ var Composer = class {
2908
3103
  this.renderer.toneMappingExposure = rendererValues.exposure;
2909
3104
  this.setAmbientLight();
2910
3105
  this.setFog();
2911
- document.body.appendChild(this.renderer.domElement);
2912
- this.composer = new EffectComposer2(this.renderer, {
3106
+ this.effectComposer = new EffectComposer2(this.renderer, {
2913
3107
  frameBufferType: HalfFloatType
2914
3108
  });
2915
- this.tweakPane = new TweakPane(this.renderer, this.scene, this.composer);
2916
3109
  this.renderPass = new RenderPass(this.scene, this.camera);
2917
3110
  this.normalPass = new NormalPass2(this.scene, this.camera);
2918
3111
  this.normalPass.enabled = ppssaoValues.enabled;
@@ -2981,26 +3174,33 @@ var Composer = class {
2981
3174
  this.bcs.uniforms.saturation.value = bcsValues.saturation;
2982
3175
  this.gaussGrainPass = new ShaderPass(this.gaussGrainEffect, "tDiffuse");
2983
3176
  this.smaaPass = new EffectPass2(this.camera, this.smaaEffect);
2984
- this.composer.addPass(this.renderPass);
3177
+ this.effectComposer.addPass(this.renderPass);
2985
3178
  if (ppssaoValues.enabled) {
2986
- this.composer.addPass(this.normalPass);
2987
- this.composer.addPass(this.ppssaoPass);
3179
+ this.effectComposer.addPass(this.normalPass);
3180
+ this.effectComposer.addPass(this.ppssaoPass);
2988
3181
  }
2989
3182
  if (n8ssaoValues.enabled) {
2990
- this.composer.addPass(this.n8aopass);
2991
- }
2992
- this.composer.addPass(this.fxaaPass);
2993
- this.composer.addPass(this.smaaPass);
2994
- this.composer.addPass(this.bloomPass);
2995
- this.composer.addPass(this.toneMappingPass);
2996
- this.composer.addPass(this.bcsPass);
2997
- this.composer.addPass(this.gaussGrainPass);
3183
+ this.effectComposer.addPass(this.n8aopass);
3184
+ }
3185
+ this.effectComposer.addPass(this.fxaaPass);
3186
+ this.effectComposer.addPass(this.smaaPass);
3187
+ this.effectComposer.addPass(this.bloomPass);
3188
+ this.effectComposer.addPass(this.toneMappingPass);
3189
+ this.effectComposer.addPass(this.bcsPass);
3190
+ this.effectComposer.addPass(this.gaussGrainPass);
2998
3191
  if (this.spawnSun === true) {
2999
3192
  this.sun = new Sun();
3000
3193
  this.scene.add(this.sun);
3001
3194
  }
3002
- this.tweakPane.setupRenderPane(
3003
- this.composer,
3195
+ this.resizeListener = () => {
3196
+ this.fitContainer();
3197
+ };
3198
+ window.addEventListener("resize", this.resizeListener, false);
3199
+ this.fitContainer();
3200
+ }
3201
+ setupTweakPane(tweakPane) {
3202
+ tweakPane.setupRenderPane(
3203
+ this.effectComposer,
3004
3204
  this.normalPass,
3005
3205
  this.ppssaoEffect,
3006
3206
  this.ppssaoPass,
@@ -3016,14 +3216,32 @@ var Composer = class {
3016
3216
  this.setAmbientLight.bind(this),
3017
3217
  this.setFog.bind(this)
3018
3218
  );
3019
- window.addEventListener("resize", () => this.updateProjection());
3020
- this.updateProjection();
3021
3219
  }
3022
- updateProjection() {
3023
- this.width = window.innerWidth;
3024
- this.height = innerHeight;
3025
- this.resolution = new Vector22(this.width, this.height);
3026
- this.composer.setSize(this.width, this.height);
3220
+ dispose() {
3221
+ window.removeEventListener("resize", this.resizeListener);
3222
+ }
3223
+ fitContainer() {
3224
+ if (!this) {
3225
+ console.error("Composer not initialized");
3226
+ return;
3227
+ }
3228
+ const parentElement = this.renderer.domElement.parentNode;
3229
+ if (!parentElement) {
3230
+ return;
3231
+ }
3232
+ this.width = parentElement.clientWidth;
3233
+ this.height = parentElement.clientHeight;
3234
+ this.camera.aspect = this.width / this.height;
3235
+ this.camera.updateProjectionMatrix();
3236
+ this.renderer.setPixelRatio(window.devicePixelRatio);
3237
+ this.resolution.set(
3238
+ this.width * window.devicePixelRatio,
3239
+ this.height * window.devicePixelRatio
3240
+ );
3241
+ this.effectComposer.setSize(
3242
+ this.width / window.devicePixelRatio,
3243
+ this.height / window.devicePixelRatio
3244
+ );
3027
3245
  this.renderPass.setSize(this.width, this.height);
3028
3246
  if (ppssaoValues.enabled) {
3029
3247
  this.normalPass.setSize(this.width, this.height);
@@ -3040,19 +3258,14 @@ var Composer = class {
3040
3258
  this.gaussGrainPass.setSize(this.width, this.height);
3041
3259
  this.renderer.setSize(this.width, this.height);
3042
3260
  }
3043
- isTweakPaneVisible() {
3044
- return this.tweakPane.guiVisible;
3045
- }
3046
3261
  render(timeManager) {
3047
3262
  this.renderer.info.reset();
3048
3263
  this.normalPass.texture.needsUpdate = true;
3049
3264
  this.gaussGrainEffect.uniforms.resolution.value = this.resolution;
3050
3265
  this.gaussGrainEffect.uniforms.time.value = timeManager.time;
3051
3266
  this.gaussGrainEffect.uniforms.alpha.value = 1;
3052
- this.composer.render();
3053
- if (this.tweakPane.guiVisible) {
3054
- this.tweakPane.updateStats(timeManager);
3055
- }
3267
+ this.effectComposer.render();
3268
+ this.renderer.render(this.postPostScene, this.camera);
3056
3269
  }
3057
3270
  useHDRI(url, fromFile = false) {
3058
3271
  if (this.isEnvHDRI && fromFile === false || !this.renderer)
@@ -3181,17 +3394,17 @@ import {
3181
3394
  MMLCollisionTrigger
3182
3395
  } from "mml-web";
3183
3396
  import {
3184
- Box3 as Box32,
3397
+ Box3,
3185
3398
  Color as Color7,
3186
3399
  DoubleSide,
3187
- Euler,
3400
+ Euler as Euler2,
3188
3401
  Group as Group4,
3189
3402
  Line3 as Line32,
3190
3403
  Matrix4 as Matrix42,
3191
- Mesh as Mesh3,
3404
+ Mesh as Mesh4,
3192
3405
  MeshBasicMaterial as MeshBasicMaterial3,
3193
3406
  Ray,
3194
- Vector3 as Vector38
3407
+ Vector3 as Vector39
3195
3408
  } from "three";
3196
3409
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
3197
3410
  import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
@@ -3199,14 +3412,14 @@ import { MeshBVH, MeshBVHVisualizer } from "three-mesh-bvh";
3199
3412
  var CollisionsManager = class {
3200
3413
  constructor(scene) {
3201
3414
  this.debug = false;
3202
- this.tempVector = new Vector38();
3203
- this.tempVector2 = new Vector38();
3204
- this.tempVector3 = new Vector38();
3415
+ this.tempVector = new Vector39();
3416
+ this.tempVector2 = new Vector39();
3417
+ this.tempVector3 = new Vector39();
3205
3418
  this.tempRay = new Ray();
3206
3419
  this.tempMatrix = new Matrix42();
3207
3420
  this.tempMatrix2 = new Matrix42();
3208
- this.tempBox = new Box32();
3209
- this.tempEuler = new Euler();
3421
+ this.tempBox = new Box3();
3422
+ this.tempEuler = new Euler2();
3210
3423
  this.tempSegment = new Line32();
3211
3424
  this.tempSegment2 = new Line32();
3212
3425
  this.collisionMeshState = /* @__PURE__ */ new Map();
@@ -3267,7 +3480,7 @@ var CollisionsManager = class {
3267
3480
  };
3268
3481
  if (this.debug) {
3269
3482
  newBufferGeometry.boundsTree = meshBVH;
3270
- const wireframeMesh = new Mesh3(newBufferGeometry, new MeshBasicMaterial3({ wireframe: true }));
3483
+ const wireframeMesh = new Mesh4(newBufferGeometry, new MeshBasicMaterial3({ wireframe: true }));
3271
3484
  const normalsHelper = new VertexNormalsHelper(wireframeMesh, 0.25, 65280);
3272
3485
  const visualizer = new MeshBVHVisualizer(wireframeMesh, 4);
3273
3486
  visualizer.edgeMaterial.color = new Color7("blue");
@@ -3350,7 +3563,7 @@ var CollisionsManager = class {
3350
3563
  const realDistance = intersectionSegment.distance();
3351
3564
  if (realDistance < capsuleRadius) {
3352
3565
  if (!collisionPosition) {
3353
- collisionPosition = new Vector38().copy(closestPointOnSegment);
3566
+ collisionPosition = new Vector39().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
3354
3567
  }
3355
3568
  const ratio = realDistance / modelReferenceDistance;
3356
3569
  const realDepth = capsuleRadius - realDistance;
@@ -3399,11 +3612,13 @@ export {
3399
3612
  AnimationState,
3400
3613
  CameraManager,
3401
3614
  CharacterManager,
3615
+ CharacterModelLoader,
3402
3616
  CollisionsManager,
3403
3617
  Composer,
3404
3618
  KeyInputManager,
3405
3619
  MMLCompositionScene,
3406
3620
  Sun,
3407
- TimeManager
3621
+ TimeManager,
3622
+ TweakPane
3408
3623
  };
3409
3624
  //# sourceMappingURL=index.js.map