fix: improve world viewer camera targeting
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-04-30 17:38:13 +08:00
parent 987ab3f6a0
commit 57b7292269

View File

@ -286,11 +286,13 @@ function positionCameraInsideWorld({
controls,
splatMesh,
groundPlaneOffset,
firstPerson = false,
}: {
camera: THREE.Camera;
controls: { target?: THREE.Vector3; update?: () => void } | null;
splatMesh: SparkSplatMesh;
groundPlaneOffset: number | null;
firstPerson?: boolean;
}): WorldCameraPose | null {
if (!(camera instanceof THREE.PerspectiveCamera)) return null;
@ -364,28 +366,30 @@ function positionCameraInsideWorld({
lookDirection.copy(horizontalAxis).multiplyScalar(-1);
}
lookDirection.normalize();
const targetPosition = cameraPosition
const nearTargetPosition = cameraPosition
.clone()
.addScaledVector(lookDirection, THREE.MathUtils.clamp(majorSpan * 0.24, 0.75, 2.4));
targetPosition.y = THREE.MathUtils.clamp(
nearTargetPosition.y = THREE.MathUtils.clamp(
eyeY - THREE.MathUtils.clamp(size.y * 0.025, 0.04, 0.18),
minEyeY <= maxEyeY ? minEyeY : worldBounds.min.y,
minEyeY <= maxEyeY ? maxEyeY : worldBounds.max.y
);
targetPosition.x = clampInsideAxis(
targetPosition.x,
nearTargetPosition.x = clampInsideAxis(
nearTargetPosition.x,
worldBounds.min.x,
worldBounds.max.x,
Math.max(size.x * 0.08, 0.18),
center.x
);
targetPosition.z = clampInsideAxis(
targetPosition.z,
nearTargetPosition.z = clampInsideAxis(
nearTargetPosition.z,
worldBounds.min.z,
worldBounds.max.z,
Math.max(size.z * 0.08, 0.18),
center.z
);
const orbitTargetPosition = anchor.clone();
const targetPosition = firstPerson ? nearTargetPosition : orbitTargetPosition;
camera.position.copy(cameraPosition);
camera.lookAt(targetPosition);
@ -879,6 +883,7 @@ function WorldSplatScene({
lastCameraPoseRef.current = positionCameraInsideWorld({
camera,
controls: orbitControlsRef.current,
firstPerson,
groundPlaneOffset,
splatMesh,
});
@ -912,7 +917,7 @@ function WorldSplatScene({
}
splatMesh.dispose();
};
}, [camera, groundPlaneOffset, onLoadStateChange, sparkRenderer, splatMesh]);
}, [camera, firstPerson, groundPlaneOffset, onLoadStateChange, sparkRenderer, splatMesh]);
useEffect(() => {
if (!hasAppliedInitialViewRef.current) return;
@ -921,10 +926,11 @@ function WorldSplatScene({
lastCameraPoseRef.current = positionCameraInsideWorld({
camera,
controls: orbitControlsRef.current,
firstPerson,
groundPlaneOffset,
splatMesh,
});
}, [camera, groundPlaneOffset, resetViewToken, splatMesh]);
}, [camera, firstPerson, groundPlaneOffset, resetViewToken, splatMesh]);
useEffect(() => {
if (!hasAppliedInitialViewRef.current) {
@ -932,10 +938,11 @@ function WorldSplatScene({
return;
}
if (!lastFirstPersonRef.current && firstPerson) {
if (lastFirstPersonRef.current !== firstPerson) {
lastCameraPoseRef.current = positionCameraInsideWorld({
camera,
controls: orbitControlsRef.current,
firstPerson,
groundPlaneOffset,
splatMesh,
});