feat(viewer): configurable walkthrough FOV with slider overlay#240
Open
b9llach wants to merge 1 commit intopascalorg:mainfrom
Open
feat(viewer): configurable walkthrough FOV with slider overlay#240b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach wants to merge 1 commit intopascalorg:mainfrom
Conversation
9d26100 to
629dfd9
Compare
reneruano95
pushed a commit
to reneruano95/editor
that referenced
this pull request
Apr 15, 2026
`packages/viewer/src/components/viewer/viewer-camera.tsx` hardcoded
`fov={50}` on the perspective camera. That framing is photogenic for
outside-looking-in orbit views, but feels telephoto once the user
enters walkthrough mode — standing inside a typical room the
scene reads as a narrow viewport instead of a walkable space, and
peripheral context disappears as soon as you start moving with WASD.
This adds a `walkthroughFov` setting to `useViewer` (default 85°,
clamped 50–110° by the setter, persisted under the existing
`viewer-preferences` key). `ViewerCamera` reads it and swaps the
perspective camera's FOV while `walkthroughMode` is true, reverting
to the 50° orbit framing when walkthrough exits. 85° is in the
standard FPS range (Quake/CS ≈ 90°, Valorant 103°) without the
near-wall fisheye distortion wider values produce. The clamp in the
setter keeps values from drifting into sniper-scope territory on the
low end or unusable fisheye on the high end.
A new `<WalkthroughFovSlider />` component exported from
`@pascal-app/viewer` provides runtime adjustment. It self-gates on
`walkthroughMode` (returns `null` otherwise) so consumers can mount
it unconditionally alongside their walkthrough UI. Styling is plain
inline CSS with a violet-500 accent so it works in any host app
without pulling in Tailwind / shadcn / design-token dependencies —
the viewer package has to stay editor-agnostic. Renders bottom-right
by default, overridable via `className`.
The editor's `FirstPersonOverlay` now mounts the slider alongside
the existing crosshair and controls hint.
One small coupling: `setFirstPersonMode` in
`packages/editor/src/store/use-editor.tsx` now mirrors its flag into
`useViewer.walkthroughMode`. The editor and viewer each had their
own walkthrough state — `isFirstPersonMode` in `useEditor`,
`walkthroughMode` in `useViewer` — and the desktop editor's
"walkthrough" button only flipped the editor-side flag. Without the
mirror, the FOV conditional in `viewer-camera.tsx` never tripped on
`/edit/[id]` because `walkthroughMode` stayed false the whole time.
Safe because `custom-camera-controls.tsx` already early-returns on
`isFirstPersonMode` before it would mount `WalkthroughControls`, so
there's no pointer-lock conflict between `FirstPersonControls` and
`WalkthroughControls`.
Caveats:
- Orthographic camera mode is unaffected — ortho has no fov concept.
- The read-only `/viewer/[id]` path is unchanged; the slider is
opt-in via the exported component, so consumers that don't mount
it see no visual or behavioural difference.
629dfd9 to
dd28b9c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
packages/viewer/src/components/viewer/viewer-camera.tsxhardcodesfov={50}on the perspective camera. That framing is photogenic for outside-looking-in orbit views, but feels telephoto once the user enters walkthrough mode — standing inside a typical room the scene reads as a narrow viewport instead of a walkable space, and peripheral context disappears as soon as you start moving with WASD. There was no way to adjust it without editing the source.Fix
Add a
walkthroughFovsetting onuseViewer(default 85°, clamped 50–110° by the setter, persisted under the existingviewer-preferenceskey).ViewerCamerareads it and swaps the perspective camera's FOV whilewalkthroughModeis true, reverting to the 50° orbit framing when walkthrough exits.85° is in the standard FPS range (Quake/CS ≈ 90°, Valorant 103°) without the near-wall fisheye distortion wider values produce. The clamp in the setter keeps values from drifting into sniper-scope territory on the low end or unusable fisheye on the high end.
A new
<WalkthroughFovSlider />component exported from@pascal-app/viewerprovides runtime adjustment:walkthroughMode(returnsnullotherwise), so consumers can mount it unconditionally alongside their walkthrough UI without their own visibility checksclassNameThe editor's
FirstPersonOverlaynow mounts the slider alongside the existing crosshair and controls hint.Shared walkthrough state
One small coupling:
setFirstPersonModeinpackages/editor/src/store/use-editor.tsxnow mirrors its flag intouseViewer.walkthroughMode. The editor and viewer each had their own walkthrough state —isFirstPersonModeinuseEditor,walkthroughModeinuseViewer— and the desktop editor's walkthrough button only flipped the editor-side flag. Without the mirror, the FOV conditional inviewer-camera.tsxnever tripped on `/edit/[id]` becausewalkthroughModestayed false the whole time.Safe because
custom-camera-controls.tsxalready early-returns onisFirstPersonModebefore it would mountWalkthroughControls, so there's no pointer-lock conflict betweenFirstPersonControlsandWalkthroughControls.Scope
/viewer/[id]path: the slider is opt-in via the exported component, so consumers that don't mount it see no visual or behavioural difference.How to test
bun dev, open `/edit/[id]` on an existing scene.bun check,bun check-types,bun run buildall clean on the touched packages.Checklist
bun devbun checkpasses on the touched files — verified viabiome checkat@biomejs/biome@^2.4.6)mainbranch