#include "Runtime/Character/CSteeringBehaviors.hpp" #include "Runtime/CStateManager.hpp" #include "Runtime/World/CPhysicsActor.hpp" namespace metaforce { zeus::CVector3f CSteeringBehaviors::Flee(const CPhysicsActor& actor, const zeus::CVector3f& v0) const { zeus::CVector3f actVec = actor.GetTranslation() - v0; if (actVec.canBeNormalized()) return actVec.normalized(); return actor.GetTransform().frontVector(); } zeus::CVector3f CSteeringBehaviors::Seek(const CPhysicsActor& actor, const zeus::CVector3f& target) const { zeus::CVector3f posDiff = target - actor.GetTranslation(); if (posDiff.canBeNormalized()) return posDiff.normalized(); return {}; } zeus::CVector3f CSteeringBehaviors::Arrival(const CPhysicsActor& actor, const zeus::CVector3f& dest, float dampingRadius) const { zeus::CVector3f posDiff = dest - actor.GetTranslation(); if (!posDiff.canBeNormalized()) return {}; if (posDiff.magSquared() < (dampingRadius * dampingRadius)) dampingRadius = posDiff.magSquared() / (dampingRadius * dampingRadius); else dampingRadius = 1.f; return dampingRadius * posDiff.normalized(); } zeus::CVector3f CSteeringBehaviors::Pursuit(const CPhysicsActor& actor, const zeus::CVector3f& v0, const zeus::CVector3f& v1) const { zeus::CVector3f target; if (!ProjectLinearIntersection(actor.GetTranslation(), actor.GetVelocity().magnitude(), v0, v1, target)) target = v1 * 1.f + v0; return CSteeringBehaviors::Seek(actor, target); } zeus::CVector3f CSteeringBehaviors::Separation(const CPhysicsActor& actor, const zeus::CVector3f& pos, float separation) const { zeus::CVector3f posDiff = actor.GetTranslation() - pos; if (posDiff.magSquared() >= separation * separation) return {}; if (!posDiff.canBeNormalized()) return actor.GetTransform().frontVector(); return (1.f - (posDiff.magSquared() / (separation * separation))) * posDiff.normalized(); } zeus::CVector3f CSteeringBehaviors::Alignment(const CPhysicsActor& actor, rstl::reserved_vector& list, const CStateManager& mgr) const { zeus::CVector3f align; if (!list.empty()) { for (const TUniqueId& id : list) if (const CActor* act = static_cast(mgr.GetObjectById(id))) align += act->GetTransform().frontVector(); align *= zeus::CVector3f(1.f / float(list.size())); } float diff = zeus::CVector3f::getAngleDiff(actor.GetTransform().frontVector(), align); return align * (diff / M_PIF); } zeus::CVector3f CSteeringBehaviors::Cohesion(const CPhysicsActor& actor, rstl::reserved_vector& list, float dampingRadius, const CStateManager& mgr) const { zeus::CVector3f dest; if (!list.empty()) { for (const TUniqueId& id : list) if (const CActor* act = static_cast(mgr.GetObjectById(id))) dest += act->GetTranslation(); dest *= zeus::CVector3f(1.f / float(list.size())); return Arrival(actor, dest, dampingRadius); } return dest; } zeus::CVector2f CSteeringBehaviors::Flee2D(const CPhysicsActor& actor, const zeus::CVector2f& v0) const { zeus::CVector2f diffVec = actor.GetTranslation().toVec2f() - v0; if (diffVec.magSquared() > FLT_EPSILON) return diffVec.normalized(); else return actor.GetTransform().basis[1].toVec2f(); } zeus::CVector2f CSteeringBehaviors::Arrival2D(const CPhysicsActor& actor, const zeus::CVector2f& v0) const { zeus::CVector2f diffVec = v0 - actor.GetTranslation().toVec2f(); if (diffVec.magSquared() > FLT_EPSILON) return diffVec.normalized(); else return {}; } bool CSteeringBehaviors::SolveQuadratic(float a, float b, float c, float& xPos, float& xNeg) { float numSq = b * b - 4.f * a * c; if (numSq < FLT_EPSILON || std::fabs(a) < FLT_EPSILON) return false; numSq = std::sqrt(numSq); float denom = 2.f * a; xPos = (-b + numSq) / denom; xNeg = (-b - numSq) / denom; return true; } bool CSteeringBehaviors::SolveCubic(const rstl::reserved_vector& in, rstl::reserved_vector& out) { if (in[3] != 0.f) { float f3 = 3.f * in[3]; float f31 = in[2] / f3; float f4 = in[1] / f3 - f31 * f31; float f0 = (f31 * f4 - in[0]) / in[3]; float f1 = 2.f * f31 * f31; f3 = f4 * f4 * f4; float f24 = -0.5f * (f31 * f1 - f0); f1 = f24 * f24 + f3; if (f1 < 0.f) { float f25 = std::acos(zeus::clamp(-1.f, f24 / std::sqrt(-f3), 1.f)); f24 = 2.f * std::pow(-f3, 0.166667f); for (float f23 = 0.f; f23 < 2.01f; f23 += 1.f) out.push_back(std::cos((2.f * f23 * M_PIF + f25) / 3.f) * f24 - f31); if (out[1] < out[0]) std::swap(out[1], out[0]); if (out[2] < out[1]) std::swap(out[2], out[1]); if (out[1] < out[0]) std::swap(out[1], out[0]); } else { float f30 = std::sqrt(f1); float f25 = std::pow(std::fabs(f24 + f30), 0.333333f); f1 = std::pow(std::fabs(f24 - f30), 0.333333f); f1 = (f24 - f30) > 0.f ? f1 : -f1; f25 = (f24 + f30) > 0.f ? f25 : -f25; out.push_back(f25 + f1 - f31); } for (float& f : out) { float f8 = (2.f * in[2] + 3.f * f * in[3]) * f + in[1]; if (f8 != 0.f) f -= (((f * in[3] + in[2]) * f + in[1]) * f + in[0]) / f8; } } else if (in[2] != 0.f) { float f23 = 0.5f * in[1] / in[2]; float f1 = f23 * f23 - (in[1] / in[2]); if (f1 >= 0.f) { f1 = std::sqrt(f1); out.push_back(-f23 - f1); out.push_back(-f23 + f1); } } else if (in[1] != 0.f) { out.push_back(-in[0] / in[1]); } return out.size() != 0; } bool CSteeringBehaviors::SolveQuartic(const rstl::reserved_vector& in, rstl::reserved_vector& out) { if (in[4] == 0.f) { rstl::reserved_vector newIn; newIn.push_back(in[0]); newIn.push_back(in[1]); newIn.push_back(in[2]); newIn.push_back(in[3]); return SolveCubic(newIn, out); } else { rstl::reserved_vector newIn; float f30 = in[3] / (4.f * in[4]); float f2 = in[1] / in[4]; float f29 = f30 * (8.f * f30 * f30 - 2.f * in[2] / in[4]) + f2; float f31 = -6.f * f30 * f30 + (in[2] / in[4]); float f28 = f30 * (f30 * (-3.f * f30 * f30 + (in[2] / in[4])) - f2) + (in[0] / in[4]); newIn.push_back(4.f * f28 * f31 - f29 * f29); newIn.push_back(-8.f * f28); newIn.push_back(-4.f * f31); newIn.push_back(8.f); rstl::reserved_vector newOut; if (SolveCubic(newIn, newOut)) { float f26 = 2.f * newOut.back() - f31; f31 = std::sqrt(f26); float f1; if (f31 == 0.f) { f1 = newOut.back() * newOut.back() - f28; if (f1 < 0.f) return false; f1 = std::sqrt(f1); } else { f1 = f29 / (2.f * f31); } float f1b = f26 - (newOut.back() + f1) * 4.f; f26 = f26 - (newOut.back() - f1) * 4.f; if (f1b >= 0.f) { f1b = std::sqrt(f1b); out.push_back((f31 - f1b) * 0.5f - f30); out.push_back((f31 + f1b) * 0.5f - f30); } if (f26 >= 0.f) { f1b = std::sqrt(f26); out.push_back((-f31 - f1b) * 0.5f - f30); out.push_back((-f31 + f1b) * 0.5f - f30); } for (float& f : out) { float f10 = ((3.f * in[3] + 4.f * f * in[4]) * f + 2.f * in[2]) * f + in[1]; if (f10 != 0.f) f -= ((((f * in[4] + in[3]) * f + in[2]) * f + in[1]) * f + in[0]) / f10; } if (out.size() > 2) { if (out[2] < out[0]) std::swap(out[2], out[0]); if (out[3] < out[1]) std::swap(out[3], out[1]); if (out[1] < out[0]) std::swap(out[1], out[0]); if (out[3] < out[2]) std::swap(out[3], out[2]); if (out[2] < out[1]) std::swap(out[2], out[1]); } } return out.size() != 0; } } bool CSteeringBehaviors::ProjectLinearIntersection(const zeus::CVector3f& v0, float f1, const zeus::CVector3f& v1, const zeus::CVector3f& v2, zeus::CVector3f& v3) { zeus::CVector3f posDiff = v1 - v0; float xPos, xNeg; if (SolveQuadratic(v2.magSquared() - f1 * f1, posDiff.dot(v2) * 2.f, posDiff.magSquared(), xPos, xNeg) && xNeg > 0.f) { v3 = v2 * xNeg + v1; return true; } return false; } bool CSteeringBehaviors::ProjectLinearIntersection(const zeus::CVector3f& v0, float f1, const zeus::CVector3f& v1, const zeus::CVector3f& v2, const zeus::CVector3f& v3, zeus::CVector3f& v4) { rstl::reserved_vector newIn; rstl::reserved_vector newOut; zeus::CVector3f f7 = v1 - v0; newIn.push_back(f7.magSquared()); newIn.push_back(f7.dot(v2) * 2.f); newIn.push_back(f7.dot(v3) + v2.magSquared() - f1 * f1); newIn.push_back(v2.dot(v3)); newIn.push_back(v3.magSquared() * 0.25f); bool ret = false; if (SolveQuartic(newIn, newOut)) for (float& f : newOut) if (f > 0.f) { ret = true; v4 = v1 + v2 * f + 0.5f * f * f * v3; } return ret; } bool CSteeringBehaviors::ProjectOrbitalIntersection(const zeus::CVector3f& v0, float f1, float f2, const zeus::CVector3f& v1, const zeus::CVector3f& v2, const zeus::CVector3f& v3, zeus::CVector3f& v4) { if (f1 > 0.f) { if (v2.canBeNormalized()) { zeus::CVector3f _12c = (v1 - v3).toVec2f(); if (_12c.canBeNormalized()) { zeus::CVector3f f25 = v1; zeus::CVector3f f22 = v2; float f17 = (f25 - v0).magnitude() / f1 - 0.f; float f18 = FLT_MAX; zeus::CVector3f _150 = _12c.normalized(); float f26 = _150.dot(f22); float f27 = _150.cross(zeus::skUp).dot(f22); for (float f19 = 0.f; f17 < f18 && f19 < 4.f;) { if (zeus::close_enough(f17, f2) || f17 < 0.f) { v4 = f25; return true; } f25 += f2 * f22; f18 = f17; _12c = (f25 - v3).toVec2f(); if (!_12c.canBeNormalized()) break; zeus::CVector3f _168 = _12c.normalized(); f22 = _168.cross(zeus::skUp) * f27 + f26 * _168; f19 += f2; f17 = (f25 - v0).magnitude() / f1 - f19; } } else { return ProjectLinearIntersection(v0, f1, v1, v2, v4); } } else { v4 = v1; return true; } } return false; } bool CSteeringBehaviors::ProjectOrbitalIntersection(const zeus::CVector3f& v0, float f1, float f2, const zeus::CVector3f& v1, const zeus::CVector3f& v2, const zeus::CVector3f& v3, const zeus::CVector3f& v4, zeus::CVector3f& v5) { if (f1 > 0.f) { zeus::CVector3f _12c = (v1 - v4).toVec2f(); if (v2.canBeNormalized() && _12c.canBeNormalized()) { zeus::CVector3f f24 = v1; zeus::CVector3f f21 = v2; float f16 = (f24 - v0).magnitude() / f1 - 0.f; float f17 = FLT_MAX; zeus::CVector3f _150 = _12c.normalized(); float f25 = _150.dot(f21); float f26 = _150.cross(zeus::skUp).dot(f21); for (float f18 = 0.f; f16 < f17 && f18 < 4.f;) { if (zeus::close_enough(f16, f2) || f16 < 0.f) { v5 = f24; return true; } f24 += f2 * f21; f17 = f16; f18 += f2; f16 = (f24 - v0).magnitude() / f1 - f18; _12c = (f24 - v4).toVec2f(); if (!_12c.canBeNormalized()) break; zeus::CVector3f _168 = _12c.normalized(); f21 = _168.cross(zeus::skUp) * f26 + f25 * _168; } } else { return ProjectLinearIntersection(v0, f1, v1, v2, v3, v5); } } return false; } zeus::CVector3f CSteeringBehaviors::ProjectOrbitalPosition(const zeus::CVector3f& pos, const zeus::CVector3f& vel, const zeus::CVector3f& orbitPoint, float dt, float preThinkDt) { zeus::CVector3f usePos = pos; if (vel.canBeNormalized()) { zeus::CVector3f pointToPos = pos - orbitPoint; pointToPos.z() = 0.f; if (pointToPos.canBeNormalized()) { zeus::CVector3f useVel = vel; pointToPos.normalize(); float f29 = pointToPos.dot(useVel); float f30 = pointToPos.cross(zeus::skUp).dot(useVel); for (float curDt = 0.f; curDt < dt;) { usePos += preThinkDt * useVel; zeus::CVector3f usePointToPos = usePos - orbitPoint; usePointToPos.z() = 0.f; if (usePointToPos.canBeNormalized()) { usePointToPos.normalize(); useVel = usePointToPos.cross(zeus::skUp) * f30 + usePointToPos * f29; } curDt += std::min(dt - curDt, preThinkDt); } } } return usePos; } } // namespace metaforce