fix(camera): repair and refactor the camera protocol stack#134
fix(camera): repair and refactor the camera protocol stack#134gold-silver-copper wants to merge 25 commits into
Conversation
Add an RGP extension to support controlling the camera Remove unused parameters
# Conflicts: # src/keyboard.rs # src/systems.rs
- compose the camera orbit as YXZ so pitch stays independent of yaw at every orbit angle, matching the inverse of the old plane rotation - carry morph, pose, and roll continuity through interrupted Mobius transitions and let the toggle turn an exit back into an enter - animate Mobius entry on slot activation and on protocol mode changes, honoring stored preset scales exactly instead of the keyboard zoom floor, and keep the settled strip when switching between Mobius slots - respect the protocol scale range in mouse-wheel zoom and reset camera drag state when the window loses focus - render Kitty inline planes with NoFrustumCulling and stop re-uploading terminal meshes every frame in settled Mobius mode - reject value-less camera protocol fields atomically, fix stale camera rustdoc, and order sync_inline_objects after the camera Transition set
- reset selection drags and forwarded mouse-button state on window focus loss alongside camera drags, so no held-button state survives a release delivered to another window - hand an in-flight Mobius strip animation to the newly activated slot continuously instead of snapping it to the settled strip - shrink a resumed transition's morph phase in proportion to the remaining distance, keeping morph velocity continuous - pass the Mobius enter source as TerminalMobiusSource and the zoom floor as a named enum instead of a raw f32, and document why the keyboard floor cannot reach protocol or activation enters - pin the keyboard enter-finish zoom floor write-back and Mobius wheel zoom routing with tests, and correct the orbit test tolerance comment
- treat a re-enter toward identical targets as a no-op so repeated slot activations cannot reset the transition clock and hold input gating open indefinitely - let a resumed morph advance during the camera phase instead of re-freezing for the hold window on every hand-off - synthesize the release events the PTY application is still owed when the window loses focus mid-drag, and clear the motion dedup cell - make apply_wheel_zoom the single owner of the Mobius-animating wheel gate, correct the zoom-floor comments (the floor is recomputed from the begin_enter argument on every call; stop() only controls start continuity), and drop the dead enter_morph_progress accessor
Manual test planSince the automated verification covered fmt/clippy/tests plus a headless smoke run on Mesa's software Vulkan renderer, this checklist focuses on what that run couldn't cover: native GPU drivers (Metal/Vulkan), interactive feel, and real keyboard handling. Conventions
Stage 0 — Build and launchcd <repo>
cargo run --release✅ Launches on the native GPU backend (Metal on macOS, Vulkan on Linux) without panics. Stage 1 — The original regression (ortho scale vs. perspective FOV)
Stage 2 — Keyboard matrix
Stage 3 — Mouse feel, zoom floor, and the ratchetIn ortho mode:
Stage 4 — Slots and per-slot persistence
Stage 5 — Transition stress
Stage 6 — Protocol contract and malformed input
Stage 6b — Degree-based
|
The c command previously overloaded scale as orthographic scale in Ortho/Mobius and vertical FOV in radians in Persp, while rotations were degrees. Since the camera contract has not shipped, fix the wire format before it becomes compatibility surface: - scale now always means orthographic projection scale (min 0.01) - new fov field carries vertical perspective FOV in degrees, converted to radians at the parse boundary and validated against the existing 0.05..pi-0.05 clamp; internal storage stays radians - every angle on the wire (rx, ry, rz, fov) is degrees - one command may set scale and fov together regardless of type, mirroring the independent per-slot storage - out-of-range numeric scale/fov is ignored field-level, other fields still apply; non-finite or valueless fov rejects the whole command
- document the degree-to-radian parse boundary on RgpCameraSettings, TerminalCameraUpdate, and the FOV clamp constants so no call site can mistake which unit a field holds - state in the protocol doc that an out-of-range scale/fov does not block set=1 activation, add a perspective example command, and extend the example session with camera control - note the preset unit conventions in the big_rat example - replace rustdoc links to private items with plain code spans; cargo doc is now warning-free
|
I verified all the behavior manually and seems to be good to goo |
|
Two follow-ups from re-reading the #118 conversation against this PR — neither is a blocker, but both deserve an explicit decision on the record rather than passing silently.
|
Parent stack
Stacked on #118; review and merge #118 first. Once #118 is merged, this PR will
contain only the architectural and corrective follow-up.
This PR is stacked on top of #118. It preserves PR #118's current head
(
22d9bbad5dcc6ae83aa37e143599b5f011f9aaef) and all 13 original commits inits ancestry, then merges current
orhun/ratty:mainwithout rebasing orrewriting those commits.
What changed
projection values, independent orthographic scale and perspective FOV,
explicit orthographic/perspective markers, and a tested activation/clear-owner
matrix
TerminalCameraSlotssource of truth, transient mouse interaction state,queued RGP updates, activation messages, and focused ordered systems
active Bevy camera while avoiding writes to inactive projections
keeps camera-only changes outside the direct Vello terminal redraw/material
pipeline introduced by Render terminal texture with direct Vello path #107
main0..9, partial updates,immediate
set=1activation, finite numeric validation,pzdolly,rx/ry/rz = pitch/yaw/rollin degrees, and independent valid scale/FOVhandling
Ctrl+Alt+Shift+0..9camera-slot bindings withoutcolliding with the existing
Ctrl+Alt+0font resetbig_ratexampleReview follow-up
0.01, previously0.1) and turns the4.0interactive cap into a one-way ratchet, so larger application-set scales are preserved instead of being snapped down by the first wheel tickRoot cause
PR #118's conflict resolution combined a perspective projection with a
negative near plane and indirect camera identification. A single zoom value was
also interpreted as both orthographic scale and perspective FOV, so an ordinary
orthographic zoom-out became a nearly-pi perspective FOV and reduced the plane
to a sliver. The implementation copied presets into live resources in keyboard
handling, copied user changes back from the brightness system, and inverted
several flat/3D visibility branches while adapting to the #107/#121 rendering
changes. The result was invalid perspective state, ambiguous clear ownership,
and camera state that depended on unrelated system execution.
Architecture
Before this follow-up, PTY parsing mutated slots, keyboard handling copied a
slot into live camera resources, mouse input mutated those copies, and the
brightness system wrote the copies back. Mode and pose ownership therefore
depended on unrelated system order.
After this follow-up,
TerminalCameraSlotsis the only persistent source ofmode and pose. Protocol updates and activation are queued, mouse drag state is
transient, Mobius transitions are explicit and slot-aware, and a final camera
system synchronizes only changed active values to marked Bevy entities. This is
necessary alongside #107 because camera changes must remain separate from its
dirty-gated Vello texture upload and material preparation path.
Protocol decisions
The
ccommand has not shipped, so this PR favors a coherent contract over itsWIP behavior. Rotations are degrees, all three translation/rotation axes are
implemented, orthographic scale and perspective FOV persist independently,
and malformed commands are consumed but ignored safely. This intentionally
changes the WIP axis/unit behavior from #118; there is no released compatibility
surface to preserve.
Verification
Passed locally:
cargo fmt --all -- --checkcargo check --all-targetscargo clippy --all-targets -- -D warningscargo test --all-targets(49 tests passed)cargo fmt --manifest-path widget/Cargo.toml --all -- --checkcargo check --manifest-path widget/Cargo.toml --all-targetscargo clippy --manifest-path widget/Cargo.toml --all-targets -- -D warningscargo test --manifest-path widget/Cargo.toml --all-targetsgit merge-base --is-ancestor refs/remotes/origin/pr-118 HEADgit diff --check origin/main...HEADManual smoke test
Passed using the real Ratty and
big_ratbinaries under Xvfb with Mesa'sllvmpipe Vulkan adapter. The smoke run
completed successfully and uploaded 16 screenshots plus renderer logs and pixel
statistics.
back to Ortho; the terminal and inline model remained rendered in every mode
Mobius exit, orthographic, and flat transitions
wheel zoom; the resulting camera framing changed without losing the terminal
Flat; captures changed from
960x620to1100x700and remained nonblankSpinyMouse.glbRGP object in flat and3D modes; logs also confirm the embedded cursor model loaded, and captures
show it on the 3D terminal surface
big_rat; the visible value changed from1.0to1.1, exercising PTY output, terminal texture redraw, and RGPmaterial refresh
panic or GPU loss occurred, and Ratty exited cleanly after the demo quit
This validates the software Vulkan path. Native Metal/DX12 driver behavior is
still left to normal platform testing, but no camera-specific blocker remains.
Related work
Update (2026-07-19): degree-based
fovfieldCommit 40c0066 replaces the dual-purpose
scalefield with a split contract,since the
ccommand has not shipped and this is the last cheap moment to fixthe wire format:
scalenow always means orthographic projection scale (minimum0.01).fovfield carries the vertical perspective field of view indegrees, valid strictly inside the equivalent of
0.05topi - 0.05radians (about
2.87to177.13degrees). Degrees are converted to radiansat the parse boundary; internal storage and the Bevy projection wiring are
unchanged.
rx,ry,rz,fov) is now degrees — one unitrule, no exceptions.
scaleandfovtogether regardless of the current orrequested
type, mirroring the independent per-slot storage.scaleorfovis ignored while the rest of a valid command still applies; non-finiteor valueless
fovrejects the whole command.The protocol documentation, the
big_ratperspective preset (scale=0.85radians →
fov=50degrees), and the regression tests were updated to match.A fresh GPU smoke run
on this commit passed with all 16 captures healthy (4,391-8,567 colors, nonzero
deviation, no panics); the perspective capture now exercises the degree-based
fov=50preset end to end.Commit c3cef5d follows up with documentation polish: the degree-to-radian
parse boundary is now documented on every struct field and constant it touches,
the protocol doc gained a perspective example, camera steps in the example
session, and an explicit note that out-of-range
scale/fovvalues do notblock
set=1activation, andcargo docis warning-free after replacingrustdoc links to private items with plain code spans.