diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 278fa7c5..3f795268 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -501,8 +501,8 @@ public: using TaxiPrecacheCallback = std::function&)>; void setTaxiPrecacheCallback(TaxiPrecacheCallback cb) { taxiPrecacheCallback_ = std::move(cb); } - // Taxi orientation callback (for mount rotation) - using TaxiOrientationCallback = std::function; + // Taxi orientation callback (for mount rotation: yaw, pitch, roll in radians) + using TaxiOrientationCallback = std::function; void setTaxiOrientationCallback(TaxiOrientationCallback cb) { taxiOrientationCallback_ = std::move(cb); } // Callback for when taxi flight is about to start (after mounting delay, before movement begins) diff --git a/include/rendering/renderer.hpp b/include/rendering/renderer.hpp index ae3a54cd..41adb8a7 100644 --- a/include/rendering/renderer.hpp +++ b/include/rendering/renderer.hpp @@ -129,6 +129,7 @@ public: // Mount rendering void setMounted(uint32_t mountInstId, float heightOffset); void setTaxiFlight(bool onTaxi) { taxiFlight_ = onTaxi; } + void setMountPitchRoll(float pitch, float roll) { mountPitch_ = pitch; mountRoll_ = roll; } void clearMount(); bool isMounted() const { return mountInstanceId_ != 0; } @@ -274,6 +275,8 @@ private: // Mount state uint32_t mountInstanceId_ = 0; float mountHeightOffset_ = 0.0f; + float mountPitch_ = 0.0f; // Up/down tilt (radians) + float mountRoll_ = 0.0f; // Left/right banking (radians) bool taxiFlight_ = false; bool terrainEnabled = true; diff --git a/src/core/application.cpp b/src/core/application.cpp index a1721fcc..65150b94 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -719,11 +719,13 @@ void Application::setupUICallbacks() { }); // Taxi orientation callback - update mount rotation during flight - gameHandler->setTaxiOrientationCallback([this](float orientationRadians) { + gameHandler->setTaxiOrientationCallback([this](float yaw, float pitch, float roll) { if (renderer && renderer->getCameraController()) { - // Convert radians to degrees for camera controller - float yawDegrees = glm::degrees(orientationRadians); + // Convert radians to degrees for camera controller (character facing) + float yawDegrees = glm::degrees(yaw); renderer->getCameraController()->setFacingYaw(yawDegrees); + // Set mount pitch and roll for realistic flight animation + renderer->setMountPitchRoll(pitch, roll); } }); diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 5ead3cb5..dfc61b02 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -5195,12 +5195,17 @@ void GameHandler::startClientTaxiPath(const std::vector& pathNodes) { glm::vec3 dir = end - start; float initialOrientation = std::atan2(dir.y, dir.x) - 1.57079632679f; + // Calculate initial pitch from altitude change + glm::vec3 dirNorm = glm::normalize(dir); + float initialPitch = std::asin(std::clamp(dirNorm.z, -1.0f, 1.0f)); + float initialRoll = 0.0f; // No initial banking + playerEntity->setPosition(start.x, start.y, start.z, initialOrientation); movementInfo.orientation = initialOrientation; - // Update mount rotation immediately + // Update mount rotation immediately with pitch and roll if (taxiOrientationCallback_) { - taxiOrientationCallback_(initialOrientation); + taxiOrientationCallback_(initialOrientation, initialPitch, initialRoll); } } @@ -5281,15 +5286,24 @@ void GameHandler::updateClientTaxi(float deltaTime) { 3.0f * (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t2 ); - // Smooth orientation based on spline tangent + // Calculate yaw from horizontal direction float targetOrientation = std::atan2(tangent.y, tangent.x) - 1.57079632679f; - // Smooth rotation transition (lerp towards target) + // Calculate pitch from vertical component (altitude change) + glm::vec3 tangentNorm = glm::normalize(tangent); + float pitch = std::asin(std::clamp(tangentNorm.z, -1.0f, 1.0f)); + + // Calculate roll (banking) from rate of yaw change float currentOrientation = movementInfo.orientation; float orientDiff = targetOrientation - currentOrientation; // Normalize angle difference to [-PI, PI] while (orientDiff > 3.14159265f) orientDiff -= 6.28318530f; while (orientDiff < -3.14159265f) orientDiff += 6.28318530f; + // Bank proportional to turn rate (scaled for visual effect) + float roll = -orientDiff * 2.5f; + roll = std::clamp(roll, -0.7f, 0.7f); // Limit to ~40 degrees + + // Smooth rotation transition (lerp towards target) float smoothOrientation = currentOrientation + orientDiff * std::min(1.0f, deltaTime * 3.0f); playerEntity->setPosition(nextPos.x, nextPos.y, nextPos.z, smoothOrientation); @@ -5298,9 +5312,9 @@ void GameHandler::updateClientTaxi(float deltaTime) { movementInfo.z = nextPos.z; movementInfo.orientation = smoothOrientation; - // Update mount rotation to face flight direction + // Update mount rotation with yaw, pitch, and roll for realistic flight if (taxiOrientationCallback_) { - taxiOrientationCallback_(smoothOrientation); + taxiOrientationCallback_(smoothOrientation, pitch, roll); } } diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index d569680c..67e2b7b8 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -508,6 +508,8 @@ void Renderer::setMounted(uint32_t mountInstId, float heightOffset) { void Renderer::clearMount() { mountInstanceId_ = 0; mountHeightOffset_ = 0.0f; + mountPitch_ = 0.0f; + mountRoll_ = 0.0f; charAnimState = CharAnimState::IDLE; if (cameraController) { cameraController->setMounted(false); @@ -659,7 +661,8 @@ void Renderer::updateCharacterAnimation() { if (mountInstanceId_ > 0) { characterRenderer->setInstancePosition(mountInstanceId_, characterPosition); float yawRad = glm::radians(characterYaw); - characterRenderer->setInstanceRotation(mountInstanceId_, glm::vec3(0.0f, 0.0f, yawRad)); + // Apply pitch (up/down), roll (banking), and yaw for realistic flight + characterRenderer->setInstanceRotation(mountInstanceId_, glm::vec3(mountPitch_, mountRoll_, yawRad)); // Drive mount model animation: idle when still, run when moving auto pickMountAnim = [&](std::initializer_list candidates, uint32_t fallback) -> uint32_t {