fix: stabilize world canvas navigation controls
This commit is contained in:
parent
fca2552412
commit
ce03c85811
@ -54,6 +54,11 @@ type SparkRevealProfile = {
|
||||
opacity: number;
|
||||
};
|
||||
|
||||
type WorldCameraPose = {
|
||||
cameraPosition: THREE.Vector3;
|
||||
targetPosition: THREE.Vector3;
|
||||
};
|
||||
|
||||
type World3DViewerProps = {
|
||||
mode: World3DViewMode;
|
||||
worldUrl?: string | null;
|
||||
@ -285,12 +290,12 @@ function positionCameraInsideWorld({
|
||||
controls: { target?: THREE.Vector3; update?: () => void } | null;
|
||||
splatMesh: SparkSplatMesh;
|
||||
groundPlaneOffset: number | null;
|
||||
}) {
|
||||
if (!(camera instanceof THREE.PerspectiveCamera)) return;
|
||||
}): WorldCameraPose | null {
|
||||
if (!(camera instanceof THREE.PerspectiveCamera)) return null;
|
||||
|
||||
splatMesh.updateMatrixWorld(true);
|
||||
const worldBounds = splatMesh.getBoundingBox(true).clone().applyMatrix4(splatMesh.matrixWorld);
|
||||
if (worldBounds.isEmpty()) return;
|
||||
if (worldBounds.isEmpty()) return null;
|
||||
|
||||
const center = worldBounds.getCenter(new THREE.Vector3());
|
||||
const size = worldBounds.getSize(new THREE.Vector3());
|
||||
@ -299,10 +304,16 @@ function positionCameraInsideWorld({
|
||||
const majorSpan = Math.max(horizontalAxis.z === 1 ? size.z : size.x, 1.4);
|
||||
const marginX = Math.max(size.x * 0.12, 0.35);
|
||||
const marginZ = Math.max(size.z * 0.12, 0.35);
|
||||
const eyeY =
|
||||
const rawEyeY =
|
||||
groundPlaneOffset !== null
|
||||
? groundPlaneOffset + 1.58
|
||||
: center.y - size.y * 0.08 + Math.max(size.y * 0.22, 0.9);
|
||||
const minEyeY = worldBounds.min.y + Math.max(size.y * 0.1, 0.55);
|
||||
const maxEyeY = worldBounds.max.y - Math.max(size.y * 0.14, 0.38);
|
||||
const eyeY =
|
||||
minEyeY <= maxEyeY
|
||||
? THREE.MathUtils.clamp(rawEyeY, minEyeY, maxEyeY)
|
||||
: center.y;
|
||||
const anchor = new THREE.Vector3(center.x, eyeY, center.z);
|
||||
const cameraPosition = anchor
|
||||
.clone()
|
||||
@ -333,6 +344,11 @@ function positionCameraInsideWorld({
|
||||
controls.target.copy(targetPosition);
|
||||
controls.update?.();
|
||||
}
|
||||
|
||||
return {
|
||||
cameraPosition: cameraPosition.clone(),
|
||||
targetPosition: targetPosition.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
function WorldEmptyState({
|
||||
@ -731,7 +747,10 @@ function WorldSplatScene({
|
||||
const renderer = useThree((state) => state.gl);
|
||||
const orbitControlsRef = useRef<any>(null);
|
||||
const hasAppliedInitialViewRef = useRef(false);
|
||||
const lastCameraPoseRef = useRef<WorldCameraPose | null>(null);
|
||||
const lastResetViewTokenRef = useRef<number | undefined>(resetViewToken);
|
||||
const revealCompletedRef = useRef(false);
|
||||
const [controlsMountVersion, setControlsMountVersion] = useState(0);
|
||||
const [particleCloudData, setParticleCloudData] = useState<SplatParticleCloudData | null>(null);
|
||||
const semanticFacts = useMemo(
|
||||
() => getSemanticFacts(semanticsMetadata),
|
||||
@ -758,6 +777,10 @@ function WorldSplatScene({
|
||||
typeof semanticsMetadata?.ground_plane_offset === "number"
|
||||
? -semanticsMetadata.ground_plane_offset
|
||||
: null;
|
||||
const handleOrbitControlsRef = React.useCallback((controls: any | null) => {
|
||||
orbitControlsRef.current = controls;
|
||||
setControlsMountVersion((version) => version + 1);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
sparkRenderer.frustumCulled = false;
|
||||
@ -780,6 +803,8 @@ function WorldSplatScene({
|
||||
|
||||
useEffect(() => {
|
||||
hasAppliedInitialViewRef.current = false;
|
||||
lastCameraPoseRef.current = null;
|
||||
lastResetViewTokenRef.current = resetViewToken;
|
||||
revealCompletedRef.current = false;
|
||||
setParticleCloudData(null);
|
||||
splatMesh.frustumCulled = false;
|
||||
@ -794,7 +819,7 @@ function WorldSplatScene({
|
||||
if (cancelled) return;
|
||||
window.requestAnimationFrame(() => {
|
||||
if (cancelled || hasAppliedInitialViewRef.current) return;
|
||||
positionCameraInsideWorld({
|
||||
lastCameraPoseRef.current = positionCameraInsideWorld({
|
||||
camera,
|
||||
controls: orbitControlsRef.current,
|
||||
groundPlaneOffset,
|
||||
@ -834,7 +859,9 @@ function WorldSplatScene({
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasAppliedInitialViewRef.current) return;
|
||||
positionCameraInsideWorld({
|
||||
if (lastResetViewTokenRef.current === resetViewToken) return;
|
||||
lastResetViewTokenRef.current = resetViewToken;
|
||||
lastCameraPoseRef.current = positionCameraInsideWorld({
|
||||
camera,
|
||||
controls: orbitControlsRef.current,
|
||||
groundPlaneOffset,
|
||||
@ -842,6 +869,18 @@ function WorldSplatScene({
|
||||
});
|
||||
}, [camera, groundPlaneOffset, resetViewToken, splatMesh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!orbitControlsRef.current || !lastCameraPoseRef.current) return;
|
||||
|
||||
camera.position.copy(lastCameraPoseRef.current.cameraPosition);
|
||||
camera.lookAt(lastCameraPoseRef.current.targetPosition);
|
||||
if (camera instanceof THREE.PerspectiveCamera) {
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
orbitControlsRef.current.target?.copy(lastCameraPoseRef.current.targetPosition);
|
||||
orbitControlsRef.current.update?.();
|
||||
}, [camera, controlsMountVersion]);
|
||||
|
||||
useFrame(() => {
|
||||
const phase = revealPhaseRef.current;
|
||||
const revealElapsed =
|
||||
@ -936,15 +975,16 @@ function WorldSplatScene({
|
||||
enablePan={false}
|
||||
enableRotate
|
||||
enableZoom
|
||||
makeDefault
|
||||
maxDistance={16}
|
||||
key="world-splat-orbit"
|
||||
makeDefault
|
||||
mouseButtons={{
|
||||
LEFT: THREE.MOUSE.ROTATE,
|
||||
MIDDLE: THREE.MOUSE.DOLLY,
|
||||
RIGHT: THREE.MOUSE.ROTATE,
|
||||
}}
|
||||
minDistance={0.5}
|
||||
ref={orbitControlsRef}
|
||||
ref={handleOrbitControlsRef}
|
||||
/>
|
||||
) : (
|
||||
<OrbitControls
|
||||
@ -953,12 +993,14 @@ function WorldSplatScene({
|
||||
enablePan={false}
|
||||
enableRotate
|
||||
enableZoom={false}
|
||||
key="world-splat-first-person"
|
||||
makeDefault
|
||||
mouseButtons={{
|
||||
LEFT: THREE.MOUSE.ROTATE,
|
||||
MIDDLE: THREE.MOUSE.ROTATE,
|
||||
RIGHT: THREE.MOUSE.ROTATE,
|
||||
}}
|
||||
ref={orbitControlsRef}
|
||||
ref={handleOrbitControlsRef}
|
||||
/>
|
||||
)}
|
||||
<Html className="pointer-events-none" fullscreen>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user