feat(viewer): widen first-person walkthrough FOV to 60°

The walkthrough rode the default 50° orbit camera, which feels cramped on
foot. Both walkthrough controllers (baked GlbWalkthroughController and the
parametric WalkthroughControls fallback) now set a shared WALKTHROUGH_FOV = 60
on enter and restore the prior FOV on exit, leaving orbit framing untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-06-26 15:51:12 -04:00
co-authored by Claude Opus 4.8
parent 69c483b5f2
commit 70acf894d1
2 changed files with 39 additions and 1 deletions
@@ -15,6 +15,7 @@ import {
Mesh,
MeshBasicMaterial,
type Object3D,
type PerspectiveCamera,
Quaternion,
Vector3,
} from 'three'
@@ -24,6 +25,7 @@ import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2'
import { SCENE_LAYER } from '../../lib/layers'
import useViewer from '../../store/use-viewer'
import BVHEcctrl, { type BVHEcctrlApi, type MovementInput } from './bvh-ecctrl'
import { WALKTHROUGH_FOV } from './walkthrough-controls'
// Eye/capsule geometry mirrors the editor's first-person controller so the
// baked walkthrough feels identical. The capsule centre sits below the eye; the
@@ -264,6 +266,22 @@ export function GlbWalkthroughController({ url }: { url: string }) {
}
}, [])
// Widen FOV while walking; the baked walkthrough rides the default 50° orbit
// camera, which feels cramped on foot. Keyed on `camera` so it re-applies if
// the instance swaps (e.g. the ortho→perspective switch above), restoring the
// prior FOV on exit.
useEffect(() => {
const cam = camera as PerspectiveCamera
if (!cam.isPerspectiveCamera) return
const prevFov = cam.fov
cam.fov = WALKTHROUGH_FOV
cam.updateProjectionMatrix()
return () => {
cam.fov = prevFov
cam.updateProjectionMatrix()
}
}, [camera])
useEffect(() => {
worldRef.current = world
if (world) {
@@ -3,12 +3,18 @@
import { PointerLockControls } from '@react-three/drei'
import { useFrame, useThree } from '@react-three/fiber'
import { useCallback, useEffect, useRef } from 'react'
import { Vector3 } from 'three'
import { type PerspectiveCamera, Vector3 } from 'three'
import useViewer from '../../store/use-viewer'
const MOVE_SPEED = 5
const EYE_HEIGHT = 1.6
// First-person FOV. The orbit camera is 50° (set on the Canvas), which feels
// cramped on foot; ~60° vertical (~90° horizontal at 16:9) restores peripheral
// awareness without wide-angle distortion. Applied only while walking — both
// walkthrough controllers read this and restore the orbit FOV on exit.
export const WALKTHROUGH_FOV = 60
const _direction = new Vector3()
const _forward = new Vector3()
const _right = new Vector3()
@@ -26,6 +32,20 @@ export const WalkthroughControls = () => {
}
}, [walkthroughMode, camera])
// Widen FOV while walking; restore the orbit FOV on exit.
useEffect(() => {
if (!walkthroughMode) return
const cam = camera as PerspectiveCamera
if (!cam.isPerspectiveCamera) return
const prevFov = cam.fov
cam.fov = WALKTHROUGH_FOV
cam.updateProjectionMatrix()
return () => {
cam.fov = prevFov
cam.updateProjectionMatrix()
}
}, [walkthroughMode, camera])
// Keyboard handlers
useEffect(() => {
if (!walkthroughMode) return