@mml-io/3d-web-client-core 0.0.0-experimental-1e55576-20240116 → 0.0.0-experimental-79fca4a-20240117

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.
@@ -1,4 +1,4 @@
1
- import { Line3 } from "three";
1
+ import { Line3, Quaternion, Vector3 } from "three";
2
2
  import { CameraManager } from "../camera/CameraManager";
3
3
  import { CollisionsManager } from "../collisions/CollisionsManager";
4
4
  import { KeyInputManager } from "../input/KeyInputManager";
@@ -12,13 +12,10 @@ export declare class LocalController {
12
12
  private readonly keyInputManager;
13
13
  private readonly cameraManager;
14
14
  private readonly timeManager;
15
- private collisionDetectionSteps;
16
15
  capsuleInfo: {
17
16
  radius: number;
18
17
  segment: Line3;
19
18
  };
20
- private maxWalkSpeed;
21
- private maxRunSpeed;
22
19
  private gravity;
23
20
  private jumpForce;
24
21
  private coyoteTimeThreshold;
@@ -28,6 +25,7 @@ export declare class LocalController {
28
25
  private characterWasOnGround;
29
26
  private characterAirborneSince;
30
27
  private currentHeight;
28
+ private currentSurfaceAngle;
31
29
  private characterVelocity;
32
30
  private vectorUp;
33
31
  private vectorDown;
@@ -35,9 +33,21 @@ export declare class LocalController {
35
33
  private azimuthalAngle;
36
34
  private tempMatrix;
37
35
  private tempSegment;
36
+ private tempQuaternion;
37
+ private tempEuler;
38
38
  private tempVector;
39
39
  private tempVector2;
40
+ private tempVector3;
40
41
  private rayCaster;
42
+ private surfaceTempQuaternion;
43
+ private surfaceTempQuaternion2;
44
+ private surfaceTempVector1;
45
+ private surfaceTempVector2;
46
+ private surfaceTempVector3;
47
+ private surfaceTempVector4;
48
+ private surfaceTempVector5;
49
+ private surfaceTempRay;
50
+ private lastFrameSurfaceState;
41
51
  private forward;
42
52
  private backward;
43
53
  private left;
@@ -46,18 +56,21 @@ export declare class LocalController {
46
56
  private jump;
47
57
  private anyDirection;
48
58
  private conflictingDirections;
49
- private speed;
50
- private targetSpeed;
51
59
  networkState: CharacterState;
52
60
  constructor(character: Character, id: number, collisionsManager: CollisionsManager, keyInputManager: KeyInputManager, cameraManager: CameraManager, timeManager: TimeManager);
61
+ private updateControllerState;
53
62
  update(): void;
54
63
  private getTargetAnimation;
55
64
  private updateRotationOffset;
56
65
  private updateAzimuthalAngle;
57
66
  private computeAngularDifference;
58
67
  private updateRotation;
59
- private addScaledVectorToCharacter;
68
+ private applyControls;
60
69
  private updatePosition;
70
+ getMovementFromSurfaces(userPosition: Vector3, deltaTime: number): {
71
+ rotation: Quaternion;
72
+ position: Vector3;
73
+ } | null;
61
74
  private updateNetworkState;
62
75
  private resetPosition;
63
76
  }
@@ -1,7 +1,7 @@
1
1
  import { MElement } from "mml-web";
2
- import { Group, Line3, Matrix4, Ray, Scene } from "three";
2
+ import { Group, Line3, Matrix4, Ray, Scene, Vector3 } from "three";
3
3
  import { MeshBVH } from "three-mesh-bvh";
4
- type CollisionMeshState = {
4
+ export type CollisionMeshState = {
5
5
  matrix: Matrix4;
6
6
  source: Group;
7
7
  meshBVH: MeshBVH;
@@ -14,6 +14,7 @@ export declare class CollisionsManager {
14
14
  private tempVector;
15
15
  private tempVector2;
16
16
  private tempVector3;
17
+ private tempQuaternion;
17
18
  private tempRay;
18
19
  private tempMatrix;
19
20
  private tempMatrix2;
@@ -24,7 +25,7 @@ export declare class CollisionsManager {
24
25
  collisionMeshState: Map<Group, CollisionMeshState>;
25
26
  private collisionTrigger;
26
27
  constructor(scene: Scene);
27
- raycastFirstDistance(ray: Ray): number | null;
28
+ raycastFirst(ray: Ray): [number, Vector3, CollisionMeshState] | null;
28
29
  private createCollisionMeshState;
29
30
  addMeshesGroup(group: Group, mElement?: MElement): void;
30
31
  updateMeshesGroup(group: Group): void;
@@ -32,4 +33,3 @@ export declare class CollisionsManager {
32
33
  private applyCollider;
33
34
  applyColliders(tempSegment: Line3, radius: number): void;
34
35
  }
35
- export {};
package/build/index.js CHANGED
@@ -203,10 +203,10 @@ var CameraManager = class {
203
203
  this.camera.position,
204
204
  this.target.clone().sub(this.camera.position).normalize()
205
205
  );
206
- const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
206
+ const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
207
207
  const cameraToPlayerDistance = this.camera.position.distanceTo(this.target);
208
- if (minimumDistance !== null && minimumDistance <= cameraToPlayerDistance) {
209
- this.targetDistance = cameraToPlayerDistance - minimumDistance;
208
+ if (firstRaycastHit !== null && firstRaycastHit[0] <= cameraToPlayerDistance) {
209
+ this.targetDistance = cameraToPlayerDistance - firstRaycastHit[0];
210
210
  this.distance = this.targetDistance;
211
211
  } else {
212
212
  this.targetDistance += (this.desiredDistance - this.targetDistance) * this.dampingFactor * 4;
@@ -1136,10 +1136,19 @@ var Character = class extends Group {
1136
1136
  };
1137
1137
 
1138
1138
  // src/character/CharacterManager.ts
1139
- import { Euler, Group as Group2, Quaternion as Quaternion5, Vector3 as Vector38 } from "three";
1139
+ import { Euler as Euler2, Group as Group2, Quaternion as Quaternion5, Vector3 as Vector38 } from "three";
1140
1140
 
1141
1141
  // src/character/LocalController.ts
1142
- import { Line3, Matrix4, Quaternion as Quaternion2, Raycaster as Raycaster2, Vector3 as Vector35 } from "three";
1142
+ import { Euler, Line3, Matrix4, Quaternion as Quaternion2, Ray, Raycaster as Raycaster2, Vector3 as Vector35 } from "three";
1143
+ var downVector = new Vector35(0, -1, 0);
1144
+ var airResistance = 0.5;
1145
+ var groundResistance = 0.99999999;
1146
+ var airControlModifier = 0.05;
1147
+ var groundWalkControl = 0.75;
1148
+ var groundRunControl = 1;
1149
+ var baseControl = 200;
1150
+ var collisionDetectionSteps = 15;
1151
+ var minimumSurfaceAngle = 0.9;
1143
1152
  var LocalController = class {
1144
1153
  constructor(character, id, collisionsManager, keyInputManager, cameraManager, timeManager) {
1145
1154
  this.character = character;
@@ -1148,15 +1157,12 @@ var LocalController = class {
1148
1157
  this.keyInputManager = keyInputManager;
1149
1158
  this.cameraManager = cameraManager;
1150
1159
  this.timeManager = timeManager;
1151
- this.collisionDetectionSteps = 15;
1152
1160
  this.capsuleInfo = {
1153
1161
  radius: 0.4,
1154
1162
  segment: new Line3(new Vector35(), new Vector35(0, 1.05, 0))
1155
1163
  };
1156
- this.maxWalkSpeed = 6;
1157
- this.maxRunSpeed = 8.5;
1158
1164
  this.gravity = -42;
1159
- this.jumpForce = 16;
1165
+ this.jumpForce = 20;
1160
1166
  this.coyoteTimeThreshold = 70;
1161
1167
  this.coyoteTime = false;
1162
1168
  this.canJump = true;
@@ -1164,6 +1170,7 @@ var LocalController = class {
1164
1170
  this.characterWasOnGround = false;
1165
1171
  this.characterAirborneSince = 0;
1166
1172
  this.currentHeight = 0;
1173
+ this.currentSurfaceAngle = new Vector35();
1167
1174
  this.characterVelocity = new Vector35();
1168
1175
  this.vectorUp = new Vector35(0, 1, 0);
1169
1176
  this.vectorDown = new Vector35(0, -1, 0);
@@ -1171,11 +1178,21 @@ var LocalController = class {
1171
1178
  this.azimuthalAngle = 0;
1172
1179
  this.tempMatrix = new Matrix4();
1173
1180
  this.tempSegment = new Line3();
1181
+ this.tempQuaternion = new Quaternion2();
1182
+ this.tempEuler = new Euler();
1174
1183
  this.tempVector = new Vector35();
1175
1184
  this.tempVector2 = new Vector35();
1185
+ this.tempVector3 = new Vector35();
1176
1186
  this.rayCaster = new Raycaster2();
1177
- this.speed = 0;
1178
- this.targetSpeed = 0;
1187
+ this.surfaceTempQuaternion = new Quaternion2();
1188
+ this.surfaceTempQuaternion2 = new Quaternion2();
1189
+ this.surfaceTempVector1 = new Vector35();
1190
+ this.surfaceTempVector2 = new Vector35();
1191
+ this.surfaceTempVector3 = new Vector35();
1192
+ this.surfaceTempVector4 = new Vector35();
1193
+ this.surfaceTempVector5 = new Vector35();
1194
+ this.surfaceTempRay = new Ray();
1195
+ this.lastFrameSurfaceState = null;
1179
1196
  this.networkState = {
1180
1197
  id: this.id,
1181
1198
  position: { x: 0, y: 0, z: 0 },
@@ -1183,47 +1200,55 @@ var LocalController = class {
1183
1200
  state: 0 /* idle */
1184
1201
  };
1185
1202
  }
1203
+ updateControllerState() {
1204
+ this.forward = this.keyInputManager.forward;
1205
+ this.backward = this.keyInputManager.backward;
1206
+ this.left = this.keyInputManager.left;
1207
+ this.right = this.keyInputManager.right;
1208
+ this.run = this.keyInputManager.run;
1209
+ this.jump = this.keyInputManager.jump;
1210
+ this.anyDirection = this.keyInputManager.anyDirection;
1211
+ this.conflictingDirections = this.keyInputManager.conflictingDirection;
1212
+ }
1186
1213
  update() {
1187
- const { forward, backward, left, right, run, jump, anyDirection, conflictingDirection } = this.keyInputManager;
1188
- this.forward = forward;
1189
- this.backward = backward;
1190
- this.left = left;
1191
- this.right = right;
1192
- this.run = run;
1193
- this.jump = jump;
1194
- this.anyDirection = anyDirection;
1195
- this.conflictingDirections = conflictingDirection;
1196
- this.targetSpeed = this.run ? this.maxRunSpeed : this.maxWalkSpeed;
1197
- this.speed += ease(this.targetSpeed, this.speed, 0.07);
1214
+ this.updateControllerState();
1198
1215
  this.rayCaster.set(this.character.position, this.vectorDown);
1199
- const minimumDistance = this.collisionsManager.raycastFirstDistance(this.rayCaster.ray);
1200
- if (minimumDistance !== null) {
1201
- this.currentHeight = minimumDistance;
1216
+ const firstRaycastHit = this.collisionsManager.raycastFirst(this.rayCaster.ray);
1217
+ if (firstRaycastHit !== null) {
1218
+ this.currentHeight = firstRaycastHit[0];
1219
+ this.currentSurfaceAngle.copy(firstRaycastHit[1]);
1202
1220
  }
1203
- if (anyDirection || !this.characterOnGround) {
1221
+ if (this.anyDirection || !this.characterOnGround) {
1204
1222
  const targetAnimation = this.getTargetAnimation();
1205
1223
  this.character.updateAnimation(targetAnimation);
1206
1224
  } else {
1207
1225
  this.character.updateAnimation(0 /* idle */);
1208
1226
  }
1209
- if (this.anyDirection)
1227
+ if (this.anyDirection) {
1210
1228
  this.updateRotation();
1211
- for (let i = 0; i < this.collisionDetectionSteps; i++) {
1212
- this.updatePosition(this.timeManager.deltaTime / this.collisionDetectionSteps, i);
1213
1229
  }
1214
- if (this.character.position.y < 0)
1230
+ for (let i = 0; i < collisionDetectionSteps; i++) {
1231
+ this.updatePosition(
1232
+ this.timeManager.deltaTime,
1233
+ this.timeManager.deltaTime / collisionDetectionSteps,
1234
+ i
1235
+ );
1236
+ }
1237
+ if (this.character.position.y < 0) {
1215
1238
  this.resetPosition();
1239
+ }
1216
1240
  this.updateNetworkState();
1217
1241
  }
1218
1242
  getTargetAnimation() {
1219
1243
  if (!this.character)
1220
1244
  return 0 /* idle */;
1221
- if (this.conflictingDirections)
1222
- return 0 /* idle */;
1223
1245
  const jumpHeight = this.characterVelocity.y > 0 ? 0.2 : 1.8;
1224
1246
  if (this.currentHeight > jumpHeight && !this.characterOnGround) {
1225
1247
  return 4 /* air */;
1226
1248
  }
1249
+ if (this.conflictingDirections) {
1250
+ return 0 /* idle */;
1251
+ }
1227
1252
  return this.run && this.anyDirection ? 2 /* running */ : this.anyDirection ? 1 /* walking */ : 0 /* idle */;
1228
1253
  }
1229
1254
  updateRotationOffset() {
@@ -1253,9 +1278,7 @@ var LocalController = class {
1253
1278
  );
1254
1279
  const isCameraFirstPerson = camToModelDistance < 2;
1255
1280
  if (isCameraFirstPerson) {
1256
- const cameraForward = new Vector35(0, 0, 1).applyQuaternion(
1257
- this.cameraManager.camera.quaternion
1258
- );
1281
+ const cameraForward = this.tempVector.set(0, 0, 1).applyQuaternion(this.cameraManager.camera.quaternion);
1259
1282
  this.azimuthalAngle = Math.atan2(cameraForward.x, cameraForward.z);
1260
1283
  } else {
1261
1284
  this.azimuthalAngle = Math.atan2(
@@ -1270,91 +1293,162 @@ var LocalController = class {
1270
1293
  updateRotation() {
1271
1294
  this.updateRotationOffset();
1272
1295
  this.updateAzimuthalAngle();
1273
- const rotationQuaternion = new Quaternion2();
1274
- rotationQuaternion.setFromAxisAngle(this.vectorUp, this.azimuthalAngle + this.rotationOffset);
1296
+ const rotationQuaternion = this.tempQuaternion.setFromAxisAngle(
1297
+ this.vectorUp,
1298
+ this.azimuthalAngle + this.rotationOffset
1299
+ );
1275
1300
  const angularDifference = this.computeAngularDifference(rotationQuaternion);
1276
1301
  const desiredTime = 0.07;
1277
1302
  const angularSpeed = angularDifference / desiredTime;
1278
1303
  const frameRotation = angularSpeed * this.timeManager.deltaTime;
1279
1304
  this.character.quaternion.rotateTowards(rotationQuaternion, frameRotation);
1280
1305
  }
1281
- addScaledVectorToCharacter(deltaTime) {
1282
- this.character.position.addScaledVector(this.tempVector, this.speed * deltaTime);
1283
- }
1284
- updatePosition(deltaTime, _iter) {
1306
+ applyControls(deltaTime) {
1307
+ const resistance = this.characterOnGround ? groundResistance : airResistance;
1308
+ const speedFactor = Math.pow(1 - resistance, deltaTime);
1309
+ this.characterVelocity.multiplyScalar(speedFactor);
1310
+ const acceleration = this.tempVector.set(0, 0, 0);
1285
1311
  if (this.characterOnGround) {
1286
- if (!this.jump)
1312
+ if (!this.jump) {
1287
1313
  this.canJump = true;
1314
+ }
1288
1315
  if (this.jump && this.canJump) {
1289
- this.characterVelocity.y += this.jumpForce;
1316
+ acceleration.y += this.jumpForce / deltaTime;
1290
1317
  this.canJump = false;
1291
1318
  } else {
1292
- this.characterVelocity.y = deltaTime * this.gravity;
1319
+ if (this.currentSurfaceAngle.y < minimumSurfaceAngle) {
1320
+ acceleration.y += this.gravity;
1321
+ }
1293
1322
  }
1294
1323
  } else if (this.jump && this.coyoteTime) {
1295
- this.characterVelocity.y = this.jumpForce;
1324
+ acceleration.y += this.jumpForce / deltaTime;
1296
1325
  this.canJump = false;
1297
1326
  } else {
1298
- this.characterVelocity.y += deltaTime * this.gravity;
1327
+ acceleration.y += this.gravity;
1299
1328
  this.canJump = false;
1300
1329
  }
1301
- this.character.position.addScaledVector(this.characterVelocity, deltaTime);
1302
- this.tempVector.set(0, 0, 0);
1303
- if (this.forward) {
1304
- const forward = new Vector35(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1305
- this.tempVector.add(forward);
1306
- }
1307
- if (this.backward) {
1308
- const backward = new Vector35(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1309
- this.tempVector.add(backward);
1310
- }
1311
- if (this.left) {
1312
- const left = new Vector35(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1313
- this.tempVector.add(left);
1330
+ const control = (this.characterOnGround ? this.run ? groundRunControl : groundWalkControl : airControlModifier) * baseControl;
1331
+ const controlAcceleration = this.tempVector2.set(0, 0, 0);
1332
+ if (!this.conflictingDirections) {
1333
+ if (this.forward) {
1334
+ const forward = this.tempVector3.set(0, 0, -1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1335
+ controlAcceleration.add(forward);
1336
+ }
1337
+ if (this.backward) {
1338
+ const backward = this.tempVector3.set(0, 0, 1).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1339
+ controlAcceleration.add(backward);
1340
+ }
1341
+ if (this.left) {
1342
+ const left = this.tempVector3.set(-1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1343
+ controlAcceleration.add(left);
1344
+ }
1345
+ if (this.right) {
1346
+ const right = this.tempVector3.set(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1347
+ controlAcceleration.add(right);
1348
+ }
1314
1349
  }
1315
- if (this.right) {
1316
- const right = new Vector35(1, 0, 0).applyAxisAngle(this.vectorUp, this.azimuthalAngle);
1317
- this.tempVector.add(right);
1350
+ if (controlAcceleration.length() > 0) {
1351
+ controlAcceleration.normalize();
1352
+ controlAcceleration.multiplyScalar(control);
1318
1353
  }
1319
- if (this.tempVector.length() > 0) {
1320
- this.tempVector.normalize();
1321
- this.addScaledVectorToCharacter(deltaTime);
1354
+ acceleration.add(controlAcceleration);
1355
+ this.characterVelocity.addScaledVector(acceleration, deltaTime);
1356
+ this.character.position.addScaledVector(this.characterVelocity, deltaTime);
1357
+ }
1358
+ updatePosition(deltaTime, stepDeltaTime, iter) {
1359
+ this.applyControls(stepDeltaTime);
1360
+ if (iter === 0) {
1361
+ const lastMovement = this.getMovementFromSurfaces(this.character.position, deltaTime);
1362
+ if (lastMovement) {
1363
+ this.character.position.add(lastMovement.position);
1364
+ const asQuaternion = this.tempQuaternion.setFromEuler(this.character.rotation);
1365
+ const lastMovementEuler = this.tempEuler.setFromQuaternion(lastMovement.rotation);
1366
+ lastMovementEuler.x = 0;
1367
+ lastMovementEuler.z = 0;
1368
+ lastMovement.rotation.setFromEuler(lastMovementEuler);
1369
+ asQuaternion.multiply(lastMovement.rotation);
1370
+ this.character.rotation.setFromQuaternion(asQuaternion);
1371
+ }
1322
1372
  }
1323
1373
  this.character.updateMatrixWorld();
1324
- this.tempSegment.copy(this.capsuleInfo.segment);
1325
- this.tempSegment.start.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1326
- this.tempSegment.end.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1327
- this.collisionsManager.applyColliders(this.tempSegment, this.capsuleInfo.radius);
1328
- const newPosition = this.tempVector;
1329
- newPosition.copy(this.tempSegment.start);
1330
- const deltaVector = this.tempVector2;
1331
- deltaVector.subVectors(newPosition, this.character.position);
1332
- const offset = Math.max(0, deltaVector.length() - 1e-5);
1333
- deltaVector.normalize().multiplyScalar(offset);
1334
- this.character.position.add(deltaVector);
1335
- this.characterOnGround = deltaVector.y > Math.abs(deltaTime * this.characterVelocity.y * 0.25);
1374
+ const avatarSegment = this.tempSegment;
1375
+ avatarSegment.copy(this.capsuleInfo.segment);
1376
+ avatarSegment.start.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1377
+ avatarSegment.end.applyMatrix4(this.character.matrixWorld).applyMatrix4(this.tempMatrix);
1378
+ const positionBeforeCollisions = this.tempVector.copy(avatarSegment.start);
1379
+ this.collisionsManager.applyColliders(avatarSegment, this.capsuleInfo.radius);
1380
+ this.character.position.copy(avatarSegment.start);
1381
+ const deltaCollisionPosition = avatarSegment.start.sub(positionBeforeCollisions);
1382
+ this.characterOnGround = deltaCollisionPosition.y > 0;
1336
1383
  if (this.characterWasOnGround && !this.characterOnGround) {
1337
1384
  this.characterAirborneSince = Date.now();
1338
1385
  }
1339
1386
  this.coyoteTime = this.characterVelocity.y < 0 && !this.characterOnGround && Date.now() - this.characterAirborneSince < this.coyoteTimeThreshold;
1340
1387
  this.characterWasOnGround = this.characterOnGround;
1341
- if (this.characterOnGround) {
1342
- this.characterVelocity.set(0, 0, 0);
1388
+ }
1389
+ getMovementFromSurfaces(userPosition, deltaTime) {
1390
+ let lastMovement = null;
1391
+ if (this.lastFrameSurfaceState !== null) {
1392
+ const meshState = this.lastFrameSurfaceState[0];
1393
+ const currentFrameMatrix = meshState.matrix;
1394
+ const lastFrameMatrix = this.lastFrameSurfaceState[1].lastMatrix;
1395
+ if (lastFrameMatrix.equals(currentFrameMatrix)) {
1396
+ } else {
1397
+ const lastMeshPosition = this.surfaceTempVector1;
1398
+ const lastMeshRotation = this.surfaceTempQuaternion;
1399
+ lastFrameMatrix.decompose(lastMeshPosition, lastMeshRotation, this.surfaceTempVector3);
1400
+ const currentMeshPosition = this.surfaceTempVector2;
1401
+ const currentMeshRotation = this.surfaceTempQuaternion2;
1402
+ currentFrameMatrix.decompose(
1403
+ currentMeshPosition,
1404
+ currentMeshRotation,
1405
+ this.surfaceTempVector3
1406
+ );
1407
+ const meshTranslationDelta = this.surfaceTempVector5.copy(currentMeshPosition).sub(lastMeshPosition);
1408
+ const lastFrameRelativeUserPosition = this.surfaceTempVector3.copy(userPosition).sub(lastMeshPosition);
1409
+ const meshRotationDelta = lastMeshRotation.invert().multiply(currentMeshRotation);
1410
+ const translationDueToRotation = this.surfaceTempVector4.copy(lastFrameRelativeUserPosition).applyQuaternion(meshRotationDelta).sub(lastFrameRelativeUserPosition);
1411
+ const translationAndRotationPositionDelta = this.surfaceTempVector1.copy(meshTranslationDelta).add(translationDueToRotation);
1412
+ lastMovement = {
1413
+ position: translationAndRotationPositionDelta,
1414
+ rotation: meshRotationDelta
1415
+ };
1416
+ lastFrameMatrix.copy(currentFrameMatrix);
1417
+ }
1418
+ }
1419
+ const newPosition = this.surfaceTempVector3.copy(userPosition);
1420
+ if (lastMovement) {
1421
+ newPosition.add(lastMovement.position);
1422
+ }
1423
+ newPosition.setY(newPosition.y + 0.05);
1424
+ const ray = this.surfaceTempRay.set(newPosition, downVector);
1425
+ const hit = this.collisionsManager.raycastFirst(ray);
1426
+ if (hit && hit[0] < 0.8) {
1427
+ const currentCollisionMeshState = hit[2];
1428
+ this.lastFrameSurfaceState = [
1429
+ currentCollisionMeshState,
1430
+ { lastMatrix: currentCollisionMeshState.matrix.clone() }
1431
+ ];
1343
1432
  } else {
1344
- deltaVector.normalize();
1345
- this.characterVelocity.addScaledVector(deltaVector, -deltaVector.dot(this.characterVelocity));
1433
+ if (this.lastFrameSurfaceState !== null && lastMovement) {
1434
+ this.characterVelocity.add(
1435
+ lastMovement.position.clone().divideScalar(deltaTime)
1436
+ // The position delta is the result of one tick which is deltaTime seconds, so we need to divide by deltaTime to get the velocity per second
1437
+ );
1438
+ }
1439
+ this.lastFrameSurfaceState = null;
1346
1440
  }
1441
+ return lastMovement;
1347
1442
  }
1348
1443
  updateNetworkState() {
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
- );
1444
+ const characterQuaternion = this.character.getWorldQuaternion(this.tempQuaternion);
1355
1445
  this.networkState = {
1356
1446
  id: this.id,
1357
- position: positionUpdate,
1447
+ position: {
1448
+ x: this.character.position.x,
1449
+ y: this.character.position.y,
1450
+ z: this.character.position.z
1451
+ },
1358
1452
  rotation: { quaternionY: characterQuaternion.y, quaternionW: characterQuaternion.w },
1359
1453
  state: this.character.getCurrentAnimation()
1360
1454
  };
@@ -1445,7 +1539,7 @@ var CharacterManager = class {
1445
1539
  this.speakingCharacters = /* @__PURE__ */ new Map();
1446
1540
  this.group = new Group2();
1447
1541
  }
1448
- spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector38(), spawnRotation = new Euler()) {
1542
+ spawnCharacter(characterDescription, id, isLocal = false, spawnPosition = new Vector38(), spawnRotation = new Euler2()) {
1449
1543
  var _a;
1450
1544
  this.characterDescription = characterDescription;
1451
1545
  const character = new Character(
@@ -1526,16 +1620,16 @@ var CharacterManager = class {
1526
1620
  if (this.speakingCharacters.has(this.id)) {
1527
1621
  (_a = this.localCharacter.speakingIndicator) == null ? void 0 : _a.setSpeaking(this.speakingCharacters.get(this.id));
1528
1622
  }
1623
+ this.localController.update();
1624
+ if (this.timeManager.frame % 2 === 0) {
1625
+ this.sendUpdate(this.localController.networkState);
1626
+ }
1529
1627
  this.cameraOffsetTarget = this.cameraManager.targetDistance <= 0.4 ? 0.13 : 0;
1530
1628
  this.cameraOffset += ease(this.cameraOffsetTarget, this.cameraOffset, 0.1);
1531
1629
  const targetOffset = new Vector38(0, 0, this.cameraOffset);
1532
1630
  targetOffset.add(this.headTargetOffset);
1533
1631
  targetOffset.applyQuaternion(this.localCharacter.quaternion);
1534
1632
  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);
1538
- }
1539
1633
  for (const [id, update] of this.clientStates) {
1540
1634
  if (this.remoteCharacters.has(id) && this.speakingCharacters.has(id)) {
1541
1635
  const character = this.remoteCharacters.get(id);
@@ -4621,13 +4715,14 @@ import {
4621
4715
  Box3,
4622
4716
  Color as Color8,
4623
4717
  DoubleSide,
4624
- Euler as Euler2,
4718
+ Euler as Euler3,
4625
4719
  Group as Group5,
4626
4720
  Line3 as Line32,
4627
4721
  Matrix4 as Matrix46,
4628
4722
  Mesh as Mesh5,
4629
4723
  MeshBasicMaterial as MeshBasicMaterial3,
4630
- Ray,
4724
+ Quaternion as Quaternion6,
4725
+ Ray as Ray2,
4631
4726
  Vector3 as Vector314
4632
4727
  } from "three";
4633
4728
  import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
@@ -4639,33 +4734,41 @@ var CollisionsManager = class {
4639
4734
  this.tempVector = new Vector314();
4640
4735
  this.tempVector2 = new Vector314();
4641
4736
  this.tempVector3 = new Vector314();
4642
- this.tempRay = new Ray();
4737
+ this.tempQuaternion = new Quaternion6();
4738
+ this.tempRay = new Ray2();
4643
4739
  this.tempMatrix = new Matrix46();
4644
4740
  this.tempMatrix2 = new Matrix46();
4645
4741
  this.tempBox = new Box3();
4646
- this.tempEuler = new Euler2();
4742
+ this.tempEuler = new Euler3();
4647
4743
  this.tempSegment = new Line32();
4648
4744
  this.tempSegment2 = new Line32();
4649
4745
  this.collisionMeshState = /* @__PURE__ */ new Map();
4650
4746
  this.scene = scene;
4651
4747
  this.collisionTrigger = MMLCollisionTrigger.init();
4652
4748
  }
4653
- raycastFirstDistance(ray) {
4749
+ raycastFirst(ray) {
4654
4750
  let minimumDistance = null;
4655
- for (const [, value] of this.collisionMeshState) {
4656
- this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(value.matrix).invert());
4657
- const hit = value.meshBVH.raycastFirst(this.tempRay, DoubleSide);
4751
+ let minimumHit = null;
4752
+ let minimumNormal = new Vector314();
4753
+ for (const [, collisionMeshState] of this.collisionMeshState) {
4754
+ this.tempRay.copy(ray).applyMatrix4(this.tempMatrix.copy(collisionMeshState.matrix).invert());
4755
+ const hit = collisionMeshState.meshBVH.raycastFirst(this.tempRay, DoubleSide);
4658
4756
  if (hit) {
4659
4757
  this.tempSegment.start.copy(this.tempRay.origin);
4660
4758
  this.tempSegment.end.copy(hit.point);
4661
- this.tempSegment.applyMatrix4(value.matrix);
4759
+ this.tempSegment.applyMatrix4(collisionMeshState.matrix);
4662
4760
  const dist = this.tempSegment.distance();
4663
4761
  if (minimumDistance === null || dist < minimumDistance) {
4664
4762
  minimumDistance = dist;
4763
+ minimumHit = collisionMeshState;
4764
+ minimumNormal = (hit.normal ? minimumNormal.copy(hit.normal) : minimumNormal).applyQuaternion(this.tempQuaternion.setFromRotationMatrix(collisionMeshState.matrix)).normalize();
4665
4765
  }
4666
4766
  }
4667
4767
  }
4668
- return minimumDistance;
4768
+ if (minimumDistance === null || minimumNormal === null || minimumHit === null) {
4769
+ return null;
4770
+ }
4771
+ return [minimumDistance, minimumNormal, minimumHit];
4669
4772
  }
4670
4773
  createCollisionMeshState(group, trackCollisions) {
4671
4774
  const geometries = [];