From 920d6ac1207076c093db4a53503f0bbe43394209 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 14:46:17 -0700 Subject: [PATCH] physics: sync camera pitch to movement packets and mount tilt during flight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add setMovementPitch() and isSwimming() to GameHandler - In the per-frame sync block, derive the pitch angle from the camera's forward vector (asin of the Z component) and write it to movementInfo.pitch whenever FLYING or SWIMMING flags are set — the server includes the pitch field in those packets, so sending 0 made other players see the character flying perfectly flat even when the camera was pitched - Also tilt the mount model (setMountPitchRoll) to match the flight direction during player-controlled flight, and reset to 0 when not flying --- include/game/game_handler.hpp | 6 ++++++ src/core/application.cpp | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index d715b17a..4cc7f45c 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1179,6 +1179,12 @@ public: bool isHovering() const { return (movementInfo.flags & static_cast(MovementFlags::HOVER)) != 0; } + bool isSwimming() const { + return (movementInfo.flags & static_cast(MovementFlags::SWIMMING)) != 0; + } + // Set the character pitch angle (radians) for movement packets (flight / swimming). + // Positive = nose up, negative = nose down. + void setMovementPitch(float radians) { movementInfo.pitch = radians; } void dismount(); // Taxi / Flight Paths diff --git a/src/core/application.cpp b/src/core/application.cpp index 78ec827d..64d98566 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1022,6 +1022,29 @@ void Application::update(float deltaTime) { renderer->getCameraController()->setWaterWalkActive(gameHandler->isWaterWalking()); renderer->getCameraController()->setFlyingActive(gameHandler->isPlayerFlying()); renderer->getCameraController()->setHoverActive(gameHandler->isHovering()); + + // Sync camera forward pitch to movement packets during flight / swimming. + // The server writes the pitch field when FLYING or SWIMMING flags are set; + // without this sync it would always be 0 (horizontal), causing other + // players to see the character flying flat even when pitching up/down. + if (gameHandler->isPlayerFlying() || gameHandler->isSwimming()) { + if (auto* cam = renderer->getCamera()) { + glm::vec3 fwd = cam->getForward(); + float len = glm::length(fwd); + if (len > 1e-4f) { + float pitchRad = std::asin(std::clamp(fwd.z / len, -1.0f, 1.0f)); + gameHandler->setMovementPitch(pitchRad); + // Tilt the mount/character model to match flight direction + // (taxi flight uses setTaxiOrientationCallback for this instead) + if (gameHandler->isPlayerFlying() && gameHandler->isMounted()) { + renderer->setMountPitchRoll(pitchRad, 0.0f); + } + } + } + } else if (gameHandler->isMounted()) { + // Reset mount pitch when not flying + renderer->setMountPitchRoll(0.0f, 0.0f); + } } bool onTaxi = gameHandler &&