diff --git a/source/egg/core/BitFlag.hh b/source/egg/core/BitFlag.hh index fdae7f5d..4a804b6d 100644 --- a/source/egg/core/BitFlag.hh +++ b/source/egg/core/BitFlag.hh @@ -167,6 +167,13 @@ struct TBitFlag { return (bits & mask) != 0; } + /// @brief Checks if all bits are on in the specified mask. + /// @param mask The bit mask to check. + /// @return True if all bits in the mask are on, otherwise false. + [[nodiscard]] constexpr bool onAll(T mask) const { + return (bits | mask) == bits; + } + /// @brief Checks if all bits are off in the specified mask. /// @param mask The bit mask to check. /// @return True if all bits in the mask are off, otherwise false. diff --git a/source/game/field/BoxColManager.cc b/source/game/field/BoxColManager.cc index 8bb8487f..1433b7a5 100644 --- a/source/game/field/BoxColManager.cc +++ b/source/game/field/BoxColManager.cc @@ -255,6 +255,24 @@ void BoxColManager::search(BoxColUnit *unit, const BoxColFlag &flag) { /// @addr{0x80786B14} void BoxColManager::search(f32 radius, const EGG::Vector3f &pos, const BoxColFlag &flag) { searchImpl(radius, pos, flag); + resetIterators(); +} + +/// @addr{0x80786E60} +bool BoxColManager::isSphereInSpatialCache(f32 radius, const EGG::Vector3f &pos, + const BoxColFlag &flag) const { + if (m_cacheRadius == -1.0f) { + return false; + } + + if (!m_cacheFlag.onAll(flag)) { + return false; + } + + f32 radiusDiff = m_cacheRadius - radius; + EGG::Vector3f posDiff = pos - m_cachePoint; + + return EGG::Mathf::abs(posDiff.x) <= radiusDiff && EGG::Mathf::abs(posDiff.z) <= radiusDiff; } /// @addr{0x807855DC} diff --git a/source/game/field/BoxColManager.hh b/source/game/field/BoxColManager.hh index e34a1516..3be2aad6 100644 --- a/source/game/field/BoxColManager.hh +++ b/source/game/field/BoxColManager.hh @@ -88,6 +88,9 @@ public: void search(BoxColUnit *unit, const BoxColFlag &flag); void search(f32 radius, const EGG::Vector3f &pos, const BoxColFlag &flag); + [[nodiscard]] bool isSphereInSpatialCache(f32 radius, const EGG::Vector3f &pos, + const BoxColFlag &flag) const; + static BoxColManager *CreateInstance(); static void DestroyInstance(); [[nodiscard]] static BoxColManager *Instance(); diff --git a/source/game/field/CollisionDirector.cc b/source/game/field/CollisionDirector.cc index d9a72398..c6c7355c 100644 --- a/source/game/field/CollisionDirector.cc +++ b/source/game/field/CollisionDirector.cc @@ -1,17 +1,20 @@ #include "CollisionDirector.hh" +#include "game/field/ObjectDrivableDirector.hh" + namespace Field { /// @addr{0x8078E4F0} void CollisionDirector::checkCourseColNarrScLocal(f32 radius, const EGG::Vector3f &pos, - KCLTypeMask mask, u32 /*timeOffset*/) { + KCLTypeMask mask, u32 timeOffset) { CourseColMgr::Instance()->scaledNarrowScopeLocal(1.0f, radius, nullptr, pos, mask); + ObjectDrivableDirector::Instance()->colNarScLocal(radius, pos, mask, timeOffset); } /// @addr{0x8078F500} bool CollisionDirector::checkSphereFull(f32 radius, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut, - u32 /*timeOffset*/) { + u32 timeOffset) { if (pInfo) { pInfo->bbox.min = EGG::Vector3f::zero; pInfo->bbox.max = EGG::Vector3f::zero; @@ -32,12 +35,11 @@ bool CollisionDirector::checkSphereFull(f32 radius, const EGG::Vector3f &v0, noBounceInfo->dist = std::numeric_limits::min(); } - bool colliding = false; + bool colliding = flags && + courseColMgr->checkSphereFull(1.0f, radius, nullptr, v0, v1, flags, pInfo, pFlagsOut); - if (flags && - courseColMgr->checkSphereFull(1.0f, radius, nullptr, v0, v1, flags, pInfo, pFlagsOut)) { - colliding = true; - } + colliding |= ObjectDrivableDirector::Instance()->checkSphereFull(radius, v0, v1, flags, pInfo, + pFlagsOut, timeOffset); if (colliding) { if (pInfo) { @@ -57,7 +59,7 @@ bool CollisionDirector::checkSphereFull(f32 radius, const EGG::Vector3f &v0, /// @addr{0x8078F784} bool CollisionDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *pInfo, KCLTypeMask *pFlagsOut, - u32 /*timeOffset*/) { + u32 timeOffset) { if (pInfo) { pInfo->bbox.setZero(); pInfo->_50 = -std::numeric_limits::min(); @@ -77,13 +79,12 @@ bool CollisionDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, noBounceInfo->dist = std::numeric_limits::min(); } - bool colliding = false; - - if (flags && + bool colliding = flags && courseColMgr->checkSphereFullPush(1.0f, radius, nullptr, v0, v1, flags, pInfo, - pFlagsOut)) { - colliding = true; - } + pFlagsOut); + + colliding |= ObjectDrivableDirector::Instance()->checkSphereFullPush(radius, v0, v1, flags, + pInfo, pFlagsOut, timeOffset); if (colliding) { if (pInfo) { @@ -103,7 +104,7 @@ bool CollisionDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, /// @addr{0x807901F0} bool CollisionDirector::checkSphereCachedPartial(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfoPartial *info, - KCLTypeMask *typeMaskOut, u32 /*timeOffset*/) { + KCLTypeMask *typeMaskOut, u32 timeOffset) { if (info) { info->bbox.setZero(); } @@ -119,10 +120,13 @@ bool CollisionDirector::checkSphereCachedPartial(f32 radius, const EGG::Vector3f noBounceInfo->dist = std::numeric_limits::min(); } - bool hasCourseCol = courseColMgr->checkSphereCachedPartial(1.0f, radius, nullptr, pos, prevPos, + bool colliding = courseColMgr->checkSphereCachedPartial(1.0f, radius, nullptr, pos, prevPos, typeMask, info, typeMaskOut); - if (hasCourseCol) { + colliding |= ObjectDrivableDirector::Instance()->checkSphereCachedPartial(radius, pos, prevPos, + typeMask, info, typeMaskOut, timeOffset); + + if (colliding) { if (info) { info->tangentOff = info->bbox.min + info->bbox.max; } @@ -134,13 +138,13 @@ bool CollisionDirector::checkSphereCachedPartial(f32 radius, const EGG::Vector3f courseColMgr->clearNoBounceWallInfo(); - return hasCourseCol; + return colliding; } /// @addr{0x807903BC} bool CollisionDirector::checkSphereCachedPartialPush(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfoPartial *info, - KCLTypeMask *typeMaskOut, u32 /*timeOffset*/) { + KCLTypeMask *typeMaskOut, u32 timeOffset) { if (info) { info->bbox.setZero(); } @@ -156,18 +160,21 @@ bool CollisionDirector::checkSphereCachedPartialPush(f32 radius, const EGG::Vect noBounceInfo->dist = std::numeric_limits::min(); } - bool hasCourseCol = courseColMgr->checkSphereCachedPartialPush(1.0f, radius, nullptr, pos, - prevPos, typeMask, info, typeMaskOut); + bool colliding = courseColMgr->checkSphereCachedPartialPush(1.0f, radius, nullptr, pos, prevPos, + typeMask, info, typeMaskOut); + + colliding |= ObjectDrivableDirector::Instance()->checkSphereCachedPartialPush(radius, pos, + prevPos, typeMask, info, typeMaskOut, timeOffset); courseColMgr->clearNoBounceWallInfo(); - return hasCourseCol; + return colliding; } /// @addr{0x807907F8} bool CollisionDirector::checkSphereCachedFullPush(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfo *colInfo, - KCLTypeMask *typeMaskOut, u32 /*timeOffset*/) { + KCLTypeMask *typeMaskOut, u32 timeOffset) { if (colInfo) { colInfo->bbox.min.setZero(); colInfo->bbox.max.setZero(); @@ -188,10 +195,13 @@ bool CollisionDirector::checkSphereCachedFullPush(f32 radius, const EGG::Vector3 info->dist = std::numeric_limits::min(); } - bool hasCourseCol = courseColMgr->checkSphereCachedFullPush(1.0f, radius, nullptr, pos, prevPos, + bool colliding = courseColMgr->checkSphereCachedFullPush(1.0f, radius, nullptr, pos, prevPos, typeMask, colInfo, typeMaskOut); - if (hasCourseCol) { + colliding |= ObjectDrivableDirector::Instance()->checkSphereCachedFullPush(radius, pos, prevPos, + typeMask, colInfo, typeMaskOut, timeOffset); + + if (colliding) { if (colInfo) { colInfo->tangentOff = colInfo->bbox.min + colInfo->bbox.max; } @@ -203,7 +213,7 @@ bool CollisionDirector::checkSphereCachedFullPush(f32 radius, const EGG::Vector3 courseColMgr->clearNoBounceWallInfo(); - return hasCourseCol; + return colliding; } /// @addr{0x807BDA7C} diff --git a/source/game/field/ObjectDirector.cc b/source/game/field/ObjectDirector.cc index 6c13552f..57322eac 100644 --- a/source/game/field/ObjectDirector.cc +++ b/source/game/field/ObjectDirector.cc @@ -1,6 +1,7 @@ #include "ObjectDirector.hh" #include "game/field/BoxColManager.hh" +#include "game/field/ObjectDrivableDirector.hh" #include "game/field/obj/ObjectRegistry.hh" #include "game/kart/KartObject.hh" @@ -15,6 +16,8 @@ void ObjectDirector::init() { obj->init(); obj->calcModel(); } + + ObjectDrivableDirector::Instance()->init(); } /// @addr{0x8082A8F4} @@ -26,6 +29,8 @@ void ObjectDirector::calc() { for (auto *&obj : m_calcObjects) { obj->calcModel(); } + + ObjectDrivableDirector::Instance()->calc(); } /// @addr{0x8082B0E8} @@ -100,6 +105,8 @@ ObjectDirector *ObjectDirector::CreateInstance() { ASSERT(!s_instance); s_instance = new ObjectDirector; + ObjectDrivableDirector::CreateInstance(); + s_instance->createObjects(); return s_instance; @@ -111,6 +118,8 @@ void ObjectDirector::DestroyInstance() { auto *instance = s_instance; s_instance = nullptr; delete instance; + + ObjectDrivableDirector::DestroyInstance(); } /// @addr{0x8082A38C} diff --git a/source/game/field/ObjectDrivableDirector.cc b/source/game/field/ObjectDrivableDirector.cc new file mode 100644 index 00000000..09676b9e --- /dev/null +++ b/source/game/field/ObjectDrivableDirector.cc @@ -0,0 +1,230 @@ +#include "ObjectDrivableDirector.hh" + +namespace Field { + +/// @addr{0x8081B500} +void ObjectDrivableDirector::init() { + for (auto *&obj : m_objects) { + obj->init(); + obj->calcModel(); + } +} + +/// @addr{0x8081B618} +void ObjectDrivableDirector::calc() { + for (auto *&obj : m_calcObjects) { + obj->calc(); + } + + for (auto *&obj : m_calcObjects) { + obj->calcModel(); + } +} + +/// @addr{0x8081B6C8} +void ObjectDrivableDirector::addObject(ObjectDrivable *obj) { + if (obj->loadFlags() & 1) { + m_calcObjects.push_back(obj); + } + + m_objects.push_back(obj); +} + +/// @addr{0x8081BC98} +bool ObjectDrivableDirector::checkSpherePartial(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + bool hasCollision = false; + auto *boxColMgr = BoxColManager::Instance(); + boxColMgr->search(radius, pos, eBoxColFlag::Drivable); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= + obj->checkSpherePartial(radius, pos, prevPos, mask, info, maskOut, timeOffset); + } + + return hasCollision; +} + +/// @addr{0x8081BD70} +bool ObjectDrivableDirector::checkSpherePartialPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + bool hasCollision = false; + auto *boxColMgr = BoxColManager::Instance(); + boxColMgr->search(radius, pos, eBoxColFlag::Drivable); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= + obj->checkSpherePartialPush(radius, pos, prevPos, mask, info, maskOut, timeOffset); + } + + return hasCollision; +} + +/// @addr{0x8081BE48} +bool ObjectDrivableDirector::checkSphereFull(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut, + u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + bool hasCollision = false; + auto *boxColMgr = BoxColManager::Instance(); + boxColMgr->search(radius, pos, eBoxColFlag::Drivable); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= obj->checkSphereFull(radius, pos, prevPos, mask, info, maskOut, timeOffset); + } + + return hasCollision; +} + +/// @addr{0x8081BFA0} +bool ObjectDrivableDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut, + u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + bool hasCollision = false; + auto *boxColMgr = BoxColManager::Instance(); + boxColMgr->search(radius, pos, eBoxColFlag::Drivable); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= + obj->checkSphereFullPush(radius, pos, prevPos, mask, info, maskOut, timeOffset); + } + + return hasCollision; +} + +/// @addr{0x8081C5A0} +bool ObjectDrivableDirector::checkSphereCachedPartial(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + auto *boxColMgr = BoxColManager::Instance(); + + if (boxColMgr->isSphereInSpatialCache(radius, pos, eBoxColFlag::Drivable)) { + boxColMgr->resetIterators(); + + bool hasCollision = false; + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= obj->checkSphereCachedPartial(radius, pos, prevPos, mask, info, maskOut, + timeOffset); + } + + return hasCollision; + } + + return checkSpherePartial(radius, pos, prevPos, mask, info, maskOut, timeOffset); +} + +/// @addr{0x8081C6B4} +bool ObjectDrivableDirector::checkSphereCachedPartialPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + auto *boxColMgr = BoxColManager::Instance(); + + if (boxColMgr->isSphereInSpatialCache(radius, pos, eBoxColFlag::Drivable)) { + boxColMgr->resetIterators(); + + bool hasCollision = false; + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= obj->checkSphereCachedPartialPush(radius, pos, prevPos, mask, info, + maskOut, timeOffset); + } + + return hasCollision; + } + + return checkSpherePartialPush(radius, pos, prevPos, mask, info, maskOut, timeOffset); +} + +/// @addr{0x8081C958} +bool ObjectDrivableDirector::checkSphereCachedFullPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut, + u32 timeOffset) { + if (m_objects.empty()) { + return false; + } + + auto *boxColMgr = BoxColManager::Instance(); + + if (boxColMgr->isSphereInSpatialCache(radius, pos, eBoxColFlag::Drivable)) { + bool hasCollision = false; + boxColMgr->resetIterators(); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + hasCollision |= obj->checkSphereCachedFullPush(radius, pos, prevPos, mask, info, + maskOut, timeOffset); + } + + return hasCollision; + } + + return checkSphereFullPush(radius, pos, prevPos, mask, info, maskOut, timeOffset); +} + +/// @addr{0x8081B7CC} +void ObjectDrivableDirector::colNarScLocal(f32 radius, const EGG::Vector3f &pos, KCLTypeMask mask, + u32 timeOffset) { + if (m_objects.empty()) { + return; + } + + auto *boxColMgr = BoxColManager::Instance(); + boxColMgr->search(radius, pos, eBoxColFlag::Drivable); + + while (ObjectDrivable *obj = boxColMgr->getNextDrivable()) { + obj->narrScLocal(radius, pos, mask, timeOffset); + } +} + +/// @addr{0x8081B428} +ObjectDrivableDirector *ObjectDrivableDirector::CreateInstance() { + ASSERT(!s_instance); + s_instance = new ObjectDrivableDirector; + return s_instance; +} + +/// @addr{0x8081B4B0} +void ObjectDrivableDirector::DestroyInstance() { + ASSERT(s_instance); + auto *instance = s_instance; + s_instance = nullptr; + delete instance; +} + +/// @addr{0x8081B324} +ObjectDrivableDirector::ObjectDrivableDirector() = default; + +/// @addr{0x8081B380} +ObjectDrivableDirector::~ObjectDrivableDirector() { + if (s_instance) { + s_instance = nullptr; + WARN("ObjectDrivableDirector instance not explicitly handled!"); + } +} + +ObjectDrivableDirector *ObjectDrivableDirector::s_instance = nullptr; ///< @addr{0x809C4310} + +} // namespace Field diff --git a/source/game/field/ObjectDrivableDirector.hh b/source/game/field/ObjectDrivableDirector.hh new file mode 100644 index 00000000..d1c57f00 --- /dev/null +++ b/source/game/field/ObjectDrivableDirector.hh @@ -0,0 +1,56 @@ +#pragma once + +#include "game/field/obj/ObjectCollidable.hh" +#include "game/field/obj/ObjectDrivable.hh" + +#include + +namespace Field { + +class ObjectDrivableDirector : EGG::Disposer { +public: + void init(); + void calc(); + void addObject(ObjectDrivable *obj); + + [[nodiscard]] bool checkSpherePartial(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSpherePartialPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSphereFull(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSphereFullPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSphereCachedPartial(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSphereCachedPartialPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info, + KCLTypeMask *maskOut, u32 timeOffset); + [[nodiscard]] bool checkSphereCachedFullPush(f32 radius, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, + KCLTypeMask *maskOut, u32 timeOffset); + void colNarScLocal(f32 radius, const EGG::Vector3f &pos, KCLTypeMask mask, u32 timeOffset); + + static ObjectDrivableDirector *CreateInstance(); + static void DestroyInstance(); + + [[nodiscard]] static ObjectDrivableDirector *Instance() { + return s_instance; + } + +private: + ObjectDrivableDirector(); + ~ObjectDrivableDirector() override; + + std::vector m_objects; ///< All objects live here + std::vector m_calcObjects; ///< Objects needing calc() live here too. + + static ObjectDrivableDirector *s_instance; +}; + +} // namespace Field diff --git a/source/game/field/obj/ObjectDrivable.cc b/source/game/field/obj/ObjectDrivable.cc index 78d398f2..6ca37dd0 100644 --- a/source/game/field/obj/ObjectDrivable.cc +++ b/source/game/field/obj/ObjectDrivable.cc @@ -1,4 +1,6 @@ -#include "game/field/obj/ObjectDrivable.hh" +#include "ObjectDrivable.hh" + +#include "game/field/ObjectDrivableDirector.hh" namespace Field { @@ -14,12 +16,7 @@ void ObjectDrivable::load() { initCollision(); loadAABB(getCollisionRadius()); - // ObjectDrivableDirector::Instance()->addObject(this); -} - -/// @addr{0x80682918} -f32 ObjectDrivable::getCollisionRadius() const { - return 5000.0f; + ObjectDrivableDirector::Instance()->addObject(this); } /// @addr{0x8081A85C} diff --git a/source/game/field/obj/ObjectDrivable.hh b/source/game/field/obj/ObjectDrivable.hh index 269f25b3..c08cb50a 100644 --- a/source/game/field/obj/ObjectDrivable.hh +++ b/source/game/field/obj/ObjectDrivable.hh @@ -13,7 +13,11 @@ public: ~ObjectDrivable() override; void load() override; - [[nodiscard]] f32 getCollisionRadius() const override; + + /// @addr{0x80682918} + [[nodiscard]] f32 getCollisionRadius() const override { + return 5000.0f; + } virtual void initCollision() {} virtual void loadAABB(f32 radius); @@ -44,8 +48,8 @@ public: const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfo *info, KCLTypeMask *maskOut, u32 timeOffset) = 0; - virtual void narrScLocal(f32 radius, const EGG::Vector3f &pos, KCLTypeMask mask, - u32 timeOffset) {} + virtual void narrScLocal(f32 /*radius*/, const EGG::Vector3f & /*pos*/, KCLTypeMask /*mask*/, + u32 /*timeOffset*/) {} [[nodiscard]] virtual bool checkPointCachedPartial(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask mask, CollisionInfoPartial *info,