#include "CFirstPersonCamera.hpp" #include "GameGlobalObjects.hpp" #include "CStateManager.hpp" #include "World/CPlayer.hpp" #include "World/CScriptGrapplePoint.hpp" #include "World/CScriptCameraPitchVolume.hpp" #include "TCastTo.hpp" namespace urde { CFirstPersonCamera::CFirstPersonCamera(TUniqueId uid, const zeus::CTransform& xf, TUniqueId watchedObj, float orbitCameraSpeed, float fov, float nearz, float farz, float aspect) : CGameCamera(uid, true, "First Person Camera", CEntityInfo(kInvalidAreaId, CEntity::NullConnectionList), xf, fov, nearz, farz, aspect, watchedObj, false, 0), x188_orbitCameraSpeed(orbitCameraSpeed), x190_gunFollowXf(xf) { x1c6_24_deferBallTransitionProcessing = false; } void CFirstPersonCamera::Accept(IVisitor& visitor) { visitor.Visit(this); } void CFirstPersonCamera::PreThink(float dt, CStateManager& mgr) { // Empty } void CFirstPersonCamera::Think(float dt, CStateManager& mgr) { if (TCastToPtr player = mgr.ObjectById(xe8_watchedObject)) { if (!x1c6_24_deferBallTransitionProcessing) { if (player->GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed) { if (player->GetCameraState() != CPlayer::EPlayerCameraState::Spawned) return; SetTransform(player->CreateTransformFromMovementDirection()); SetTranslation(player->GetEyePosition()); return; } if (player->GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Unmorphed) { if (player->GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Unmorphing) return; if (std::fabs(player->GetMorphFactor() - 1.f) >= 0.00001f) return; } } else { x1c6_24_deferBallTransitionProcessing = false; } zeus::CTransform backupXf = x34_transform; UpdateElevation(mgr); UpdateTransform(mgr, dt); SetTransform(ValidateCameraTransform(x34_transform, backupXf)); if (x1d4_closeInTimer > 0.f) x1d4_closeInTimer -= dt; } } void CFirstPersonCamera::ProcessInput(const CFinalInput&, CStateManager& mgr) { // Empty } void CFirstPersonCamera::Reset(const zeus::CTransform& xf, CStateManager& mgr) { SetTransform(xf); SetTranslation(mgr.GetPlayer().GetEyePosition()); x190_gunFollowXf = x34_transform; } void CFirstPersonCamera::SkipCinematic() { x1c8_closeInVec = zeus::CVector3f::skZero; x1d4_closeInTimer = 0.f; } void CFirstPersonCamera::CalculateGunFollowOrientationAndTransform(zeus::CTransform& gunXf, zeus::CQuaternion& gunQ, float dt, zeus::CVector3f& rVec) { zeus::CVector3f gunFrontVec = x190_gunFollowXf.frontVector(); gunFrontVec.z = 0.f; if (gunFrontVec.canBeNormalized()) gunFrontVec.normalize(); zeus::CVector3f rVecNoZ = rVec; rVecNoZ.z = 0.f; if (rVecNoZ.canBeNormalized()) rVecNoZ.normalize(); gunXf = zeus::CQuaternion::lookAt(rVecNoZ, gunFrontVec, zeus::CRelAngle::FromDegrees(360.f)).toTransform() * gunXf.getRotation(); zeus::CVector3f newgunFront = gunXf.frontVector(); if (newgunFront.canBeNormalized()) newgunFront.normalize(); float angle = newgunFront.dot(rVec); if (std::fabs(angle) > 0.f) angle = (angle > -0.f ? -1.f : 1.f); gunQ = zeus::CQuaternion::lookAt(rVec, newgunFront, zeus::clamp(0.f, std::acos(angle) / dt, 1.f)); } void CFirstPersonCamera::UpdateTransform(CStateManager& mgr, float dt) { TCastToPtr player(mgr.ObjectById(GetWatchedObject())); if (!player) { SetTransform(zeus::CTransform::Identity()); return; } zeus::CTransform playerXf = player->GetTransform(); zeus::CVector3f rVec = playerXf.rotate({0.f, std::min(std::fabs(std::cos(x1c0_pitch)), 1.0f), std::min(std::fabs(std::sin(x1c0_pitch)), 1.0f)}); if (player->x3dc_inFreeLook) { float angle = player->x3ec_freeLookPitchAngle; float angleClamp = g_tweakPlayer->GetVerticalFreeLookAngleVel() - std::fabs(x1c0_pitch); angle = zeus::clamp(-angleClamp, angle, angleClamp); zeus::CVector3f vec; vec.z = std::sin(angle); vec.y = std::cos(-player->x3e4_freeLookYawAngle) * std::cos(angle); vec.x = std::sin(-player->x3e4_freeLookYawAngle) * std::cos(angle); if (g_tweakPlayer->GetFreeLookTurnsPlayer() && !zeus::close_enough(vec, zeus::CVector3f::skZero)) vec.normalize(); rVec = zeus::CQuaternion::lookAt({0.f, 1.f, 0.f}, rVec, zeus::CRelAngle::FromDegrees(360.f)).transform(vec); } zeus::CVector3f eyePos = player->GetEyePosition(); if (x1d4_closeInTimer > 0.f) { eyePos += zeus::clamp(0.f, 0.5f * x1d4_closeInTimer, 1.f) * x1c8_closeInVec; player->GetCameraBob()->ResetCameraBobTime(); player->GetCameraBob()->SetCameraBobTransform(zeus::CTransform::Identity()); } if (player->GetOrbitState() == CPlayer::EPlayerOrbitState::ForcedOrbitObject || player->GetOrbitState() == CPlayer::EPlayerOrbitState::OrbitObject) { const CActor* act = TCastToConstPtr(mgr.GetObjectById(player->x310_orbitTargetId)); if (act && act->GetMaterialList().Intersection(CMaterialList(EMaterialTypes::Lava)) != 0) { zeus::CVector3f v = player->x314_orbitPoint.y - eyePos; if (v.canBeNormalized()) v.normalize(); rVec = v; } } else if (player->GetOrbitState() == CPlayer::EPlayerOrbitState::NoOrbit && player->GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed && !player->x3dc_inFreeLook && x1c4_pitchId == kInvalidUniqueId) { if (player->x294_jumpCameraTimer > 0.f) { float angle = zeus::clamp(0.f, (player->x294_jumpCameraTimer - g_tweakPlayer->GetJumpCameraPitchDownStart()) / g_tweakPlayer->GetJumpCameraPitchDownFull(), 1.f) * g_tweakPlayer->GetJumpCameraPitchDownAngle(); angle += x1c0_pitch; rVec.x = 0.f; rVec.y = std::cos(angle); rVec.z = -std::sin(angle); rVec = playerXf.rotate(rVec); } else if (player->x29c_fallCameraTimer > 0.f) { float angle = zeus::clamp(0.f, (player->x29c_fallCameraTimer - g_tweakPlayer->GetFallCameraPitchDownStart()) / g_tweakPlayer->GetFallCameraPitchDownFull(), 1.f) * g_tweakPlayer->GetFallCameraPitchDownAngle(); rVec.x = 0.f; rVec.y = std::cos(angle); rVec.z = -std::sin(angle); rVec = playerXf.rotate(rVec); } } if (rVec.canBeNormalized()) rVec.normalize(); zeus::CTransform gunXf = x190_gunFollowXf; zeus::CQuaternion qGun = zeus::CQuaternion::skNoRotation; if (player->x3dc_inFreeLook) { if (player->GetOrbitState() == CPlayer::EPlayerOrbitState::ForcedOrbitObject || player->GetOrbitState() == CPlayer::EPlayerOrbitState::OrbitObject) { zeus::CVector3f gunFrontVec = gunXf.frontVector(); if (gunFrontVec.canBeNormalized()) gunFrontVec.normalize(); float scaledDt = (dt * g_tweakPlayer->GetOrbitCameraSpeed()); float angle = gunFrontVec.dot(rVec); angle = zeus::clamp(-1.f, angle, 1.f); float clampedAngle = zeus::clamp(0.f, std::acos(angle) / scaledDt, 1.f); if (angle > 0.999f && x18c_lockCamera && !player->x374_orbitLockEstablished) qGun = zeus::CQuaternion::lookAt(rVec, gunFrontVec, zeus::CRelAngle::FromDegrees(360.f)); else qGun = zeus::CQuaternion::lookAt(rVec, gunFrontVec, scaledDt * clampedAngle); const CScriptGrapplePoint* gPoint = TCastToConstPtr(mgr.GetObjectById(player->x310_orbitTargetId)); if (gPoint && player->x29c_fallCameraTimer > 0.f) { gunFrontVec = x190_gunFollowXf.frontVector(); if (gunFrontVec.canBeNormalized()) gunFrontVec.normalize(); zeus::CVector3f rVecCpy = rVec; if (rVecCpy.canBeNormalized()) rVecCpy.normalize(); gunXf = zeus::CQuaternion::lookAt(rVecCpy, gunFrontVec, zeus::CRelAngle::FromDegrees(360.f)).toTransform() * x190_gunFollowXf.getRotation(); gunFrontVec = gunXf.frontVector(); if (gunFrontVec.canBeNormalized()) gunFrontVec.normalize(); float angle = gunFrontVec.dot(rVec); float sdt = dt * g_tweakPlayer->GetGrappleCameraSpeed(); angle = zeus::clamp(-1.f, angle, 1.f); angle = zeus::clamp(0.f, std::acos(angle) / sdt, 1.f) * sdt; qGun = zeus::CQuaternion::lookAt(rVec, gunFrontVec, angle); } } else if (player->GetOrbitState() == CPlayer::EPlayerOrbitState::OrbitPoint || player->GetOrbitState() == CPlayer::EPlayerOrbitState::OrbitCarcass) { dt *= g_tweakPlayer->GetOrbitCameraSpeed(); CalculateGunFollowOrientationAndTransform(gunXf, qGun, dt, rVec); } else if (player->GetOrbitState() == CPlayer::EPlayerOrbitState::Grapple) { dt *= g_tweakPlayer->GetGrappleCameraSpeed(); CalculateGunFollowOrientationAndTransform(gunXf, qGun, dt, rVec); } else { dt *= g_tweakPlayer->GetFirstPersonCameraSpeed(); CalculateGunFollowOrientationAndTransform(gunXf, qGun, dt, rVec); } } else { zeus::CVector3f gunFront = x190_gunFollowXf.frontVector(); if (gunFront.canBeNormalized()) gunFront.normalize(); zeus::CVector3f rVecCpy = rVec; if (rVecCpy.canBeNormalized()) rVecCpy.normalize(); gunXf = zeus::CQuaternion::lookAt(rVecCpy, gunFront, zeus::CRelAngle::FromDegrees(360.f)).toTransform() * x190_gunFollowXf.getRotation(); gunFront = gunXf.frontVector(); if (gunFront.canBeNormalized()) gunFront.normalize(); float angle = gunFront.dot(rVec); angle = zeus::clamp(-1.f, angle, 1.f); float sdt = dt * g_tweakPlayer->GetFreeLookSpeed(); qGun = zeus::CQuaternion::lookAt( rVec, gunFront, sdt * zeus::clamp(0.f, g_tweakPlayer->GetFreeLookDampenFactor() * (std::acos(angle) / sdt), 1.f)); } zeus::CTransform bobXf = player->GetCameraBob()->GetCameraBobTransformation(); if (player->GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed || player->GetOrbitState() == CPlayer::EPlayerOrbitState::Grapple || player->GetGrappleState() == CPlayer::EGrappleState::None || mgr.GetGameState() == CStateManager::EGameState::SoftPaused || mgr.GetCameraManager()->IsInCinematicCamera()) { bobXf = zeus::CTransform::Identity(); player->GetCameraBob()->SetCameraBobTransform(bobXf); } x190_gunFollowXf = qGun.toTransform() * gunXf; SetTransform(x190_gunFollowXf * bobXf.getRotation()); x190_gunFollowXf.origin = eyePos; CActor::SetTranslation(eyePos + player->GetTransform().rotate(bobXf.origin)); x190_gunFollowXf.orthonormalize(); } void CFirstPersonCamera::UpdateElevation(CStateManager& mgr) { x1c0_pitch = 0.f; if (TCastToConstPtr player = mgr.GetObjectById(xe8_watchedObject)) { if (x1c4_pitchId != kInvalidUniqueId) { if (TCastToConstPtr pvol = mgr.GetObjectById(x1c4_pitchId)) { zeus::CVector3f pitchDirFlat = pvol->GetTransform().basis[1]; pitchDirFlat.z = 0.f; if (!pitchDirFlat.canBeNormalized()) pitchDirFlat = zeus::CVector3f::skForward; zeus::CVector3f playerDirFlat = player->GetTransform().basis[1]; playerDirFlat.z = 0.f; playerDirFlat.normalize(); float pitchDot = zeus::clamp(-1.f, pitchDirFlat.dot(playerDirFlat), 1.f); if (pitchDot < 0.f) x1c0_pitch = pvol->GetDownPitch() * -pitchDot; else x1c0_pitch = pvol->GetUpPitch() * -pitchDot; zeus::CVector3f pvolToPlayerFlat = player->GetTranslation() - pvol->GetTranslation(); pvolToPlayerFlat.z = 0.f; float pitchMul = 0.f; if (pvolToPlayerFlat.canBeNormalized()) { float pvolPlayerProj = std::fabs(zeus::clamp(-1.f, pvolToPlayerFlat.dot(pitchDirFlat), 1.f)) * pvolToPlayerFlat.magnitude(); if (pvolPlayerProj <= pvol->GetMaxInterpolationDistance()) pitchMul = 1.f; else pitchMul = 1.f - zeus::clamp(-1.f, (pvolPlayerProj - pvol->GetMaxInterpolationDistance()) / (pvol->GetScale().y - pvol->GetMaxInterpolationDistance()), 1.f); } x1c0_pitch *= pitchMul; } } } } }