@mml-io/3d-web-client-core 0.0.0-experimental-ec6e197-20231110 → 0.0.0-experimental-f92224e-20231121

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,6 +34,9 @@ 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
+ }
37
40
  var remap = (value, minValue, maxValue, minScaledValue, maxScaledValue) => {
38
41
  return minScaledValue + (maxScaledValue - minScaledValue) * (value - minValue) / (maxValue - minValue);
39
42
  };
@@ -106,6 +109,8 @@ var CameraManager = class {
106
109
  this.targetDistance = this.initialDistance;
107
110
  this.distance = this.initialDistance;
108
111
  this.desiredDistance = this.initialDistance;
112
+ this.phi = Math.PI / 2;
113
+ this.theta = Math.PI / 2;
109
114
  this.dragging = false;
110
115
  this.target = new Vector32(0, 1.55, 0);
111
116
  this.hadTarget = false;
@@ -174,17 +179,24 @@ var CameraManager = class {
174
179
  this.setTarget(target);
175
180
  }
176
181
  reverseUpdateFromPositions() {
177
- if (this.phi === null || this.theta == null)
178
- return;
179
182
  const dx = this.camera.position.x - this.target.x;
180
183
  const dy = this.camera.position.y - this.target.y;
181
184
  const dz = this.camera.position.z - this.target.z;
182
185
  this.targetDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
183
- this.targetTheta = (this.theta + 2 * Math.PI) % (2 * Math.PI);
184
- this.targetPhi = Math.max(0, Math.min(Math.PI, this.phi));
186
+ this.targetTheta = Math.atan2(dz, dx);
187
+ this.targetPhi = Math.acos(dy / this.targetDistance);
185
188
  this.phi = this.targetPhi;
186
189
  this.theta = this.targetTheta;
187
190
  this.distance = this.targetDistance;
191
+ this.desiredDistance = this.targetDistance;
192
+ this.targetFOV = remap(
193
+ this.targetDistance,
194
+ this.minDistance,
195
+ this.maxDistance,
196
+ this.minFOV,
197
+ this.maxFOV
198
+ );
199
+ this.fov = this.targetFOV;
188
200
  }
189
201
  adjustCameraPosition() {
190
202
  this.rayCaster.set(
@@ -244,14 +256,13 @@ var CameraManager = class {
244
256
  };
245
257
 
246
258
  // src/character/Character.ts
247
- import { Color as Color3, Vector3 as Vector35 } from "three";
259
+ import { Color as Color3, Group, Vector3 as Vector34 } from "three";
248
260
 
249
261
  // src/character/CharacterModel.ts
250
262
  import {
251
263
  AnimationClip,
252
264
  AnimationMixer,
253
265
  LoopRepeat,
254
- MeshStandardMaterial,
255
266
  Object3D
256
267
  } from "three";
257
268
 
@@ -659,36 +670,10 @@ var CharacterModel = class {
659
670
  );
660
671
  this.applyMaterialToAllSkinnedMeshes(this.material);
661
672
  }
662
- updateAnimation(targetAnimation, deltaTime) {
663
- var _a;
673
+ updateAnimation(targetAnimation) {
664
674
  if (this.currentAnimation !== targetAnimation) {
665
675
  this.transitionToAnimation(targetAnimation);
666
676
  }
667
- (_a = this.animationMixer) == null ? void 0 : _a.update(deltaTime);
668
- }
669
- hideMaterialByMeshName(meshName) {
670
- if (!this.mesh)
671
- return;
672
- this.mesh.traverse((child) => {
673
- if (child.type === "Bone" && child.name === "mixamorigHeadTop_End") {
674
- this.headBone = child;
675
- }
676
- if (child.type === "SkinnedMesh" && child.name === meshName) {
677
- child.material = new MeshStandardMaterial({
678
- color: 16711680,
679
- transparent: true,
680
- opacity: 0
681
- });
682
- }
683
- });
684
- }
685
- setShadows(mesh, castShadow = true, receiveShadow = true) {
686
- mesh.traverse((child) => {
687
- if (child.type === "SkinnedMesh") {
688
- child.castShadow = castShadow;
689
- child.receiveShadow = receiveShadow;
690
- }
691
- });
692
677
  }
693
678
  applyMaterialToAllSkinnedMeshes(material) {
694
679
  if (!this.mesh)
@@ -699,11 +684,6 @@ var CharacterModel = class {
699
684
  }
700
685
  });
701
686
  }
702
- initAnimationMixer() {
703
- if (this.animationMixer !== null || this.mesh === null)
704
- return;
705
- this.animationMixer = new AnimationMixer(this.mesh);
706
- }
707
687
  async loadMainMesh() {
708
688
  const mainMeshUrl = this.characterDescription.meshFileUrl;
709
689
  const scale = this.characterDescription.modelScale;
@@ -717,12 +697,17 @@ var CharacterModel = class {
717
697
  this.mesh.add(model);
718
698
  this.mesh.name = name;
719
699
  this.mesh.scale.set(scale, scale, scale);
720
- this.setShadows(this.mesh);
700
+ this.mesh.traverse((child) => {
701
+ if (child.type === "SkinnedMesh") {
702
+ child.castShadow = true;
703
+ child.receiveShadow = true;
704
+ }
705
+ });
706
+ this.animationMixer = new AnimationMixer(this.mesh);
721
707
  }
722
708
  }
723
709
  async setAnimationFromFile(animationFileUrl, animationType) {
724
710
  return new Promise(async (resolve, reject) => {
725
- this.initAnimationMixer();
726
711
  const animation = await this.characterModelLoader.load(animationFileUrl, "animation");
727
712
  if (typeof animation !== "undefined" && animation instanceof AnimationClip) {
728
713
  this.animations[animationType] = this.animationMixer.clipAction(animation);
@@ -737,9 +722,10 @@ var CharacterModel = class {
737
722
  });
738
723
  }
739
724
  transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
740
- if (!this.mesh || this.currentAnimation === null)
725
+ if (!this.mesh)
741
726
  return;
742
727
  const currentAction = this.animations[this.currentAnimation];
728
+ this.currentAnimation = targetAnimation;
743
729
  const targetAction = this.animations[targetAnimation];
744
730
  if (!targetAction)
745
731
  return;
@@ -752,7 +738,11 @@ var CharacterModel = class {
752
738
  targetAction.setLoop(LoopRepeat, Infinity);
753
739
  targetAction.enabled = true;
754
740
  targetAction.fadeIn(transitionDuration);
755
- this.currentAnimation = targetAnimation;
741
+ }
742
+ update(time) {
743
+ if (this.animationMixer) {
744
+ this.animationMixer.update(time);
745
+ }
756
746
  }
757
747
  };
758
748
 
@@ -978,8 +968,10 @@ var defaultLabelPadding = 0;
978
968
  var defaultLabelWidth = 0.25;
979
969
  var defaultLabelHeight = 0.125;
980
970
  var defaultLabelCastShadows = true;
981
- var CharacterTooltip = class {
982
- constructor(parentModel) {
971
+ var tooltipGeometry = new PlaneGeometry(1, 1, 1, 1);
972
+ var CharacterTooltip = class extends Mesh3 {
973
+ constructor() {
974
+ super(tooltipGeometry);
983
975
  this.visibleOpacity = 0.85;
984
976
  this.targetOpacity = 0;
985
977
  this.fadingSpeed = 0.02;
@@ -995,25 +987,22 @@ var CharacterTooltip = class {
995
987
  fontColor: defaultFontColor,
996
988
  castShadows: defaultLabelCastShadows
997
989
  };
998
- this.setText = this.setText.bind(this);
999
- this.material = new MeshBasicMaterial2({
990
+ this.tooltipMaterial = new MeshBasicMaterial2({
1000
991
  map: null,
1001
992
  transparent: true,
1002
- opacity: 0
993
+ opacity: 0,
994
+ side: FrontSide
1003
995
  });
1004
- this.material.side = FrontSide;
1005
- this.geometry = new PlaneGeometry(1, 1, 1, 1);
1006
- this.mesh = new Mesh3(this.geometry, this.material);
1007
- this.mesh.position.set(0, 1.6, 0);
1008
- this.mesh.visible = false;
1009
- parentModel.add(this.mesh);
996
+ this.material = this.tooltipMaterial;
997
+ this.position.set(0, 1.6, 0);
998
+ this.visible = false;
1010
999
  }
1011
1000
  redrawText(content) {
1012
- if (!this.material) {
1001
+ if (!this.tooltipMaterial) {
1013
1002
  return;
1014
1003
  }
1015
- if (this.material.map) {
1016
- this.material.map.dispose();
1004
+ if (this.tooltipMaterial.map) {
1005
+ this.tooltipMaterial.map.dispose();
1017
1006
  }
1018
1007
  const { texture, width, height } = THREECanvasTextTexture(content, {
1019
1008
  bold: true,
@@ -1037,17 +1026,17 @@ var CharacterTooltip = class {
1037
1026
  },
1038
1027
  alignment: this.props.alignment
1039
1028
  });
1040
- this.material.map = texture;
1041
- this.material.map.magFilter = LinearFilter2;
1042
- this.material.map.minFilter = LinearFilter2;
1043
- this.material.needsUpdate = true;
1044
- this.mesh.scale.x = width / (100 * fontScale);
1045
- this.mesh.scale.y = height / (100 * fontScale);
1046
- this.mesh.position.y = 1.6;
1029
+ this.tooltipMaterial.map = texture;
1030
+ this.tooltipMaterial.map.magFilter = LinearFilter2;
1031
+ this.tooltipMaterial.map.minFilter = LinearFilter2;
1032
+ this.tooltipMaterial.needsUpdate = true;
1033
+ this.scale.x = width / (100 * fontScale);
1034
+ this.scale.y = height / (100 * fontScale);
1035
+ this.position.y = 1.6;
1047
1036
  }
1048
1037
  setText(text, temporary = false) {
1049
1038
  this.redrawText(text);
1050
- this.mesh.visible = true;
1039
+ this.visible = true;
1051
1040
  this.targetOpacity = this.visibleOpacity;
1052
1041
  if (temporary) {
1053
1042
  setTimeout(() => {
@@ -1059,37 +1048,101 @@ var CharacterTooltip = class {
1059
1048
  this.targetOpacity = 0;
1060
1049
  }
1061
1050
  update(camera) {
1062
- this.mesh.lookAt(camera.position);
1063
- const opacity = this.mesh.material.opacity;
1051
+ this.lookAt(camera.position);
1052
+ const opacity = this.tooltipMaterial.opacity;
1064
1053
  if (opacity < this.targetOpacity) {
1065
- this.mesh.material.opacity = Math.min(
1066
- this.mesh.material.opacity + this.fadingSpeed,
1054
+ this.tooltipMaterial.opacity = Math.min(
1055
+ this.tooltipMaterial.opacity + this.fadingSpeed,
1067
1056
  this.targetOpacity
1068
1057
  );
1069
1058
  } else if (opacity > this.targetOpacity) {
1070
- this.mesh.material.opacity = Math.max(
1071
- this.mesh.material.opacity - this.fadingSpeed,
1059
+ this.tooltipMaterial.opacity = Math.max(
1060
+ this.tooltipMaterial.opacity - this.fadingSpeed,
1072
1061
  this.targetOpacity
1073
1062
  );
1074
- if (opacity >= 1 && this.mesh.material.transparent === true) {
1075
- this.mesh.material.transparent = false;
1076
- this.mesh.material.needsUpdate = true;
1077
- } else if (opacity > 0 && opacity < 1 && this.mesh.material.transparent === false) {
1078
- this.mesh.material.transparent = true;
1079
- this.mesh.material.needsUpdate = true;
1063
+ if (opacity >= 1 && this.tooltipMaterial.transparent) {
1064
+ this.tooltipMaterial.transparent = false;
1065
+ this.tooltipMaterial.needsUpdate = true;
1066
+ } else if (opacity > 0 && opacity < 1 && !this.tooltipMaterial.transparent) {
1067
+ this.tooltipMaterial.transparent = true;
1068
+ this.tooltipMaterial.needsUpdate = true;
1080
1069
  }
1081
- if (this.mesh.material.opacity <= 0) {
1082
- this.mesh.visible = false;
1070
+ if (this.tooltipMaterial.opacity <= 0) {
1071
+ this.visible = false;
1083
1072
  }
1084
1073
  }
1085
1074
  }
1086
1075
  };
1087
1076
 
1077
+ // src/character/Character.ts
1078
+ var Character = class extends Group {
1079
+ constructor(characterDescription, characterModelLoader, characterId, modelLoadedCallback, cameraManager, composer) {
1080
+ super();
1081
+ this.characterDescription = characterDescription;
1082
+ this.characterModelLoader = characterModelLoader;
1083
+ this.characterId = characterId;
1084
+ this.modelLoadedCallback = modelLoadedCallback;
1085
+ this.cameraManager = cameraManager;
1086
+ this.composer = composer;
1087
+ this.model = null;
1088
+ this.color = new Color3();
1089
+ this.tooltip = null;
1090
+ this.speakingIndicator = null;
1091
+ this.tooltip = new CharacterTooltip();
1092
+ this.add(this.tooltip);
1093
+ this.load();
1094
+ }
1095
+ async load() {
1096
+ this.model = new CharacterModel(this.characterDescription, this.characterModelLoader);
1097
+ await this.model.init();
1098
+ this.add(this.model.mesh);
1099
+ if (this.speakingIndicator === null) {
1100
+ this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1101
+ }
1102
+ this.color = this.model.material.colorsCube216[this.characterId];
1103
+ this.modelLoadedCallback();
1104
+ }
1105
+ updateAnimation(targetAnimation) {
1106
+ var _a;
1107
+ (_a = this.model) == null ? void 0 : _a.updateAnimation(targetAnimation);
1108
+ }
1109
+ update(time, deltaTime) {
1110
+ var _a;
1111
+ if (!this.model)
1112
+ return;
1113
+ if (this.tooltip) {
1114
+ this.tooltip.update(this.cameraManager.camera);
1115
+ }
1116
+ if (this.speakingIndicator) {
1117
+ this.speakingIndicator.setTime(time);
1118
+ if (this.model.mesh && this.model.headBone) {
1119
+ this.speakingIndicator.setBillboarding(
1120
+ (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector34()),
1121
+ this.cameraManager.camera
1122
+ );
1123
+ }
1124
+ }
1125
+ if (typeof this.model.material.uniforms.time !== "undefined") {
1126
+ this.model.material.uniforms.time.value = time;
1127
+ this.model.material.uniforms.diffuseRandomColor.value = this.color;
1128
+ this.model.material.update();
1129
+ }
1130
+ this.model.update(deltaTime);
1131
+ }
1132
+ getCurrentAnimation() {
1133
+ var _a;
1134
+ return ((_a = this.model) == null ? void 0 : _a.currentAnimation) || 0 /* idle */;
1135
+ }
1136
+ };
1137
+
1138
+ // src/character/CharacterManager.ts
1139
+ import { Euler, Group as Group2, Quaternion as Quaternion5, Vector3 as Vector38 } from "three";
1140
+
1088
1141
  // src/character/LocalController.ts
1089
- import { Line3, Matrix4, Quaternion as Quaternion2, Raycaster as Raycaster2, Vector3 as Vector34 } from "three";
1142
+ import { Line3, Matrix4, Quaternion as Quaternion2, Raycaster as Raycaster2, Vector3 as Vector35 } from "three";
1090
1143
  var LocalController = class {
1091
- constructor(model, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1092
- this.model = model;
1144
+ constructor(character, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1145
+ this.character = character;
1093
1146
  this.id = id;
1094
1147
  this.collisionsManager = collisionsManager;
1095
1148
  this.keyInputManager = keyInputManager;
@@ -1098,7 +1151,7 @@ var LocalController = class {
1098
1151
  this.collisionDetectionSteps = 15;
1099
1152
  this.capsuleInfo = {
1100
1153
  radius: 0.4,
1101
- segment: new Line3(new Vector34(), new Vector34(0, 1.05, 0))
1154
+ segment: new Line3(new Vector35(), new Vector35(0, 1.05, 0))
1102
1155
  };
1103
1156
  this.maxWalkSpeed = 6;
1104
1157
  this.maxRunSpeed = 8.5;
@@ -1111,17 +1164,16 @@ var LocalController = class {
1111
1164
  this.characterWasOnGround = false;
1112
1165
  this.characterAirborneSince = 0;
1113
1166
  this.currentHeight = 0;
1114
- this.characterVelocity = new Vector34();
1115
- this.vectorUp = new Vector34(0, 1, 0);
1116
- this.vectorDown = new Vector34(0, -1, 0);
1167
+ this.characterVelocity = new Vector35();
1168
+ this.vectorUp = new Vector35(0, 1, 0);
1169
+ this.vectorDown = new Vector35(0, -1, 0);
1117
1170
  this.rotationOffset = 0;
1118
1171
  this.azimuthalAngle = 0;
1119
1172
  this.tempMatrix = new Matrix4();
1120
1173
  this.tempSegment = new Line3();
1121
- this.tempVector = new Vector34();
1122
- this.tempVector2 = new Vector34();
1174
+ this.tempVector = new Vector35();
1175
+ this.tempVector2 = new Vector35();
1123
1176
  this.rayCaster = new Raycaster2();
1124
- this.thirdPersonCamera = null;
1125
1177
  this.speed = 0;
1126
1178
  this.targetSpeed = 0;
1127
1179
  this.networkState = {
@@ -1132,11 +1184,6 @@ var LocalController = class {
1132
1184
  };
1133
1185
  }
1134
1186
  update() {
1135
- var _a, _b;
1136
- if (!((_a = this.model) == null ? void 0 : _a.mesh) || !((_b = this.model) == null ? void 0 : _b.animationMixer))
1137
- return;
1138
- if (!this.thirdPersonCamera)
1139
- this.thirdPersonCamera = this.cameraManager.camera;
1140
1187
  const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
1141
1188
  this.forward = forward;
1142
1189
  this.backward = backward;
@@ -1148,28 +1195,28 @@ var LocalController = class {
1148
1195
  this.conflictingDirections = conflictingDirection;
1149
1196
  this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
1150
1197
  this.speed += ease(this.targetSpeed, this.speed, 0.07);
1151
- this.rayCaster.set(this.model.mesh.position, this.vectorDown);
1198
+ this.rayCaster.set(this.character.position, this.vectorDown);
1152
1199
  const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
1153
1200
  if (minimumDistance !== null) {
1154
1201
  this.currentHeight = minimumDistance;
1155
1202
  }
1156
1203
  if (anyDirection || !this.characterOnGround) {
1157
1204
  const targetAnimation = this.getTargetAnimation();
1158
- this.model.updateAnimation(targetAnimation, this.timeManager.deltaTime);
1205
+ this.character.updateAnimation(targetAnimation);
1159
1206
  } else {
1160
- this.model.updateAnimation(0 /* idle */, this.timeManager.deltaTime);
1207
+ this.character.updateAnimation(0 /* idle */);
1161
1208
  }
1162
1209
  if (this.anyDirection)
1163
1210
  this.updateRotation();
1164
1211
  for (let i = 0; i < this.collisionDetectionSteps; i++) {
1165
1212
  this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
1166
1213
  }
1167
- if (this.model.mesh.position.y < 0)
1214
+ if (this.character.position.y < 0)
1168
1215
  this.resetPosition();
1169
1216
  this.updateNetworkState();
1170
1217
  }
1171
1218
  getTargetAnimation() {
1172
- if (!this.model.mesh)
1219
+ if (!this.character)
1173
1220
  return 0 /* idle */;
1174
1221
  if (this.conflictingDirections)
1175
1222
  return 0 /* idle */;
@@ -1201,31 +1248,26 @@ var LocalController = class {
1201
1248
  }
1202
1249
  }
1203
1250
  updateAzimuthalAngle() {
1204
- var _a;
1205
- if (!this.thirdPersonCamera || !((_a = this.model) == null ? void 0 : _a.mesh))
1206
- return;
1207
- const camToModelDistance = this.thirdPersonCamera.position.distanceTo(this.model.mesh.position);
1251
+ const camToModelDistance = this.cameraManager.camera.position.distanceTo(
1252
+ this.character.position
1253
+ );
1208
1254
  const isCameraFirstPerson = camToModelDistance < 2;
1209
1255
  if (isCameraFirstPerson) {
1210
- const cameraForward = new Vector34(0, 0, 1).applyQuaternion(this.thirdPersonCamera.quaternion);
1256
+ const cameraForward = new Vector35(0, 0, 1).applyQuaternion(
1257
+ this.cameraManager.camera.quaternion
1258
+ );
1211
1259
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1212
1260
  } else {
1213
1261
  this.azimuthalAngle = Math.atan2(
1214
- this.thirdPersonCamera.position.x - this.model.mesh.position.x,
1215
- this.thirdPersonCamera.position.z - this.model.mesh.position.z
1262
+ this.cameraManager.camera.position.x - this.character.position.x,
1263
+ this.cameraManager.camera.position.z - this.character.position.z
1216
1264
  );
1217
1265
  }
1218
1266
  }
1219
1267
  computeAngularDifference(rotationQuaternion) {
1220
- var _a;
1221
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1222
- return 0;
1223
- return 2 * Math.acos(Math.abs(this.model.mesh.quaternion.dot(rotationQuaternion)));
1268
+ return 2 * Math.acos(Math.abs(this.character.quaternion.dot(rotationQuaternion)));
1224
1269
  }
1225
1270
  updateRotation() {
1226
- var _a;
1227
- if (!this.thirdPersonCamera || !((_a = this.model) == null ? void 0 : _a.mesh))
1228
- return;
1229
1271
  this.updateRotationOffset();
1230
1272
  this.updateAzimuthalAngle();
1231
1273
  const rotationQuaternion = new Quaternion2();
@@ -1234,18 +1276,12 @@ var LocalController = class {
1234
1276
  const desiredTime = 0.07;
1235
1277
  const angularSpeed = angularDifference / desiredTime;
1236
1278
  const frameRotation = angularSpeed * this.timeManager.deltaTime;
1237
- this.model.mesh.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1279
+ this.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1238
1280
  }
1239
1281
  addScaledVectorToCharacter(deltaTime) {
1240
- var _a;
1241
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1242
- return;
1243
- this.model.mesh.position.addScaledVector(this.tempVector, this.speed * deltaTime);
1282
+ this.character.position.addScaledVector(this.tempVector, this.speed * deltaTime);
1244
1283
  }
1245
1284
  updatePosition(deltaTime, _iter) {
1246
- var _a;
1247
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1248
- return;
1249
1285
  if (this.characterOnGround) {
1250
1286
  if (!this.jump)
1251
1287
  this.canJump = true;
@@ -1262,45 +1298,45 @@ var LocalController = class {
1262
1298
  this.characterVelocity.y += deltaTime * this.gravity;
1263
1299
  this.canJump = false;
1264
1300
  }
1265
- this.model.mesh.position.addScaledVector(this.characterVelocity, deltaTime);
1301
+ this.character.position.addScaledVector(this.characterVelocity, deltaTime);
1266
1302
  this.tempVector.set(0, 0, 0);
1267
1303
  if (this.forward) {
1268
- const forward = new Vector34(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1304
+ const forward = new Vector35(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1269
1305
  this.tempVector.add(forward);
1270
1306
  }
1271
1307
  if (this.backward) {
1272
- const backward = new Vector34(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1308
+ const backward = new Vector35(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1273
1309
  this.tempVector.add(backward);
1274
1310
  }
1275
1311
  if (this.left) {
1276
- const left = new Vector34(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1312
+ const left = new Vector35(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1277
1313
  this.tempVector.add(left);
1278
1314
  }
1279
1315
  if (this.right) {
1280
- const right = new Vector34(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1316
+ const right = new Vector35(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1281
1317
  this.tempVector.add(right);
1282
1318
  }
1283
1319
  if (this.tempVector.length() > 0) {
1284
1320
  this.tempVector.normalize();
1285
1321
  this.addScaledVectorToCharacter(deltaTime);
1286
1322
  }
1287
- this.model.mesh.updateMatrixWorld();
1323
+ this.character.updateMatrixWorld();
1288
1324
  this.tempSegment.copy(this.capsuleInfo.segment);
1289
- this.tempSegment.start.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
1290
- this.tempSegment.end.applyMatrix4(this.model.mesh.matrixWorld).applyMatrix4(this.tempMatrix);
1325
+ this.tempSegment.start.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1326
+ this.tempSegment.end.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1291
1327
  this.collisionsManager.applyColliders(this.tempSegment, this.capsuleInfo.radius);
1292
1328
  const newPosition = this.tempVector;
1293
1329
  newPosition.copy(this.tempSegment.start);
1294
1330
  const deltaVector = this.tempVector2;
1295
- deltaVector.subVectors(newPosition, this.model.mesh.position);
1331
+ deltaVector.subVectors(newPosition, this.character.position);
1296
1332
  const offset = Math.max(0, deltaVector.length() - 1e-5);
1297
1333
  deltaVector.normalize().multiplyScalar(offset);
1298
- this.model.mesh.position.add(deltaVector);
1334
+ this.character.position.add(deltaVector);
1299
1335
  this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
1300
1336
  if (this.characterWasOnGround && !this.characterOnGround) {
1301
1337
  this.characterAirborneSince = Date.now();
1302
1338
  }
1303
- this.coyoteTime = this.characterVelocity.y < 0 && this.characterOnGround === false && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
1339
+ this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
1304
1340
  this.characterWasOnGround = this.characterOnGround;
1305
1341
  if (this.characterOnGround) {
1306
1342
  this.characterVelocity.set(0, 0, 0);
@@ -1310,130 +1346,33 @@ var LocalController = class {
1310
1346
  }
1311
1347
  }
1312
1348
  updateNetworkState() {
1313
- var _a;
1314
- if (!((_a = this.model) == null ? void 0 : _a.mesh)) {
1315
- this.networkState = {
1316
- id: this.id,
1317
- position: new Vector34(),
1318
- rotation: { quaternionY: 0, quaternionW: 1 },
1319
- state: 0 /* idle */
1320
- };
1321
- } else {
1322
- const characterQuaternion = this.model.mesh.getWorldQuaternion(new Quaternion2());
1323
- const positionUpdate = new Vector34(
1324
- this.model.mesh.position.x,
1325
- this.model.mesh.position.y,
1326
- this.model.mesh.position.z
1327
- );
1328
- this.networkState = {
1329
- id: this.id,
1330
- position: positionUpdate,
1331
- rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1332
- state: this.model.currentAnimation
1333
- };
1334
- }
1349
+ const characterQuaternion = this.character.getWorldQuaternion(new Quaternion2());
1350
+ const positionUpdate = new Vector35(
1351
+ this.character.position.x,
1352
+ this.character.position.y,
1353
+ this.character.position.z
1354
+ );
1355
+ this.networkState = {
1356
+ id: this.id,
1357
+ position: positionUpdate,
1358
+ rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1359
+ state: this.character.getCurrentAnimation()
1360
+ };
1335
1361
  }
1336
1362
  resetPosition() {
1337
- var _a;
1338
- if (!((_a = this.model) == null ? void 0 : _a.mesh))
1339
- return;
1340
1363
  this.characterVelocity.y = 0;
1341
- this.model.mesh.position.y = 3;
1364
+ this.character.position.y = 3;
1342
1365
  this.characterOnGround = false;
1343
1366
  }
1344
1367
  };
1345
1368
 
1346
- // src/character/Character.ts
1347
- var Character = class {
1348
- constructor(characterDescription, characterModelLoader, id, isLocal, modelLoadedCallback, collisionsManager, keyInputManager, cameraManager, timeManager, composer) {
1349
- this.characterDescription = characterDescription;
1350
- this.characterModelLoader = characterModelLoader;
1351
- this.id = id;
1352
- this.isLocal = isLocal;
1353
- this.modelLoadedCallback = modelLoadedCallback;
1354
- this.collisionsManager = collisionsManager;
1355
- this.keyInputManager = keyInputManager;
1356
- this.cameraManager = cameraManager;
1357
- this.timeManager = timeManager;
1358
- this.composer = composer;
1359
- this.controller = null;
1360
- this.name = null;
1361
- this.model = null;
1362
- this.color = new Color3();
1363
- this.position = new Vector35();
1364
- this.tooltip = null;
1365
- this.speakingIndicator = null;
1366
- this.load();
1367
- }
1368
- async load() {
1369
- this.model = new CharacterModel(this.characterDescription, this.characterModelLoader);
1370
- await this.model.init();
1371
- if (this.tooltip === null) {
1372
- this.tooltip = new CharacterTooltip(this.model.mesh);
1373
- }
1374
- if (this.speakingIndicator === null) {
1375
- this.speakingIndicator = new CharacterSpeakingIndicator(this.composer.postPostScene);
1376
- }
1377
- this.color = this.model.material.colorsCube216[this.id];
1378
- if (this.isLocal) {
1379
- this.controller = new LocalController(
1380
- this.model,
1381
- this.id,
1382
- this.collisionsManager,
1383
- this.keyInputManager,
1384
- this.cameraManager,
1385
- this.timeManager
1386
- );
1387
- }
1388
- this.modelLoadedCallback();
1389
- }
1390
- update(time) {
1391
- var _a;
1392
- if (!this.model)
1393
- return;
1394
- if (this.tooltip) {
1395
- this.tooltip.update(this.cameraManager.camera);
1396
- }
1397
- if (this.speakingIndicator) {
1398
- this.speakingIndicator.setTime(time);
1399
- if (this.model.mesh && this.model.headBone) {
1400
- this.speakingIndicator.setBillboarding(
1401
- (_a = this.model.headBone) == null ? void 0 : _a.getWorldPosition(new Vector35()),
1402
- this.cameraManager.camera
1403
- );
1404
- }
1405
- }
1406
- this.model.mesh.getWorldPosition(this.position);
1407
- if (typeof this.model.material.uniforms.time !== "undefined") {
1408
- this.model.material.uniforms.time.value = time;
1409
- this.model.material.uniforms.diffuseRandomColor.value = this.color;
1410
- this.model.material.update();
1411
- }
1412
- }
1413
- };
1414
-
1415
- // src/character/CharacterManager.ts
1416
- import { Euler, Group, Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1417
-
1418
1369
  // src/character/RemoteController.ts
1419
- import {
1420
- AnimationMixer as AnimationMixer2,
1421
- Object3D as Object3D4,
1422
- Quaternion as Quaternion3,
1423
- Vector3 as Vector36
1424
- } from "three";
1370
+ import { Quaternion as Quaternion3, Vector3 as Vector36 } from "three";
1425
1371
  var RemoteController = class {
1426
- constructor(character, characterModelLoader, id) {
1372
+ constructor(character, id) {
1427
1373
  this.character = character;
1428
- this.characterModelLoader = characterModelLoader;
1429
1374
  this.id = id;
1430
- this.characterModel = null;
1431
- this.animationMixer = new AnimationMixer2(new Object3D4());
1432
- this.animations = /* @__PURE__ */ new Map();
1433
1375
  this.currentAnimation = 0 /* idle */;
1434
- this.characterModel = this.character.model.mesh;
1435
- this.characterModel.updateMatrixWorld();
1436
- this.animationMixer = new AnimationMixer2(this.characterModel);
1437
1376
  this.networkState = {
1438
1377
  id: this.id,
1439
1378
  position: { x: 0, y: 0, z: 0 },
@@ -1444,48 +1383,23 @@ var RemoteController = class {
1444
1383
  update(clientUpdate, time, deltaTime) {
1445
1384
  if (!this.character)
1446
1385
  return;
1447
- this.character.update(time);
1448
1386
  this.updateFromNetwork(clientUpdate);
1449
- this.animationMixer.update(deltaTime);
1450
- }
1451
- async setAnimationFromFile(animationType, fileName) {
1452
- const animation = await this.characterModelLoader.load(fileName, "animation");
1453
- const animationAction = this.animationMixer.clipAction(animation);
1454
- this.animations.set(animationType, animationAction);
1455
- if (animationType === 0 /* idle */) {
1456
- animationAction.play();
1457
- }
1458
- }
1459
- transitionToAnimation(targetAnimation, transitionDuration = 0.15) {
1460
- if (this.currentAnimation === targetAnimation)
1461
- return;
1462
- const currentAction = this.animations.get(this.currentAnimation);
1463
- const targetAction = this.animations.get(targetAnimation);
1464
- if (!targetAction)
1465
- return;
1466
- if (currentAction) {
1467
- currentAction.enabled = true;
1468
- targetAction.reset().setEffectiveTimeScale(1).setEffectiveWeight(1).fadeIn(transitionDuration).play();
1469
- currentAction.crossFadeTo(targetAction, transitionDuration, true);
1470
- } else {
1471
- targetAction.play();
1472
- }
1473
- this.currentAnimation = targetAnimation;
1387
+ this.character.update(time, deltaTime);
1474
1388
  }
1475
1389
  updateFromNetwork(clientUpdate) {
1476
- if (!this.characterModel)
1477
- return;
1478
1390
  const { position, rotation, state } = clientUpdate;
1479
- this.characterModel.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1391
+ this.character.position.lerp(new Vector36(position.x, position.y, position.z), 0.15);
1480
1392
  const rotationQuaternion = new Quaternion3(0, rotation.quaternionY, 0, rotation.quaternionW);
1481
- this.characterModel.quaternion.slerp(rotationQuaternion, 0.6);
1393
+ this.character.quaternion.slerp(rotationQuaternion, 0.6);
1482
1394
  if (state !== this.currentAnimation) {
1483
- this.transitionToAnimation(state);
1395
+ this.currentAnimation = state;
1396
+ this.character.updateAnimation(state);
1484
1397
  }
1485
1398
  }
1486
1399
  };
1487
1400
 
1488
- // src/character/CharacterManager.ts
1401
+ // src/character/url-position.ts
1402
+ import { Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
1489
1403
  function encodeCharacterAndCamera(character, camera) {
1490
1404
  return [
1491
1405
  ...toArray(character.position),
@@ -1494,135 +1408,96 @@ function encodeCharacterAndCamera(character, camera) {
1494
1408
  ...toArray(camera.quaternion)
1495
1409
  ].join(",");
1496
1410
  }
1497
- function decodeCharacterAndCamera(hash, character, camera) {
1411
+ function decodeCharacterAndCamera(hash) {
1498
1412
  const values = hash.split(",").map(Number);
1499
- character.position.fromArray(values.slice(0, 3));
1500
- character.quaternion.fromArray(values.slice(3, 7));
1501
- camera.position.fromArray(values.slice(7, 10));
1502
- camera.quaternion.fromArray(values.slice(10, 14));
1413
+ return {
1414
+ character: {
1415
+ position: new Vector37(values[0], values[1], values[2]),
1416
+ quaternion: new Quaternion4(values[3], values[4], values[5], values[6])
1417
+ },
1418
+ camera: {
1419
+ position: new Vector37(values[7], values[8], values[9]),
1420
+ quaternion: new Quaternion4(values[10], values[11], values[12], values[13])
1421
+ }
1422
+ };
1503
1423
  }
1424
+
1425
+ // src/character/CharacterManager.ts
1504
1426
  var CharacterManager = class {
1505
- constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, inputManager, clientStates, sendUpdate) {
1427
+ constructor(composer, characterModelLoader, collisionsManager, cameraManager, timeManager, keyInputManager, clientStates, sendUpdate) {
1506
1428
  this.composer = composer;
1507
1429
  this.characterModelLoader = characterModelLoader;
1508
1430
  this.collisionsManager = collisionsManager;
1509
1431
  this.cameraManager = cameraManager;
1510
1432
  this.timeManager = timeManager;
1511
- this.inputManager = inputManager;
1433
+ this.keyInputManager = keyInputManager;
1512
1434
  this.clientStates = clientStates;
1513
1435
  this.sendUpdate = sendUpdate;
1514
- /*
1515
- TODO - re-enable updating location hash when there is a solution that waits for models to load (currently if the
1516
- character was standing on a model and the page is reloaded the character falls into the model before it loads and
1517
- can be trapped).
1518
- */
1519
- this.updateLocationHash = false;
1436
+ this.updateLocationHash = true;
1437
+ this.headTargetOffset = new Vector38(0, 1.3, 0);
1520
1438
  this.id = 0;
1521
- this.loadingCharacters = /* @__PURE__ */ new Map();
1522
1439
  this.remoteCharacters = /* @__PURE__ */ new Map();
1523
1440
  this.remoteCharacterControllers = /* @__PURE__ */ new Map();
1524
1441
  this.characterDescription = null;
1525
- this.character = null;
1442
+ this.localCharacter = null;
1526
1443
  this.cameraOffsetTarget = 0;
1527
1444
  this.cameraOffset = 0;
1528
1445
  this.speakingCharacters = /* @__PURE__ */ new Map();
1529
- this.group = new Group();
1530
- }
1531
- /* TODO:
1532
- 1) Separate this method into spawnLocalCharacter and spawnRemoteCharacter
1533
- 2) Make this synchronous to avoid having loadingCharacters and instead manage
1534
- the mesh loading async (would allow us to show a nameplate where a remote
1535
- user is before the asset loads).
1536
- */
1537
- spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector37(), spawnRotation = new Euler()) {
1446
+ this.group = new Group2();
1447
+ }
1448
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector38(), spawnRotation = new Euler()) {
1449
+ var _a;
1538
1450
  this.characterDescription = characterDescription;
1539
- const characterLoadingPromise = new Promise((resolve) => {
1540
- const character = new Character(
1541
- characterDescription,
1542
- this.characterModelLoader,
1451
+ const character = new Character(
1452
+ characterDescription,
1453
+ this.characterModelLoader,
1454
+ id,
1455
+ () => {
1456
+ },
1457
+ this.cameraManager,
1458
+ this.composer
1459
+ );
1460
+ if (isLocal) {
1461
+ const quaternion = new Quaternion5().setFromEuler(character.rotation);
1462
+ this.sendUpdate({
1543
1463
  id,
1544
- isLocal,
1545
- () => {
1546
- var _a, _b, _c, _d, _e, _f, _g;
1547
- if (window.location.hash && window.location.hash.length > 1) {
1548
- decodeCharacterAndCamera(
1549
- window.location.hash.substring(1),
1550
- character.model.mesh,
1551
- this.cameraManager.camera
1552
- );
1553
- } else {
1554
- spawnPosition = spawnPosition || getSpawnPositionInsideCircle(3, 30, id, 0.4);
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);
1558
- character.model.mesh.updateMatrixWorld();
1559
- const quaternion = new Quaternion4().setFromEuler(character.model.mesh.rotation);
1560
- this.sendUpdate({
1561
- id,
1562
- position: {
1563
- x: spawnPosition.x,
1564
- y: spawnPosition.y,
1565
- z: spawnPosition.z
1566
- },
1567
- rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1568
- state: 0 /* idle */
1569
- });
1570
- }
1571
- character.model.hideMaterialByMeshName("SK_Mannequin_2");
1572
- if (!isLocal) {
1573
- (_b = (_a = character.model) == null ? void 0 : _a.mesh) == null ? void 0 : _b.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1574
- (_d = (_c = character.model) == null ? void 0 : _c.mesh) == null ? void 0 : _d.updateMatrixWorld();
1575
- character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1576
- }
1577
- this.group.add(character.model.mesh);
1578
- if (isLocal) {
1579
- this.id = id;
1580
- this.character = character;
1581
- (_e = this.character.tooltip) == null ? void 0 : _e.setText(`${id}`);
1582
- } else {
1583
- this.remoteCharacters.set(id, character);
1584
- const remoteController = new RemoteController(character, this.characterModelLoader, id);
1585
- remoteController.setAnimationFromFile(
1586
- 0 /* idle */,
1587
- characterDescription.idleAnimationFileUrl
1588
- );
1589
- remoteController.setAnimationFromFile(
1590
- 1 /* walking */,
1591
- characterDescription.jogAnimationFileUrl
1592
- );
1593
- remoteController.setAnimationFromFile(
1594
- 2 /* running */,
1595
- characterDescription.sprintAnimationFileUrl
1596
- );
1597
- remoteController.setAnimationFromFile(
1598
- 4 /* air */,
1599
- characterDescription.airAnimationFileUrl
1600
- );
1601
- (_f = remoteController.characterModel) == null ? void 0 : _f.position.set(
1602
- spawnPosition.x,
1603
- spawnPosition.y,
1604
- spawnPosition.z
1605
- );
1606
- this.remoteCharacterControllers.set(id, remoteController);
1607
- (_g = character.tooltip) == null ? void 0 : _g.setText(`${id}`);
1608
- }
1609
- resolve(character);
1464
+ position: {
1465
+ x: spawnPosition.x,
1466
+ y: spawnPosition.y,
1467
+ z: spawnPosition.z
1610
1468
  },
1469
+ rotation: { quaternionY: quaternion.y, quaternionW: quaternion.w },
1470
+ state: 0 /* idle */
1471
+ });
1472
+ }
1473
+ if (isLocal) {
1474
+ this.id = id;
1475
+ this.localCharacter = character;
1476
+ this.localController = new LocalController(
1477
+ this.localCharacter,
1478
+ this.id,
1611
1479
  this.collisionsManager,
1612
- this.inputManager,
1480
+ this.keyInputManager,
1613
1481
  this.cameraManager,
1614
- this.timeManager,
1615
- this.composer
1482
+ this.timeManager
1616
1483
  );
1617
- });
1618
- this.loadingCharacters.set(id, characterLoadingPromise);
1619
- return characterLoadingPromise;
1484
+ this.localCharacter.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1485
+ this.localCharacter.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1486
+ } else {
1487
+ this.remoteCharacters.set(id, character);
1488
+ const remoteController = new RemoteController(character, id);
1489
+ remoteController.character.position.set(spawnPosition.x, spawnPosition.y, spawnPosition.z);
1490
+ remoteController.character.rotation.set(spawnRotation.x, spawnRotation.y, spawnRotation.z);
1491
+ this.remoteCharacterControllers.set(id, remoteController);
1492
+ }
1493
+ (_a = character.tooltip) == null ? void 0 : _a.setText(`${id}`);
1494
+ this.group.add(character);
1620
1495
  }
1621
1496
  getLocalCharacterPositionAndRotation() {
1622
- if (this.character && this.character.model && this.character.model.mesh) {
1497
+ if (this.localCharacter && this.localCharacter && this.localCharacter) {
1623
1498
  return {
1624
- position: this.character.model.mesh.position,
1625
- rotation: this.character.model.mesh.rotation
1499
+ position: this.localCharacter.position,
1500
+ rotation: this.localCharacter.rotation
1626
1501
  };
1627
1502
  }
1628
1503
  return {
@@ -1632,54 +1507,48 @@ var CharacterManager = class {
1632
1507
  }
1633
1508
  clear() {
1634
1509
  for (const [id, character] of this.remoteCharacters) {
1635
- this.group.remove(character.model.mesh);
1510
+ this.group.remove(character);
1636
1511
  this.remoteCharacters.delete(id);
1637
1512
  this.remoteCharacterControllers.delete(id);
1638
1513
  }
1639
- if (this.character) {
1640
- this.group.remove(this.character.model.mesh);
1641
- this.character = null;
1514
+ if (this.localCharacter) {
1515
+ this.group.remove(this.localCharacter);
1516
+ this.localCharacter = null;
1642
1517
  }
1643
- this.loadingCharacters.clear();
1644
1518
  }
1645
1519
  setSpeakingCharacter(id, value) {
1646
1520
  this.speakingCharacters.set(id, value);
1647
1521
  }
1648
1522
  update() {
1649
- var _a, _b, _c, _d;
1650
- if (this.character) {
1651
- this.character.update(this.timeManager.time);
1523
+ var _a, _b, _c;
1524
+ if (this.localCharacter) {
1525
+ this.localCharacter.update(this.timeManager.time, this.timeManager.deltaTime);
1652
1526
  if (this.speakingCharacters.has(this.id)) {
1653
- (_a = this.character.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1527
+ (_a = this.localCharacter.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1654
1528
  }
1655
- if ((_b = this.character.model) == null ? void 0 : _b.mesh) {
1656
- this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1657
- this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1658
- const targetOffset = new Vector37(0, 1.3, this.cameraOffset);
1659
- targetOffset.applyQuaternion(this.character.model.mesh.quaternion);
1660
- this.cameraManager.setTarget(this.character.position.add(targetOffset));
1661
- }
1662
- if (this.character.controller) {
1663
- this.character.controller.update();
1664
- if (this.timeManager.frame % 2 === 0) {
1665
- this.sendUpdate(this.character.controller.networkState);
1666
- }
1529
+ this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1530
+ this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1531
+ const targetOffset = new Vector38(0, 0, this.cameraOffset);
1532
+ targetOffset.add(this.headTargetOffset);
1533
+ targetOffset.applyQuaternion(this.localCharacter.quaternion);
1534
+ this.cameraManager.setTarget(targetOffset.add(this.localCharacter.position));
1535
+ this.localController.update();
1536
+ if (this.timeManager.frame % 2 === 0) {
1537
+ this.sendUpdate(this.localController.networkState);
1667
1538
  }
1668
1539
  for (const [id, update] of this.clientStates) {
1669
1540
  if (this.remoteCharacters.has(id) && this.speakingCharacters.has(id)) {
1670
1541
  const character = this.remoteCharacters.get(id);
1671
- (_c = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _c.setSpeaking(this.speakingCharacters.get(id));
1542
+ (_b = character == null ? void 0 : character.speakingIndicator) == null ? void 0 : _b.setSpeaking(this.speakingCharacters.get(id));
1672
1543
  }
1673
1544
  const { position } = update;
1674
- if (!this.remoteCharacters.has(id) && !this.loadingCharacters.has(id)) {
1545
+ if (!this.remoteCharacters.has(id)) {
1675
1546
  this.spawnCharacter(
1676
1547
  this.characterDescription,
1677
1548
  id,
1678
1549
  false,
1679
- new Vector37(position.x, position.y, position.z)
1680
- ).then((_character) => {
1681
- this.loadingCharacters.delete(id);
1682
- });
1550
+ new Vector38(position.x, position.y, position.z)
1551
+ );
1683
1552
  }
1684
1553
  const characterController = this.remoteCharacterControllers.get(id);
1685
1554
  if (characterController) {
@@ -1688,15 +1557,15 @@ var CharacterManager = class {
1688
1557
  }
1689
1558
  for (const [id, character] of this.remoteCharacters) {
1690
1559
  if (!this.clientStates.has(id)) {
1691
- (_d = character.speakingIndicator) == null ? void 0 : _d.dispose();
1692
- this.group.remove(character.model.mesh);
1560
+ (_c = character.speakingIndicator) == null ? void 0 : _c.dispose();
1561
+ this.group.remove(character);
1693
1562
  this.remoteCharacters.delete(id);
1694
1563
  this.remoteCharacterControllers.delete(id);
1695
1564
  }
1696
1565
  }
1697
1566
  if (this.updateLocationHash && this.timeManager.frame % 60 === 0) {
1698
1567
  window.location.hash = encodeCharacterAndCamera(
1699
- this.character.model.mesh,
1568
+ this.localCharacter,
1700
1569
  this.cameraManager.camera
1701
1570
  );
1702
1571
  }
@@ -1749,20 +1618,13 @@ var LRUCache = class {
1749
1618
  this.cache.set(key, value);
1750
1619
  }
1751
1620
  };
1752
- var _CharacterModelLoader = class _CharacterModelLoader {
1621
+ var CharacterModelLoader = class {
1753
1622
  constructor(maxCacheSize = 100) {
1754
1623
  this.ongoingLoads = /* @__PURE__ */ new Map();
1755
1624
  this.loadingManager = new LoadingManager();
1756
1625
  this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
1757
1626
  this.modelCache = new LRUCache(maxCacheSize);
1758
1627
  }
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
1628
  async load(fileUrl, fileType) {
1767
1629
  const cachedModel = this.modelCache.get(fileUrl);
1768
1630
  if (cachedModel) {
@@ -1773,16 +1635,22 @@ var _CharacterModelLoader = class _CharacterModelLoader {
1773
1635
  console.log(`Loading ${fileUrl} from server`);
1774
1636
  const ongoingLoad = this.ongoingLoads.get(fileUrl);
1775
1637
  if (ongoingLoad)
1776
- return ongoingLoad;
1638
+ return ongoingLoad.then((loadedModel) => {
1639
+ const blobURL = URL.createObjectURL(loadedModel.blob);
1640
+ return this.loadFromUrl(blobURL, fileType, loadedModel.originalExtension);
1641
+ });
1777
1642
  const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
1778
1643
  const originalExtension = fileUrl.split(".").pop() || "";
1779
- this.modelCache.set(fileUrl, { blob, originalExtension });
1780
- const blobURL = URL.createObjectURL(blob);
1644
+ const cached = { blob, originalExtension };
1645
+ this.modelCache.set(fileUrl, cached);
1781
1646
  this.ongoingLoads.delete(fileUrl);
1782
- return this.loadFromUrl(blobURL, fileType, originalExtension);
1647
+ return cached;
1783
1648
  });
1784
1649
  this.ongoingLoads.set(fileUrl, loadPromise);
1785
- return loadPromise;
1650
+ return loadPromise.then((loadedModel) => {
1651
+ const blobURL = URL.createObjectURL(loadedModel.blob);
1652
+ return this.loadFromUrl(blobURL, fileType, loadedModel.originalExtension);
1653
+ });
1786
1654
  }
1787
1655
  }
1788
1656
  async loadFromUrl(url, fileType, extension) {
@@ -1813,9 +1681,6 @@ var _CharacterModelLoader = class _CharacterModelLoader {
1813
1681
  }
1814
1682
  }
1815
1683
  };
1816
- _CharacterModelLoader.instance = null;
1817
- var CharacterModelLoader = _CharacterModelLoader;
1818
- var MODEL_LOADER = CharacterModelLoader.getInstance();
1819
1684
 
1820
1685
  // src/input/KeyInputManager.ts
1821
1686
  var KeyInputManager = class {
@@ -1884,9 +1749,10 @@ var KeyInputManager = class {
1884
1749
  import {
1885
1750
  InteractionManager,
1886
1751
  MMLClickTrigger,
1887
- PromptManager
1752
+ PromptManager,
1753
+ LoadingProgressManager
1888
1754
  } from "mml-web";
1889
- import { Group as Group2 } from "three";
1755
+ import { Group as Group3 } from "three";
1890
1756
  var MMLCompositionScene = class {
1891
1757
  constructor(targetElement, renderer, scene, camera, audioListener, collisionsManager, getUserPositionAndRotation) {
1892
1758
  this.renderer = renderer;
@@ -1896,7 +1762,7 @@ var MMLCompositionScene = class {
1896
1762
  this.collisionsManager = collisionsManager;
1897
1763
  this.getUserPositionAndRotation = getUserPositionAndRotation;
1898
1764
  this.chatProbes = /* @__PURE__ */ new Set();
1899
- this.group = new Group2();
1765
+ this.group = new Group3();
1900
1766
  this.promptManager = PromptManager.init(targetElement);
1901
1767
  const { interactionListener, interactionManager } = InteractionManager.init(
1902
1768
  targetElement,
@@ -1905,6 +1771,7 @@ var MMLCompositionScene = class {
1905
1771
  );
1906
1772
  this.interactionManager = interactionManager;
1907
1773
  this.interactionListener = interactionListener;
1774
+ this.loadingProgressManager = new LoadingProgressManager();
1908
1775
  this.mmlScene = {
1909
1776
  getAudioListener: () => this.audioListener,
1910
1777
  getRenderer: () => this.renderer,
@@ -1940,6 +1807,9 @@ var MMLCompositionScene = class {
1940
1807
  },
1941
1808
  prompt: (promptProps, callback) => {
1942
1809
  this.promptManager.prompt(promptProps, callback);
1810
+ },
1811
+ getLoadingProgressManager: () => {
1812
+ return this.loadingProgressManager;
1943
1813
  }
1944
1814
  };
1945
1815
  this.clickTrigger = MMLClickTrigger.init(targetElement, this.mmlScene);
@@ -2888,12 +2758,12 @@ import {
2888
2758
  import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
2889
2759
 
2890
2760
  // 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 {
2761
+ import { CameraHelper, Color as Color5, DirectionalLight, Group as Group4, OrthographicCamera, Vector3 as Vector39 } from "three";
2762
+ var Sun = class extends Group4 {
2893
2763
  constructor() {
2894
2764
  super();
2895
2765
  this.debug = false;
2896
- this.sunOffset = new Vector38(
2766
+ this.sunOffset = new Vector39(
2897
2767
  39 * (Math.PI / 180),
2898
2768
  50 * (Math.PI / 180),
2899
2769
  100
@@ -2920,7 +2790,7 @@ var Sun = class extends Group3 {
2920
2790
  this.directionalLight.shadow.mapSize.set(this.shadowResolution, this.shadowResolution);
2921
2791
  this.directionalLight.castShadow = true;
2922
2792
  this.setColor();
2923
- this.updateCharacterPosition(new Vector38(0, 0, 0));
2793
+ this.updateCharacterPosition(new Vector39(0, 0, 0));
2924
2794
  this.add(this.directionalLight);
2925
2795
  if (this.debug === true && this.camHelper instanceof CameraHelper) {
2926
2796
  this.add(this.camHelper);
@@ -2958,7 +2828,7 @@ var Sun = class extends Group3 {
2958
2828
  if (!this.target)
2959
2829
  return;
2960
2830
  const distance = this.sunOffset.z;
2961
- const sphericalPosition = new Vector38(
2831
+ const sphericalPosition = new Vector39(
2962
2832
  distance * Math.sin(polarAngle) * Math.cos(azimuthalAngle),
2963
2833
  distance * Math.cos(polarAngle),
2964
2834
  distance * Math.sin(polarAngle) * Math.sin(azimuthalAngle)
@@ -3438,13 +3308,13 @@ import {
3438
3308
  Color as Color7,
3439
3309
  DoubleSide,
3440
3310
  Euler as Euler2,
3441
- Group as Group4,
3311
+ Group as Group5,
3442
3312
  Line3 as Line32,
3443
3313
  Matrix4 as Matrix42,
3444
3314
  Mesh as Mesh4,
3445
3315
  MeshBasicMaterial as MeshBasicMaterial3,
3446
3316
  Ray,
3447
- Vector3 as Vector39
3317
+ Vector3 as Vector310
3448
3318
  } from "three";
3449
3319
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
3450
3320
  import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
@@ -3452,9 +3322,9 @@ import { MeshBVH, MeshBVHVisualizer } from "three-mesh-bvh";
3452
3322
  var CollisionsManager = class {
3453
3323
  constructor(scene) {
3454
3324
  this.debug = false;
3455
- this.tempVector = new Vector39();
3456
- this.tempVector2 = new Vector39();
3457
- this.tempVector3 = new Vector39();
3325
+ this.tempVector = new Vector310();
3326
+ this.tempVector2 = new Vector310();
3327
+ this.tempVector3 = new Vector310();
3458
3328
  this.tempRay = new Ray();
3459
3329
  this.tempMatrix = new Matrix42();
3460
3330
  this.tempMatrix2 = new Matrix42();
@@ -3524,7 +3394,7 @@ var CollisionsManager = class {
3524
3394
  const normalsHelper = new VertexNormalsHelper(wireframeMesh, 0.25, 65280);
3525
3395
  const visualizer = new MeshBVHVisualizer(wireframeMesh, 4);
3526
3396
  visualizer.edgeMaterial.color = new Color7("blue");
3527
- const debugGroup = new Group4();
3397
+ const debugGroup = new Group5();
3528
3398
  debugGroup.add(wireframeMesh, normalsHelper, visualizer);
3529
3399
  group.matrixWorld.decompose(debugGroup.position, debugGroup.quaternion, debugGroup.scale);
3530
3400
  visualizer.update();
@@ -3603,7 +3473,7 @@ var CollisionsManager = class {
3603
3473
  const realDistance = intersectionSegment.distance();
3604
3474
  if (realDistance < capsuleRadius) {
3605
3475
  if (!collisionPosition) {
3606
- collisionPosition = new Vector39().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
3476
+ collisionPosition = new Vector310().copy(closestPointOnSegment).applyMatrix4(meshState.matrix);
3607
3477
  }
3608
3478
  const ratio = realDistance / modelReferenceDistance;
3609
3479
  const realDepth = capsuleRadius - realDistance;
@@ -3659,6 +3529,15 @@ export {
3659
3529
  MMLCompositionScene,
3660
3530
  Sun,
3661
3531
  TimeManager,
3662
- TweakPane
3532
+ TweakPane,
3533
+ clamp,
3534
+ decodeCharacterAndCamera,
3535
+ ease,
3536
+ encodeCharacterAndCamera,
3537
+ getSpawnPositionInsideCircle,
3538
+ remap,
3539
+ round,
3540
+ roundToDecimalPlaces,
3541
+ toArray
3663
3542
  };
3664
3543
  //# sourceMappingURL=index.js.map