From 598c4b724d0a5c4300a4fb82c52c0a3ce279cd94 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sat, 25 Apr 2026 18:59:35 +0200 Subject: [PATCH] Implement camera collision smoothing and position resolution --- src/runtime-three/runtime-host.ts | 77 ++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/runtime-three/runtime-host.ts b/src/runtime-three/runtime-host.ts index b52bcee2..e1063d17 100644 --- a/src/runtime-three/runtime-host.ts +++ b/src/runtime-three/runtime-host.ts @@ -2180,8 +2180,68 @@ export class RuntimeHost { }; } + private resetRuntimeCameraCollisionSmoothing() { + this.smoothedRuntimeCameraCollisionDistance = null; + } + + private smoothRuntimeCameraCollisionPosition( + pivot: { x: number; y: number; z: number }, + desiredPosition: Vector3, + resolvedPosition: { x: number; y: number; z: number }, + dt: number + ): Vector3 { + this.cameraCollisionDirection.set( + desiredPosition.x - pivot.x, + desiredPosition.y - pivot.y, + desiredPosition.z - pivot.z + ); + + const desiredDistance = this.cameraCollisionDirection.length(); + + if (desiredDistance <= CAMERA_COLLISION_DISTANCE_EPSILON) { + this.resetRuntimeCameraCollisionSmoothing(); + return new Vector3( + resolvedPosition.x, + resolvedPosition.y, + resolvedPosition.z + ); + } + + const resolvedDistance = Math.hypot( + resolvedPosition.x - pivot.x, + resolvedPosition.y - pivot.y, + resolvedPosition.z - pivot.z + ); + const previousDistance = this.smoothedRuntimeCameraCollisionDistance; + const nextDistance = + previousDistance === null || + dt <= 0 || + resolvedDistance < previousDistance + ? resolvedDistance + : dampScalar( + previousDistance, + resolvedDistance, + CAMERA_COLLISION_RECOVERY_SPEED, + dt + ); + const clampedDistance = Math.min( + Math.max(0, nextDistance), + Math.min(resolvedDistance, desiredDistance) + ); + + this.smoothedRuntimeCameraCollisionDistance = clampedDistance; + this.cameraCollisionDirection.normalize().multiplyScalar(clampedDistance); + + return new Vector3( + pivot.x + this.cameraCollisionDirection.x, + pivot.y + this.cameraCollisionDirection.y, + pivot.z + this.cameraCollisionDirection.z + ); + } + private resolveCollisionAdjustedCameraPose( - pose: RuntimeCameraPose + pose: RuntimeCameraPose, + dt = 0 ): RuntimeCameraPose { if ( pose.collisionPivot === undefined || @@ -2189,6 +2249,7 @@ export class RuntimeHost { pose.collisionRadius === undefined || pose.collisionRadius === null ) { + this.resetRuntimeCameraCollisionSmoothing(); return pose; } @@ -2207,21 +2268,23 @@ export class RuntimeHost { ); if (resolvedPosition === undefined) { + this.resetRuntimeCameraCollisionSmoothing(); return pose; } return { ...pose, - position: new Vector3( - resolvedPosition.x, - resolvedPosition.y, - resolvedPosition.z + position: this.smoothRuntimeCameraCollisionPosition( + pose.collisionPivot, + pose.position, + resolvedPosition, + dt ) }; } - private applyCameraPose(pose: RuntimeCameraPose) { - const resolvedPose = this.resolveCollisionAdjustedCameraPose(pose); + private applyCameraPose(pose: RuntimeCameraPose, dt = 0) { + const resolvedPose = this.resolveCollisionAdjustedCameraPose(pose, dt); this.camera.position.copy(resolvedPose.position); this.camera.lookAt(resolvedPose.lookTarget);