Merge pull request #445 from pascalorg/polish/viewer-baking

feat(viewer): walkthrough FOV 60° + refresh ceiling-fan model
This commit is contained in:
Wassim SAMAD
2026-06-26 15:58:06 -04:00
committed by GitHub
3 changed files with 39 additions and 1 deletions
Binary file not shown.
@@ -15,6 +15,7 @@ import {
Mesh, Mesh,
MeshBasicMaterial, MeshBasicMaterial,
type Object3D, type Object3D,
type PerspectiveCamera,
Quaternion, Quaternion,
Vector3, Vector3,
} from 'three' } from 'three'
@@ -24,6 +25,7 @@ import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2'
import { SCENE_LAYER } from '../../lib/layers' import { SCENE_LAYER } from '../../lib/layers'
import useViewer from '../../store/use-viewer' import useViewer from '../../store/use-viewer'
import BVHEcctrl, { type BVHEcctrlApi, type MovementInput } from './bvh-ecctrl' 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 // Eye/capsule geometry mirrors the editor's first-person controller so the
// baked walkthrough feels identical. The capsule centre sits below the eye; 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(() => { useEffect(() => {
worldRef.current = world worldRef.current = world
if (world) { if (world) {
@@ -3,12 +3,18 @@
import { PointerLockControls } from '@react-three/drei' import { PointerLockControls } from '@react-three/drei'
import { useFrame, useThree } from '@react-three/fiber' import { useFrame, useThree } from '@react-three/fiber'
import { useCallback, useEffect, useRef } from 'react' import { useCallback, useEffect, useRef } from 'react'
import { Vector3 } from 'three' import { type PerspectiveCamera, Vector3 } from 'three'
import useViewer from '../../store/use-viewer' import useViewer from '../../store/use-viewer'
const MOVE_SPEED = 5 const MOVE_SPEED = 5
const EYE_HEIGHT = 1.6 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 _direction = new Vector3()
const _forward = new Vector3() const _forward = new Vector3()
const _right = new Vector3() const _right = new Vector3()
@@ -26,6 +32,20 @@ export const WalkthroughControls = () => {
} }
}, [walkthroughMode, camera]) }, [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 // Keyboard handlers
useEffect(() => { useEffect(() => {
if (!walkthroughMode) return if (!walkthroughMode) return