physics: sync camera pitch to movement packets and mount tilt during flight

- 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
This commit is contained in:
Kelsi 2026-03-10 14:46:17 -07:00
parent 132598fc88
commit 920d6ac120
2 changed files with 29 additions and 0 deletions

View file

@ -1179,6 +1179,12 @@ public:
bool isHovering() const {
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::HOVER)) != 0;
}
bool isSwimming() const {
return (movementInfo.flags & static_cast<uint32_t>(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

View file

@ -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 &&