diff --git a/Runtime/Camera/CBallCamera.hpp b/Runtime/Camera/CBallCamera.hpp index 313d55494..7bd663c33 100644 --- a/Runtime/Camera/CBallCamera.hpp +++ b/Runtime/Camera/CBallCamera.hpp @@ -234,7 +234,6 @@ private: static zeus::CVector3f GetFailsafeSplinePoint(const std::vector& points, float t); bool CheckFailsafeFromMorphBallState(CStateManager& mgr) const; bool SplineIntersectTest(CMaterialList& intersectMat, CStateManager& mgr) const; - static bool IsBallNearDoor(const zeus::CVector3f& pos, CStateManager& mgr); void ActivateFailsafe(float dt, CStateManager& mgr); bool ConstrainElevationAndDistance(float& elevation, float& distance, float dt, CStateManager& mgr); zeus::CVector3f FindDesiredPosition(float distance, float elevation, const zeus::CVector3f& dir, @@ -272,6 +271,9 @@ public: void ResetPosition(CStateManager& mgr); void DoorClosed(TUniqueId doorId); void DoorClosing(TUniqueId doorId); + const zeus::CVector3f& GetFixedLookPos() const { return x1cc_fixedLookPos; } + + static bool IsBallNearDoor(const zeus::CVector3f& pos, CStateManager& mgr); }; } diff --git a/Runtime/Camera/CCameraSpline.cpp b/Runtime/Camera/CCameraSpline.cpp index cb331ee0e..bd047033d 100644 --- a/Runtime/Camera/CCameraSpline.cpp +++ b/Runtime/Camera/CCameraSpline.cpp @@ -293,8 +293,174 @@ zeus::CVector3f CCameraSpline::GetInterpolatedSplinePointByTime(float time, floa return {}; } -float CCameraSpline::FindClosestLengthAlongSpline(float time, const zeus::CVector3f& p) +float CCameraSpline::FindClosestLengthOnSpline(float time, const zeus::CVector3f& p) const { - return 0.f; + float ret = -1.f; + float minLenDelta = 10000.f; + float minMag = 10000.f; + + size_t iterations = x4_positions.size() - 1; + if (x48_closedLoop) + iterations += 1; + + for (size_t i=0 ; i 1.f) + t = 1.f; + } + + if (t >= 0.f && t <= 1.f) + { + float tLen; + if (i == x4_positions.size() - 1) + tLen = x44_length - x24_t[i]; + else + tLen = x24_t[i + 1] - x24_t[i]; + + float lenT = t * tLen + x24_t[i]; + zeus::CVector3f pointDelta = p - GetInterpolatedSplinePointByLength(lenT).origin; + float mag = 0.f; + if (pointDelta.canBeNormalized()) + mag = pointDelta.magnitude(); + float lenDelta = std::fabs(lenT - time); + if (x48_closedLoop && lenDelta > x44_length - lenDelta) + lenDelta = x44_length - lenDelta; + if (zeus::close_enough(std::fabs(mag - minMag), 0.f)) + { + if (lenDelta < minLenDelta) + { + ret = lenT; + minLenDelta = lenDelta; + } + } + else + { + if (mag < minMag) + { + ret = lenT; + minLenDelta = lenDelta; + minMag = mag; + } + } + } + } + + return std::max(ret, 0.f); } + +float CCameraSpline::ValidateLength(float t) const +{ + if (x48_closedLoop) + { + while (t >= x44_length) + t -= x44_length; + while (t < 0.f) + t += x44_length; + return t; + } + else + { + return zeus::clamp(0.f, t, x44_length); + } +} + +float CCameraSpline::ClampLength(const zeus::CVector3f& pos, bool collide, + const CMaterialFilter& filter, const CStateManager& mgr) const +{ + if (x4_positions.empty()) + return 0.f; + + if (x48_closedLoop) + return 0.f; + + zeus::CVector3f deltaA = pos - x4_positions.front(); + zeus::CVector3f deltaB = pos - x4_positions.back(); + float magA = deltaA.magnitude(); + float magB = deltaB.magnitude(); + if (!deltaA.canBeNormalized()) + return 0.f; + if (!deltaB.canBeNormalized()) + return x44_length; + + if (collide) + { + bool collideA = mgr.RayStaticIntersection(x4_positions.front(), deltaA.normalized(), magA, filter).IsValid(); + bool collideB = mgr.RayStaticIntersection(x4_positions.back(), deltaB.normalized(), magB, filter).IsValid(); + if (collideA) + return x44_length; + if (collideB) + return 0.f; + } + + if (magA < magB) + return 0.f; + else + return x44_length; +} + } diff --git a/Runtime/Camera/CCameraSpline.hpp b/Runtime/Camera/CCameraSpline.hpp index bd5bdcc34..7cec25c01 100644 --- a/Runtime/Camera/CCameraSpline.hpp +++ b/Runtime/Camera/CCameraSpline.hpp @@ -7,6 +7,8 @@ namespace urde { class CStateManager; +class CMaterialFilter; + class CCameraSpline { friend class CBallCamera; @@ -31,10 +33,15 @@ public: void UpdateSplineLength() { x44_length = CalculateSplineLength(); } zeus::CTransform GetInterpolatedSplinePointByLength(float pos) const; zeus::CVector3f GetInterpolatedSplinePointByTime(float time, float range) const; - float FindClosestLengthAlongSpline(float time, const zeus::CVector3f& p); + float FindClosestLengthOnSpline(float time, const zeus::CVector3f& p) const; + float ValidateLength(float t) const; + float ClampLength(const zeus::CVector3f& pos, bool collide, + const CMaterialFilter& filter, const CStateManager& mgr) const; s32 GetSize() const { return x4_positions.size(); } float GetLength() const { return x44_length; } + bool IsClosedLoop() const { return x48_closedLoop; } }; + } #endif // __URDE_CCAMERASPLINE_HPP__ diff --git a/Runtime/Camera/CPathCamera.cpp b/Runtime/Camera/CPathCamera.cpp index 904bae383..76e5fea3d 100644 --- a/Runtime/Camera/CPathCamera.cpp +++ b/Runtime/Camera/CPathCamera.cpp @@ -4,6 +4,7 @@ #include "CBallCamera.hpp" #include "World/CScriptCameraHint.hpp" #include "World/CPlayer.hpp" +#include "World/CScriptDoor.hpp" #include "GameGlobalObjects.hpp" #include "TCastTo.hpp" @@ -18,7 +19,7 @@ CPathCamera::CPathCamera(TUniqueId uid, std::string_view name, const CEntityInfo CCameraManager::NearPlane(), CCameraManager::FarPlane(), CCameraManager::Aspect(), kInvalidUniqueId, 0, 0) -, x188_spline(flags & 1), x1dc_(f1), x1e0_(f2), x1e4_(f3), x1e8_initPos(initPos) +, x188_spline(flags & 1), x1dc_lengthExtent(f1), x1e0_(f2), x1e4_(f3), x1e8_initPos(initPos) , x1ec_flags(flags), x1f0_(f4), x1f4_(f5) { } @@ -68,7 +69,7 @@ void CPathCamera::Think(float dt, CStateManager& mgr) SetTranslation(xf.origin); if (x1ec_flags & 0x20) - sub8012DD3C(mgr); + ClampToClosedDoor(mgr); zeus::CVector3f tmp = ballLook - GetTranslation(); tmp.z = 0.f; @@ -81,28 +82,171 @@ void CPathCamera::Think(float dt, CStateManager& mgr) void CPathCamera::ProcessInput(const CFinalInput&, CStateManager& mgr) { + // Empty } +static const CMaterialFilter kLineOfSightFilter = + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::ProjectilePassthrough}); + void CPathCamera::Reset(const zeus::CTransform&, CStateManager& mgr) { CPlayer& player = mgr.GetPlayer(); - float f23 = x188_spline.FindClosestLengthAlongSpline(0.f, player.GetTranslation() + g_tweakPlayer->GetPlayerBallHalfExtent()); + zeus::CVector3f playerPt = player.GetTranslation() + + zeus::CVector3f(0.f, 0.f, g_tweakPlayer->GetPlayerBallHalfExtent()); + float closestLength = x188_spline.FindClosestLengthOnSpline(0.f, playerPt); - float f25 = std::max(0.f, f23 - x1dc_); + float negLength = std::max(0.f, closestLength - x1dc_lengthExtent); + zeus::CVector3f negPoint = x188_spline.GetInterpolatedSplinePointByLength(negLength).origin; - zeus::CTransform xf = x188_spline.GetInterpolatedSplinePointByLength(f25); + float posLength = std::min(x188_spline.GetLength(), closestLength + x1dc_lengthExtent); + zeus::CVector3f posPoint = x188_spline.GetInterpolatedSplinePointByLength(posLength).origin; - float f1 = x188_spline.GetLength(); + zeus::CTransform camXf = mgr.GetCameraManager()->GetBallCamera()->GetTransform(); + if (player.GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) + camXf = mgr.GetCameraManager()->GetCurrentCameraTransform(mgr); + bool neg = false; + if (x1e8_initPos == EInitialSplinePosition::BallCamBasis) + { + zeus::CVector3f tmp = playerPt - negPoint; + if (tmp.canBeNormalized()) + { + if (tmp.normalized().dot(camXf.basis[1]) > 0.f) + neg = true; + } + } + else + { + neg = x1e8_initPos == EInitialSplinePosition::Negative; + } + +#if 0 + zeus::CVector3f camToSpline = splinePt - camXf.origin; + mgr.RayStaticIntersection(camXf.origin, camToSpline.normalized(), camToSpline.magnitude(), kLineOfSightFilter); + zeus::CVector3f camToSpline2 = splinePt2 - camXf.origin; + mgr.RayStaticIntersection(camXf.origin, camToSpline2.normalized(), camToSpline2.magnitude(), kLineOfSightFilter); +#endif + + zeus::CVector3f viewPoint; + if (neg) + { + x1d4_pos = negLength; + viewPoint = negPoint; + } + else + { + x1d4_pos = posLength; + viewPoint = posPoint; + } + + if (x1e8_initPos == EInitialSplinePosition::ClampBasis) + { + if (x188_spline.ClampLength(playerPt, false, kLineOfSightFilter, mgr) <= negLength) + { + x1d4_pos = negLength; + viewPoint = negPoint; + } + else + { + x1d4_pos = posLength; + viewPoint = posPoint; + } + } + + SetTransform(zeus::lookAt(viewPoint, mgr.GetCameraManager()->GetBallCamera()->GetFixedLookPos())); } -zeus::CTransform CPathCamera::MoveAlongSpline(float, CStateManager& mgr) +zeus::CTransform CPathCamera::MoveAlongSpline(float t, CStateManager& mgr) { - return {}; + zeus::CTransform ret = x34_transform; + x1d8_time = x188_spline.FindClosestLengthOnSpline(x1d8_time, mgr.GetPlayer().GetTranslation()); + float f30 = x1dc_lengthExtent; + if (x1ec_flags & 0x8) + { + zeus::CVector3f splineToPlayer = mgr.GetPlayer().GetTranslation() - + x188_spline.GetInterpolatedSplinePointByLength(x1d8_time).origin; + float distToPlayer = 0.f; + if (splineToPlayer.canBeNormalized()) + distToPlayer = splineToPlayer.magnitude(); + f30 *= 1.f - std::sin(zeus::degToRad(zeus::clamp(0.f, (distToPlayer - x1f0_) / (x1f4_ - x1f0_), 1.f) * 90.f)); + } + + float newPos; + if (x188_spline.IsClosedLoop()) + { + float lenA = x188_spline.ValidateLength(x1d8_time + f30); + newPos = x188_spline.ValidateLength(x1d8_time - f30); + float disp = std::fabs(x1d4_pos - x1d8_time); + float remLen = x188_spline.GetLength() - disp; + if (x1d4_pos > x1d8_time) + { + if (disp <= remLen) + newPos = lenA; + } + else + { + if (disp > remLen) + newPos = lenA; + } + } + else + { + if (x1d4_pos > x1d8_time) + newPos = x188_spline.ValidateLength(x1d8_time + f30); + else + newPos = x188_spline.ValidateLength(x1d8_time - f30); + } + + if (x1ec_flags & 0x2) + { + x1d4_pos = newPos; + ret = x188_spline.GetInterpolatedSplinePointByLength(x1d4_pos); + } + else + { + if (x188_spline.IsClosedLoop()) + { + float absDelta = std::fabs(newPos - x1d4_pos); + if (absDelta > x188_spline.GetLength() - absDelta) + absDelta = x188_spline.GetLength() - absDelta; + float tBias = zeus::clamp(-1.f, absDelta / x1e4_, 1.f) * x1e0_ * t; + float tmpAbs = std::fabs(x1d4_pos - newPos); + float absDelta2 = x188_spline.GetLength() - tmpAbs; + if (x1d4_pos > newPos) + { + if (tmpAbs <= absDelta2) + tBias *= -1.f; + } + else + { + if (tmpAbs > absDelta2) + tBias *= -1.f; + } + x1d4_pos = x188_spline.ValidateLength(x1d4_pos + tBias); + } + else + { + x1d4_pos = x188_spline.ValidateLength( + zeus::clamp(-1.f, (newPos - x1d4_pos) / x1e4_, 1.f) * x1e0_ * t + x1d4_pos); + } + ret = x188_spline.GetInterpolatedSplinePointByLength(x1d4_pos); + } + + return ret; } -void CPathCamera::sub8012DD3C(CStateManager& ) +void CPathCamera::ClampToClosedDoor(CStateManager& mgr) { + if (TCastToConstPtr door = + mgr.GetObjectById(mgr.GetCameraManager()->GetBallCamera()->GetTooCloseActorId())) + { + if (!door->IsOpen() && CBallCamera::IsBallNearDoor(GetTranslation(), mgr)) + { + x1d4_pos = (x1d4_pos > x1d8_time) ? + x1d8_time - x1dc_lengthExtent : x1d8_time + x1dc_lengthExtent; + SetTranslation(x188_spline.GetInterpolatedSplinePointByLength(x1d4_pos).origin); + } + } } } diff --git a/Runtime/Camera/CPathCamera.hpp b/Runtime/Camera/CPathCamera.hpp index cd41d9b81..df606aeb6 100644 --- a/Runtime/Camera/CPathCamera.hpp +++ b/Runtime/Camera/CPathCamera.hpp @@ -12,13 +12,17 @@ class CPathCamera : public CGameCamera public: enum class EInitialSplinePosition { + BallCamBasis, + Negative, + Positive, + ClampBasis }; private: CCameraSpline x188_spline; float x1d4_pos = 0.f; float x1d8_time = 0.f; - float x1dc_; + float x1dc_lengthExtent; float x1e0_; float x1e4_; EInitialSplinePosition x1e8_initPos; @@ -38,7 +42,7 @@ public: void ProcessInput(const CFinalInput&, CStateManager& mgr); void Reset(const zeus::CTransform&, CStateManager& mgr); zeus::CTransform MoveAlongSpline(float, CStateManager&); - void sub8012DD3C(CStateManager&); + void ClampToClosedDoor(CStateManager&); }; } diff --git a/Runtime/MP1/CFrontEndUI.cpp b/Runtime/MP1/CFrontEndUI.cpp index e8f4f82f3..44cddd35f 100644 --- a/Runtime/MP1/CFrontEndUI.cpp +++ b/Runtime/MP1/CFrontEndUI.cpp @@ -2006,7 +2006,7 @@ CFrontEndUI::CFrontEndUI() m->ResetGameState(); g_GameState->SetCurrentWorldId(g_ResFactory->TranslateOriginalToNew(g_DefaultWorldTag.id)); - g_GameState->CurrentWorldState().SetAreaId(6); + g_GameState->CurrentWorldState().SetAreaId(8); g_GameState->GameOptions().ResetToDefaults(); g_GameState->WriteBackupBuf();