ifcviewer: first-person / fly view — share the desktop fly camera with web

DRY the fly camera into ViewportCore so desktop and web share identical math:
- flyMove(fwd,back,right,left,up,down,boost,dt): WASD in the view plane, QE
  along world +Z, forward = eye→target (no snap after orbiting), Shift = 5x,
  dt clamped to 0.1s. flyLook(dx,dy): turn in place (yaw/pitch, eye pinned,
  0.2 deg/px, pitch ±89.9). flyAdjustSpeed(notches): wheel scales speed x1.25.
  fly_move_speed_ + orbitEye now live in the core (orbitEye's duplicate removed
  from ViewportWindow).
- Desktop ViewportWindow: fpsIntegrate / mouse-look / wheel-speed call the core
  methods; behaviour unchanged, BonsaiViewer builds clean.
- Web main_web: Shift+F (or the Fly toolbar button) enters and pointer-locks the
  canvas; W/A/S/D/Q/E + Shift are held-tracked (keydown/keyup) and integrated
  each RAF frame with wall-clock dt; pointer-lock mouse deltas drive flyLook;
  wheel tunes speed. Exit on Esc OR a canvas click (matches desktop) — a
  pointerlockchange handler catches the browser eating the first Esc to release
  the lock (so a single Esc exits), guarded by fly_locked so a denied lock on
  entry doesn't insta-exit. Fly button reflects/ syncs active state.

Verified: web enter→WASD moves→click/Esc exits, 0 GPU errors; 113/113 desktop
core + 6/6 web smoke; desktop app builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-07-02 14:04:35 +10:00
parent 0b8c787ac0
commit f9acf8be3a
7 changed files with 217 additions and 113 deletions
+22
View File
@@ -217,6 +217,25 @@ public:
void panBy(float dx_px, float dy_px, int viewport_height_px);
void dollyBy(float notches);
// ---- First-person / fly navigation --------------------------------------
//
// Shared fly-camera math (desktop + web). The HOST owns the fly-mode flag,
// held-key tracking, cursor/pointer-lock, and per-frame timing; it calls
// these each frame while flying. Behaviour is identical across platforms.
//
// flyMove: WASD moves in the view plane, QE rises/falls along world +Z;
// forward = eye→target so it doesn't snap after orbiting. `boost`
// (Shift) is 5x. Speed is metres/second (flyAdjustSpeed tunes it);
// `dt` seconds is clamped to 0.1 so a stall can't warp the camera.
// flyLook: mouse-look — turn the camera in place (yaw/pitch) with the eye
// pinned; pixel deltas, 0.2 deg/px, pitch clamped to ±89.9.
// flyAdjustSpeed: wheel scales the move speed (x1.25 per notch, clamped).
void flyMove(bool fwd, bool back, bool right, bool left,
bool up, bool down, bool boost, float dt_seconds);
void flyLook(float dx_px, float dy_px);
void flyAdjustSpeed(float notches);
float flySpeed() const { return fly_move_speed_; }
void toggleProjection();
bool projectionOrtho() const { return projection_ortho_; }
std::string cameraString() const;
@@ -1116,6 +1135,9 @@ private:
float camera_fov_y_deg_ = 45.0f;
float camera_near_ = 0.1f;
float camera_far_ = 10000.0f;
// Fly-camera move speed (m/s), wheel-adjustable via flyAdjustSpeed. Shared
// by desktop + web fly mode; the mode flag itself lives in each host.
float fly_move_speed_ = 5.0f;
// Perspective by default; toggleProjection (P key) flips this. When
// true, buildViewProj uses an orthographic matrix sized by
// camera_distance_ × tan(fov/2) so toggling looks like a smooth