diff --git a/data/uking_functions.csv b/data/uking_functions.csv index ee40b6df..37dbc534 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -83797,17 +83797,17 @@ Address,Quality,Size,Name 0x0000007100fb503c,O,000096,_ZThn32_N4ksys4phys17EntityGroupFilterD0Ev 0x0000007100fb509c,O,000096,_ZThn40_N4ksys4phys17EntityGroupFilterD0Ev 0x0000007100fb50fc,O,000032,_ZN4ksys4phys17EntityGroupFilter7doInit_EPN4sead4HeapE -0x0000007100fb511c,U,001440,phys::EntityGroupFilter::isCollisionEnabled +0x0000007100fb511c,m,001440,_ZNK4ksys4phys17EntityGroupFilter24testCollisionForEntitiesEjj 0x0000007100fb56bc,O,000268,_ZNK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK13hkpCollidableS4_ 0x0000007100fb57c8,O,000028,_ZThn16_NK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK13hkpCollidableS4_ 0x0000007100fb57e4,O,000176,_ZNK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK17hkpCollisionInputRK9hkpCdBodyS7_RK17hkpShapeContainerSA_jj 0x0000007100fb5894,O,000176,_ZThn24_NK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK17hkpCollisionInputRK9hkpCdBodyS7_RK17hkpShapeContainerSA_jj 0x0000007100fb5944,O,000304,_ZNK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK17hkpCollisionInputRK9hkpCdBodyS7_RK17hkpShapeContainerj 0x0000007100fb5a74,O,000028,_ZThn24_NK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK17hkpCollisionInputRK9hkpCdBodyS7_RK17hkpShapeContainerj -0x0000007100fb5a90,U,000284,phys::EntityGroupFilter::isCollisionEnabled__3 -0x0000007100fb5bac,U,000284, -0x0000007100fb5cc8,U,000256,phys::EntityGroupFilter::isCollisionEnabled__4 -0x0000007100fb5dc8,U,000028, +0x0000007100fb5a90,O,000284,_ZNK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK20hkpShapeRayCastInputRK17hkpShapeContainerj +0x0000007100fb5bac,O,000284,_ZThn32_NK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK20hkpShapeRayCastInputRK17hkpShapeContainerj +0x0000007100fb5cc8,O,000256,_ZNK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK20hkpWorldRayCastInputRK13hkpCollidable +0x0000007100fb5dc8,O,000028,_ZThn40_NK4ksys4phys17EntityGroupFilter18isCollisionEnabledERK20hkpWorldRayCastInputRK13hkpCollidable 0x0000007100fb5de4,O,000192,_ZN4ksys4phys17EntityGroupFilter30doInitSystemGroupHandlerLists_EPN4sead4HeapE 0x0000007100fb5ea4,O,000016,_ZN4ksys4phys17EntityGroupFilter16getFreeListIndexEPKNS0_18SystemGroupHandlerE 0x0000007100fb5eb4,O,000036,_ZN4ksys4phys21orEntityGroundHitMaskEjNS0_9GroundHitE @@ -83815,29 +83815,29 @@ Address,Quality,Size,Name 0x0000007100fb5f14,O,000032,_ZN4ksys4phys23makeEntityGroundHitMaskENS0_12ContactLayerEj 0x0000007100fb5f34,O,000068,_ZN4ksys4phys23makeEntityCollisionMaskENS0_12ContactLayerEj 0x0000007100fb5f78,O,000036,_ZN4ksys4phys31setEntityCollisionMaskGroundHitENS0_9GroundHitEj -0x0000007100fb5f9c,U,000084,EntitySystemGroupHandler::m5 -0x0000007100fb5ff0,U,000048,EntitySystemGroupHandler::m6 -0x0000007100fb6020,U,000040,EntitySystemGroupHandler::m7 -0x0000007100fb6048,U,000020,EntitySystemGroupHandler::m8 +0x0000007100fb5f9c,O,000084,_ZN4ksys4phys24EntitySystemGroupHandler23makeCollisionFilterInfoEjNS0_12ContactLayerENS0_9GroundHitE +0x0000007100fb5ff0,O,000048,_ZN4ksys4phys24EntitySystemGroupHandler22makeQueryCollisionMaskEjNS0_9GroundHitEb +0x0000007100fb6020,O,000040,_ZN4ksys4phys24EntitySystemGroupHandler30makeRagdollCollisionFilterInfoENS0_9GroundHitE +0x0000007100fb6048,O,000020,_ZN4ksys4phys24EntitySystemGroupHandler2m8Ev 0x0000007100fb605c,O,000112,_ZNK4ksys4phys18SystemGroupHandler27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE 0x0000007100fb60cc,O,000092,_ZNK4ksys4phys18SystemGroupHandler18getRuntimeTypeInfoEv 0x0000007100fb6128,O,000004,_ZN4ksys4phys18SystemGroupHandlerD2Ev -0x0000007100fb612c,O,000004,_ZN4ksys4phys18SystemGroupHandlerD0Ev +0x0000007100fb612c,O,000004,_ZN4ksys4phys24EntitySystemGroupHandlerD0Ev 0x0000007100fb6130,O,000004,_ZN18hkpCollisionFilter4initEP8hkpWorld 0x0000007100fb6134,O,000008,_ZN14hkpGroupFilter11dummyUnusedEv 0x0000007100fb613c,O,000204,_ZNK4ksys4phys17EntityGroupFilter27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE 0x0000007100fb6208,O,000092,_ZNK4ksys4phys17EntityGroupFilter18getRuntimeTypeInfoEv -0x0000007100fb6264,U,000044,phys::EntityGroupFilter::m2 -0x0000007100fb6290,U,000028,phys::EntityGroupFilter::m3 -0x0000007100fb62ac,U,000020,phys::EntityGroupFilter::m4 -0x0000007100fb62c0,U,000028,phys::EntityGroupFilter::m5 -0x0000007100fb62dc,U,000008,phys::EntityGroupFilter::m6 -0x0000007100fb62e4,U,000040,phys::EntityGroupFilter::m7 -0x0000007100fb630c,U,000048,phys::EntityGroupFilter::m8_GetStringGroundHitMaskModeOrSensorText +0x0000007100fb6264,O,000044,_ZN4ksys4phys17EntityGroupFilter2m2ENS0_12ContactLayerES2_ +0x0000007100fb6290,O,000028,_ZN4ksys4phys17EntityGroupFilter23makeCollisionFilterInfoENS0_12ContactLayerENS0_9GroundHitE +0x0000007100fb62ac,O,000020,_ZN4ksys4phys17EntityGroupFilter27getCollisionFilterInfoLayerEj +0x0000007100fb62c0,O,000028,_ZN4ksys4phys17EntityGroupFilter22makeQueryCollisionMaskEjNS0_9GroundHitEb +0x0000007100fb62dc,O,000008,_ZN4ksys4phys17EntityGroupFilter30getQueryCollisionMaskGroundHitEj +0x0000007100fb62e4,O,000040,_ZN4ksys4phys17EntityGroupFilter39getCollisionFilterInfoLayerAndGroundHitEjPNS0_12ContactLayerEPNS0_9GroundHitE +0x0000007100fb630c,O,000048,_ZN4ksys4phys17EntityGroupFilter31getCollisionFilterInfoLayerTextEj 0x0000007100fb633c,O,000036,_ZN4ksys4phys17EntityGroupFilter18setLayerCustomMaskENS0_12ContactLayerEj -0x0000007100fb6360,U,000008,phys::EntityGroupFilter::m10 -0x0000007100fb6368,U,000044,phys::EntityGroupFilter::m14 -0x0000007100fb6394,U,000028,phys::EntityGroupFilter::m15 +0x0000007100fb6360,O,000008,_ZN4ksys4phys17EntityGroupFilter37getCollisionFilterInfoGroupHandlerIdxEj +0x0000007100fb6368,O,000044,_ZN4ksys4phys17EntityGroupFilter23makeCollisionFilterInfoENS0_12ContactLayerENS0_9GroundHitEjj +0x0000007100fb6394,O,000028,_ZN4ksys4phys17EntityGroupFilter34setEntityLayerCollisionEnabledMaskENS0_12ContactLayerEj 0x0000007100fb63b0,O,000140,_ZNK4sead15RuntimeTypeInfo6DeriveIN4ksys4phys11GroupFilterEE9isDerivedEPKNS0_9InterfaceE 0x0000007100fb643c,U,000108,phys::makeEntityContactListener 0x0000007100fb64a8,U,000004,j_ksys::phys::ContactListener::dtor @@ -93391,7 +93391,7 @@ Address,Quality,Size,Name 0x0000007101213c68,O,000148,_ZN4ksys4phys11GroupFilter7destroyEv 0x0000007101213cfc,O,000160,_ZN4ksys4phys11GroupFilter21addSystemGroupHandlerEi 0x0000007101213d9c,O,000148,_ZN4ksys4phys11GroupFilter24removeSystemGroupHandlerEPNS0_18SystemGroupHandlerE -0x0000007101213e30,O,000008,_ZN4ksys4phys18SystemGroupHandler2m7Ev +0x0000007101213e30,O,000008,_ZN4ksys4phys18SystemGroupHandler30makeRagdollCollisionFilterInfoENS0_9GroundHitE 0x0000007101213e38,O,000024,_ZN4ksys4phys18SystemGroupHandler10removeThisEv 0x0000007101213e50,O,000112,_ZNK4ksys4phys11GroupFilter27checkDerivedRuntimeTypeInfoEPKN4sead15RuntimeTypeInfo9InterfaceE 0x0000007101213ec0,O,000092,_ZNK4ksys4phys11GroupFilter18getRuntimeTypeInfoEv diff --git a/lib/hkStubs/Havok/Physics2012/Collide/Agent/Collidable/hkpCollidable.h b/lib/hkStubs/Havok/Physics2012/Collide/Agent/Collidable/hkpCollidable.h index b30c4644..841acf14 100644 --- a/lib/hkStubs/Havok/Physics2012/Collide/Agent/Collidable/hkpCollidable.h +++ b/lib/hkStubs/Havok/Physics2012/Collide/Agent/Collidable/hkpCollidable.h @@ -63,6 +63,7 @@ public: inline hkUint32 getCollisionFilterInfo() const; inline void setCollisionFilterInfo(hkUint32 info); + /// @see hkpWorldObject::BroadPhaseType inline int getType() const; protected: diff --git a/lib/hkStubs/Havok/Physics2012/Collide/Filter/hkpRayCollidableFilter.h b/lib/hkStubs/Havok/Physics2012/Collide/Filter/hkpRayCollidableFilter.h index e909d410..047ad5f0 100644 --- a/lib/hkStubs/Havok/Physics2012/Collide/Filter/hkpRayCollidableFilter.h +++ b/lib/hkStubs/Havok/Physics2012/Collide/Filter/hkpRayCollidableFilter.h @@ -2,8 +2,8 @@ #include -class hkpWorldRayCastInput; class hkpCollidable; +struct hkpWorldRayCastInput; class hkpRayCollidableFilter { public: diff --git a/src/KingSystem/Physics/RigidBody/physRigidBody.cpp b/src/KingSystem/Physics/RigidBody/physRigidBody.cpp index e459b478..4bbee758 100644 --- a/src/KingSystem/Physics/RigidBody/physRigidBody.cpp +++ b/src/KingSystem/Physics/RigidBody/physRigidBody.cpp @@ -602,8 +602,8 @@ void RigidBody::enableGroundCollision(bool enabled) { const auto current_info = getEntityCollisionFilterInfo(); auto info = current_info; - info.unk5 = false; - info.no_ground_collision.SetBit(!enabled); + info.ground_col_mode = + enabled ? GroundCollisionMode::Normal : GroundCollisionMode::IgnoreGround; if (current_info != info) setCollisionFilterInfo(info.raw); } @@ -615,9 +615,8 @@ bool RigidBody::isGroundCollisionEnabled() const { const auto info = getEntityCollisionFilterInfo(); bool enabled = false; - enabled |= info.unk5; + enabled |= info.ground_col_mode != GroundCollisionMode::IgnoreGround; enabled |= info.unk30; - enabled |= !info.no_ground_collision; return enabled; } @@ -630,7 +629,7 @@ void RigidBody::enableWaterCollision(bool enabled) { const auto current_info = getEntityCollisionFilterInfo(); auto info = current_info; - info.no_water_collision = !enabled; + info.water_col_mode = enabled ? WaterCollisionMode::Normal : WaterCollisionMode::IgnoreWater; if (current_info != info) setCollisionFilterInfo(info.raw); } @@ -644,7 +643,7 @@ bool RigidBody::isWaterCollisionEnabled() const { bool enabled = false; // unk30 enables all collisions? enabled |= info.unk30; - enabled |= !info.no_water_collision; + enabled |= info.water_col_mode != WaterCollisionMode::IgnoreWater; return enabled; } diff --git a/src/KingSystem/Physics/System/physEntityGroupFilter.cpp b/src/KingSystem/Physics/System/physEntityGroupFilter.cpp index 1db69753..69c9cd92 100644 --- a/src/KingSystem/Physics/System/physEntityGroupFilter.cpp +++ b/src/KingSystem/Physics/System/physEntityGroupFilter.cpp @@ -2,11 +2,15 @@ #include #include #include +#include #include #include +#include #include #include #include +#include "Havok/Physics2012/Dynamics/Entity/hkpEntity.h" +#include "KingSystem/Physics/RigidBody/physRigidBody.h" #include "KingSystem/Physics/System/physContactMgr.h" #include "KingSystem/Physics/System/physSystem.h" #include "KingSystem/Utils/BitField.h" @@ -49,16 +53,170 @@ void EntityGroupFilter::doInit_(sead::Heap* heap) { mMasks.fill(0xffffffff); } -hkBool EntityGroupFilter::isCollisionEnabledPhantom(u32 infoPhantom, u32 infoB) const { +hkBool EntityGroupFilter::shouldHandleGroundCollision(u32 infoA, u32 infoB, + ContactLayer::ValueType layerA, + ContactLayer::ValueType layerB) const { + const EntityCollisionFilterInfo a{infoA}; + const EntityCollisionFilterInfo b{infoB}; + + if (EntityCollisionFilterInfo(infoA | infoB).ground_col_mode != GroundCollisionMode::Normal) { + if (a.ground_col_mode != GroundCollisionMode::Normal) { + bool ground = isEntityGroundLayer(layerB); + if (a.ground_col_mode == GroundCollisionMode::IgnoreNonGround && !ground) + return false; + if (a.ground_col_mode == GroundCollisionMode::IgnoreGround && ground) + return false; + } + + if (b.ground_col_mode != GroundCollisionMode::Normal) { + bool ground = isEntityGroundLayer(layerA); + if (b.ground_col_mode == GroundCollisionMode::IgnoreNonGround && !ground) + return false; + if (b.ground_col_mode == GroundCollisionMode::IgnoreGround && ground) + return false; + } + } + return true; +} + +hkBool EntityGroupFilter::shouldHandleWaterCollision(u32 infoA, u32 infoB, + ContactLayer::ValueType layerA, + ContactLayer::ValueType layerB) const { + const EntityCollisionFilterInfo a{infoA}; + const EntityCollisionFilterInfo b{infoB}; + + if (EntityCollisionFilterInfo(infoA | infoB).water_col_mode != WaterCollisionMode::Normal) { + if (a.water_col_mode == WaterCollisionMode::IgnoreWater && + layerB == ContactLayer::EntityWater) { + return false; + } + if (b.water_col_mode == WaterCollisionMode::IgnoreWater && + layerA == ContactLayer::EntityWater) { + return false; + } + } + return true; +} + +// XXX: find a better name +static bool testHandler(u32 idx) { + return idx != 0 && idx <= 15; +} + +// NON_MATCHING: deduplicated branch: `return a.data.query_custom_receiver_layer_mask & (1 << ...)` +hkBool EntityGroupFilter::testCollisionForEntities(u32 infoA, u32 infoB) const { if (mInhibitCollisions) return false; - // TODO: figure out what kind of mask infoPhantom is. Receiver/sensor mask? - // RigidBodyParam::getParams and ContactInfoTable seem to manipulate similar looking masks. + const EntityCollisionFilterInfo a{infoA}; + const EntityCollisionFilterInfo b{infoB}; + + constexpr auto GroupHandlerIdxMask = decltype(a.group_handler_index)::GetMask(); + constexpr auto GroupHandlerIdxShift = decltype(a.group_handler_index)::StartBit(); + + if (!EntityCollisionFilterInfo(infoA | infoB).is_ground_hit_mask) { + if (a.unk30 && b.unk30) { + if (((infoA ^ infoB) & GroupHandlerIdxMask) != 0) { + if (testHandler(a.group_handler_index) || testHandler(b.group_handler_index)) + return false; + } else if ((infoA & GroupHandlerIdxMask) >> GroupHandlerIdxShift != 0) { + if (a.data.unk5 == b.data.unk10 || b.data.unk5 == a.data.unk10) + return false; + } + return true; + } + + const auto layerA = static_cast(a.data.layer.Value()); + const auto layerB = static_cast(b.data.layer.Value()); + + if (layerA != ContactLayer::EntityQueryCustomReceiver && + layerB != ContactLayer::EntityQueryCustomReceiver) { + if (!a.unk30 && !b.unk30) { + if (!shouldHandleGroundCollision(infoA, infoB, layerA, layerB)) + return false; + if (!shouldHandleWaterCollision(infoA, infoB, layerA, layerB)) + return false; + } + + if (((infoA ^ infoB) & GroupHandlerIdxMask) != 0) { + if (testHandler(a.group_handler_index) || testHandler(b.group_handler_index)) + return false; + } else if (((infoA & GroupHandlerIdxMask) >> GroupHandlerIdxShift) > 15) { + return false; + } + return testLayerCollision(layerA, layerB); + } + + if (layerA == ContactLayer::EntityQueryCustomReceiver && + layerB == ContactLayer::EntityQueryCustomReceiver) { + return false; + } + + if (layerA == ContactLayer::EntityQueryCustomReceiver) + return a.data.query_custom_receiver_layer_mask & (1 << layerB); + else + return b.data.query_custom_receiver_layer_mask & (1 << layerA); + } + + if (a.is_ground_hit_mask && b.is_ground_hit_mask) { + const auto layerA = static_cast(a.ground_hit.layer.Value()); + const auto layerB = static_cast(b.ground_hit.layer.Value()); + + if (!shouldHandleGroundCollision(infoA, infoB, layerA, layerB)) + return false; + if (!shouldHandleWaterCollision(infoA, infoB, layerA, layerB)) + return false; + if (!testLayerCollision(layerA, layerB)) + return false; + return !a.ground_hit.unk23 && !b.ground_hit.unk23; + } + + EntityCollisionFilterInfo entity_mask, ground_hit_mask; + + if (a.is_ground_hit_mask && !b.is_ground_hit_mask) { + const auto layerA = static_cast(a.ground_hit.layer.Value()); + const auto layerB = static_cast(b.data.layer.Value()); + entity_mask = b; + ground_hit_mask = a; + + if (layerB == ContactLayer::EntityQueryCustomReceiver) + return b.data.query_custom_receiver_layer_mask & (1 << layerA); + + if (!b.unk30 && !shouldHandleGroundCollision(infoA, infoB, layerA, layerB)) + return false; + if (!b.unk30 && !shouldHandleWaterCollision(infoA, infoB, layerA, layerB)) + return false; + if (!testLayerCollision(layerA, layerB)) + return false; + + } else /* A entity, B ground hit */ { + const auto layerA = static_cast(a.data.layer.Value()); + const auto layerB = static_cast(b.ground_hit.layer.Value()); + entity_mask = a; + ground_hit_mask = b; + + if (layerA == ContactLayer::EntityQueryCustomReceiver) + return a.data.query_custom_receiver_layer_mask & (1 << layerB); + + if (!a.unk30 && !shouldHandleGroundCollision(infoA, infoB, layerA, layerB)) + return false; + if (!a.unk30 && !shouldHandleWaterCollision(infoA, infoB, layerA, layerB)) + return false; + if (!testLayerCollision(layerB, layerA)) + return false; + } + return !(ground_hit_mask.ground_hit.ground_hit_types & (1 << entity_mask.data.ground_hit)); +} + +hkBool EntityGroupFilter::testCollisionForPhantom(u32 infoPhantom, u32 infoB) const { + if (mInhibitCollisions) + return false; + + RayCastCollisionMask infoPhantomData{infoPhantom}; const EntityCollisionFilterInfo info{infoB}; if (info.is_ground_hit_mask) - return infoPhantom & (1 << info.ground_hit.getLayer()); - return (infoPhantom & (1 << info.data.layer)) & 0x1ffff; + return infoPhantomData.raw & (1 << info.ground_hit.getLayer()); + return infoPhantomData.layer_mask & (1 << info.data.layer); } hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollidable& a, const hkpCollidable& b) const { @@ -69,19 +227,19 @@ hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollidable& a, const hkpCo if (a.getType() == hkpWorldObject::BROAD_PHASE_PHANTOM) { if (a.getShape() != nullptr) - return isCollisionEnabled(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); + return testCollisionForEntities(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); - return isCollisionEnabledPhantom(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); + return testCollisionForPhantom(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); } if (b.getType() == hkpWorldObject::BROAD_PHASE_PHANTOM) { if (b.getShape() != nullptr) - return isCollisionEnabled(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); + return testCollisionForEntities(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); - return isCollisionEnabledPhantom(b.getCollisionFilterInfo(), a.getCollisionFilterInfo()); + return testCollisionForPhantom(b.getCollisionFilterInfo(), a.getCollisionFilterInfo()); } - return isCollisionEnabled(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); + return testCollisionForEntities(a.getCollisionFilterInfo(), b.getCollisionFilterInfo()); } hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollisionInput& input, @@ -98,7 +256,7 @@ hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollisionInput& input, if (infoB == 0xffffffff) infoB = collectionBodyB.getRootCollidable()->getCollisionFilterInfo(); - return isCollisionEnabled(infoA, infoB); + return testCollisionForEntities(infoA, infoB); } hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollisionInput& input, const hkpCdBody& a, @@ -150,7 +308,72 @@ hkBool EntityGroupFilter::isCollisionEnabled(const hkpCollisionInput& input, con } end: - return isCollisionEnabled(infoA, infoB); + return testCollisionForEntities(infoA, infoB); +} + +static hkBool +checkCollisionWithGroundHitMask(EntityCollisionFilterInfo::GroundHitMask ground_hit_mask, + RayCastCollisionMask ray_cast) { + if (!(ray_cast.layer_mask & (1 << ground_hit_mask.getLayer()))) + return false; + + if (ground_hit_mask.unk & ray_cast.unk) + return false; + + if (ground_hit_mask.ground_hit_types & (1 << ray_cast.ground_hit_type)) + return false; + + return true; +} + +hkBool EntityGroupFilter::testCollisionForRayCasting(u32 infoRayCast, u32 info) const { + if (mInhibitCollisions) + return false; + + RayCastCollisionMask a{infoRayCast}; + EntityCollisionFilterInfo b{info}; + + if (b.is_ground_hit_mask) + return checkCollisionWithGroundHitMask(b.ground_hit, a); + + const u32 bHandlerIdx = b.group_handler_index; + const u32 aHandlerIdx = a.group_handler_index; + + if (aHandlerIdx == bHandlerIdx) { + if (bHandlerIdx > 15) + return false; + return a.layer_mask & (1 << b.data.layer); + } + + if (testHandler(aHandlerIdx) || testHandler(bHandlerIdx)) + return false; + + return a.layer_mask & (1 << b.data.layer); +} + +KSYS_ALWAYS_INLINE hkBool EntityGroupFilter::isCollisionEnabled(const hkpShapeRayCastInput& aInput, + const hkpShapeContainer& bContainer, + hkpShapeKey bKey) const { + u32 bInfo = bContainer.getCollisionFilterInfo(bKey); + if (bInfo == 0) + return true; + + if (bInfo == 0xffffffff) + bInfo = aInput.m_collidable->getCollisionFilterInfo(); + + return testCollisionForRayCasting(aInput.m_filterInfo, bInfo); +} + +hkBool EntityGroupFilter::isCollisionEnabled(const hkpWorldRayCastInput& inputA, + const hkpCollidable& collidableB) const { + if (collidableB.getType() == hkpWorldObject::BROAD_PHASE_ENTITY) { + auto* entity = static_cast(collidableB.getOwner()); + auto* body = entity ? reinterpret_cast(entity->getUserData()) : nullptr; + if (body && body->hasFlag(RigidBody::Flag::_200)) + return false; + } + + return testCollisionForRayCasting(inputA.m_filterInfo, collidableB.getCollisionFilterInfo()); } void EntityGroupFilter::doInitSystemGroupHandlerLists_(sead::Heap* heap) { diff --git a/src/KingSystem/Physics/System/physEntityGroupFilter.h b/src/KingSystem/Physics/System/physEntityGroupFilter.h index a5a8689c..2afed9f0 100644 --- a/src/KingSystem/Physics/System/physEntityGroupFilter.h +++ b/src/KingSystem/Physics/System/physEntityGroupFilter.h @@ -13,7 +13,8 @@ public: explicit EntitySystemGroupHandler(int i) : SystemGroupHandler(i, ContactLayerType::Entity) {} u32 makeCollisionFilterInfo(u32 info, ContactLayer layer, GroundHit ground_hit) override; - u32 m6() override; + u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) override; + u32 makeRagdollCollisionFilterInfo(GroundHit ground_hit) override; bool m8() override; }; @@ -40,27 +41,42 @@ public: hkBool isCollisionEnabled(const hkpWorldRayCastInput& inputA, const hkpCollidable& collidableB) const override; - bool m2() override { return GroupFilter::m2(); } - void m3() override {} - void m4() override {} - void m5() override {} - void m6() override {} - void m7() override {} - void m8() override {} + bool m2(ContactLayer layerA, ContactLayer layerB) override; + u32 makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit) override; + ContactLayer getCollisionFilterInfoLayer(u32 info) override; + u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) override; + GroundHit getQueryCollisionMaskGroundHit(u32 info) override; + void getCollisionFilterInfoLayerAndGroundHit(u32 info, ContactLayer* layer, + GroundHit* ground_hit) override; + const char* getCollisionFilterInfoLayerText(u32 info) override; + void setLayerCustomMask(ContactLayer layer, u32 mask) override; + u32 getCollisionFilterInfoGroupHandlerIdx(u32 info) override; - void setLayerCustomMask(ContactLayer layer, u32 mask) override { mMasks[layer] = mask; } - - void m10() override {} + virtual u32 makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit, u32 unk5, + u32 unk10); + /// @param layer An entity layer + virtual void setEntityLayerCollisionEnabledMask(ContactLayer layer, u32 mask); private: - hkBool isCollisionEnabled(u32 infoA, u32 infoB) const; - hkBool isCollisionEnabledPhantom(u32 infoPhantom, u32 infoB) const; + /// Checks whether two entities are colliding. + hkBool testCollisionForEntities(u32 infoA, u32 gh_mask) const; + /// Checks whether a phantom and an entity are colliding with each other. + hkBool testCollisionForPhantom(u32 infoPhantom, u32 infoB) const; + /// Checks whether a ray cast and an entity are colliding with each other. + hkBool testCollisionForRayCasting(u32 infoRayCast, u32 info) const; + + hkBool shouldHandleGroundCollision(u32 infoA, u32 infoB, ContactLayer::ValueType layerA, + ContactLayer::ValueType layerB) const; + hkBool shouldHandleWaterCollision(u32 infoA, u32 infoB, ContactLayer::ValueType layerA, + ContactLayer::ValueType layerB) const; + + hkBool testLayerCollision(ContactLayer::ValueType a, ContactLayer::ValueType b) const { + return m_collisionLookupTable[a] & (1 << b); + } void doInitSystemGroupHandlerLists_(sead::Heap* heap) override; int getFreeListIndex(const SystemGroupHandler* handler) override; void doInit_(sead::Heap* heap) override; - virtual void m14(); - virtual void m15(); sead::SafeArray mMasks; }; @@ -84,4 +100,111 @@ u32 makeEntityCollisionMask(ContactLayer layer, u32 mask); /// Updates the collision mask with the specified ground hit type (*not* mask). u32 setEntityCollisionMaskGroundHit(GroundHit ground_hit, u32 mask); +inline u32 EntitySystemGroupHandler::makeCollisionFilterInfo(u32 info, ContactLayer layer, + GroundHit ground_hit) { + const EntityCollisionFilterInfo current_info{info}; + EntityCollisionFilterInfo result; + + if (layer == ContactLayer::EntityRagdoll) { + result.data.layer.Init(layer); + result.data.unk5.Init(current_info.data.unk5); + result.data.unk10.Init(current_info.data.unk10); + result.group_handler_index.Init(getIndex()); + result.data.ground_hit.Init(ground_hit); + result.unk30 = true; + } else { + result.data.layer.Init(layer); + result.ground_col_mode.Init(current_info.ground_col_mode); + result.group_handler_index.Init(getIndex()); + result.data.ground_hit.Init(ground_hit); + } + return result.raw; +} + +inline u32 EntitySystemGroupHandler::makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, + bool unk) { + RayCastCollisionMask mask; + mask.layer_mask = layer_mask; + mask.group_handler_index.Init(getIndex()); + mask.ground_hit_type.Init(static_cast(int(ground_hit))); + mask.unk.SetBit(unk); + return mask.raw; +} + +inline u32 EntitySystemGroupHandler::makeRagdollCollisionFilterInfo(GroundHit ground_hit) { + EntityCollisionFilterInfo info; + info.data.layer.Init(ContactLayer::EntityRagdoll); + info.group_handler_index.Init(getIndex()); + info.data.ground_hit.Init(ground_hit); + return info.raw; +} + +inline bool EntitySystemGroupHandler::m8() { + return getIndex() > 0 && getIndex() < 0x400; +} + +inline bool EntityGroupFilter::m2(ContactLayer layerA, ContactLayer layerB) { + return (mMasks[layerA.value()] & (1 << layerB.value())) == 0; +} + +inline u32 EntityGroupFilter::makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit) { + return EntityCollisionFilterInfo::make(layer, ground_hit).raw; +} + +inline ContactLayer EntityGroupFilter::getCollisionFilterInfoLayer(u32 info) { + return EntityCollisionFilterInfo(info).getLayer(); +} + +inline u32 EntityGroupFilter::makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, + bool unk) { + RayCastCollisionMask mask; + mask.layer_mask = layer_mask; + mask.ground_hit_type = ground_hit.value(); + mask.unk.SetBit(unk); + return mask.raw; +} + +inline GroundHit EntityGroupFilter::getQueryCollisionMaskGroundHit(u32 info) { + return RayCastCollisionMask(info).ground_hit_type.Value(); +} + +inline void EntityGroupFilter::getCollisionFilterInfoLayerAndGroundHit(u32 info, + ContactLayer* layer, + GroundHit* ground_hit) { + EntityCollisionFilterInfo info_{info}; + *layer = info_.getLayer(); + *ground_hit = info_.getGroundHit(); +} + +inline const char* EntityGroupFilter::getCollisionFilterInfoLayerText(u32 info) { + EntityCollisionFilterInfo info_{info}; + if (info_.is_ground_hit_mask) { + return "GroundHitMaskMode"; + } + return contactLayerToText(getCollisionFilterInfoLayer(info)); +} + +inline void EntityGroupFilter::setLayerCustomMask(ContactLayer layer, u32 mask) { + mMasks[layer] = mask; +} + +inline u32 EntityGroupFilter::getCollisionFilterInfoGroupHandlerIdx(u32 info) { + return EntityCollisionFilterInfo(info).group_handler_index; +} + +inline u32 EntityGroupFilter::makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit, + u32 unk5, u32 unk10) { + EntityCollisionFilterInfo info; + info.data.layer.Init(layer); + info.data.unk5.Init(unk5); + info.data.unk10.Init(unk10); + info.data.ground_hit.Init(ground_hit); + info.unk30 = true; + return info.raw; +} + +inline void EntityGroupFilter::setEntityLayerCollisionEnabledMask(ContactLayer layer, u32 mask) { + m_collisionLookupTable[layer] = mask; +} + } // namespace ksys::phys diff --git a/src/KingSystem/Physics/System/physGroupFilter.cpp b/src/KingSystem/Physics/System/physGroupFilter.cpp index 9bb8c3c8..5e0c817b 100644 --- a/src/KingSystem/Physics/System/physGroupFilter.cpp +++ b/src/KingSystem/Physics/System/physGroupFilter.cpp @@ -50,7 +50,7 @@ void GroupFilter::removeSystemGroupHandler(SystemGroupHandler* handler) { mUsedList.erase(handler); } -u32 SystemGroupHandler::m7() { +u32 SystemGroupHandler::makeRagdollCollisionFilterInfo(GroundHit ground_hit) { return 0; } diff --git a/src/KingSystem/Physics/System/physGroupFilter.h b/src/KingSystem/Physics/System/physGroupFilter.h index cb7843c1..67dd1118 100644 --- a/src/KingSystem/Physics/System/physGroupFilter.h +++ b/src/KingSystem/Physics/System/physGroupFilter.h @@ -18,8 +18,8 @@ public: virtual ~SystemGroupHandler() = default; virtual u32 makeCollisionFilterInfo(u32 info, ContactLayer layer, GroundHit ground_hit) = 0; - virtual u32 m6() = 0; - virtual u32 m7(); + virtual u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) = 0; + virtual u32 makeRagdollCollisionFilterInfo(GroundHit ground_hit); virtual bool m8() = 0; int getIndex() const { return mIndex; } @@ -75,15 +75,31 @@ public: m_collisionLookupTable[layer - getLayerFirst()] = mask; } - virtual bool m2() { return true; } - virtual void m3() = 0; - virtual void m4() = 0; - virtual void m5() = 0; - virtual void m6() = 0; - virtual void m7() = 0; - virtual void m8() = 0; + virtual bool m2(ContactLayer layerA, ContactLayer layerB) { return true; } + + /// Make a collision filter mask with the specified layer and ground hit type. + virtual u32 makeCollisionFilterInfo(ContactLayer layer, GroundHit ground_hit) = 0; + + /// Get the layer from a collision filter mask. + virtual ContactLayer getCollisionFilterInfoLayer(u32 info) = 0; + + /// Make a query collision mask with the specified layer mask, ground hit type and flag. + virtual u32 makeQueryCollisionMask(u32 layer_mask, GroundHit ground_hit, bool unk) = 0; + + /// Get the ground hit type from a query collision mask. + virtual GroundHit getQueryCollisionMaskGroundHit(u32 info) = 0; + + /// Get the layer and ground hit type from a collision filter mask. + virtual void getCollisionFilterInfoLayerAndGroundHit(u32 info, ContactLayer* layer, + GroundHit* ground_hit) = 0; + + /// Get the layer from a collision filter mask. + virtual const char* getCollisionFilterInfoLayerText(u32 info) = 0; + virtual void setLayerCustomMask(ContactLayer layer, u32 mask) {} - virtual void m10() = 0; + + /// Get the group handler index from a collision filter mask. + virtual u32 getCollisionFilterInfoGroupHandlerIdx(u32 info) = 0; protected: virtual void doInitSystemGroupHandlerLists_(sead::Heap* heap) = 0; diff --git a/src/KingSystem/Physics/physDefines.h b/src/KingSystem/Physics/physDefines.h index 1de8e860..04147d0b 100644 --- a/src/KingSystem/Physics/physDefines.h +++ b/src/KingSystem/Physics/physDefines.h @@ -80,6 +80,11 @@ constexpr auto LastEntity = ContactLayer::EntityMeshVisualizer; constexpr auto FirstSensor = ContactLayer::SensorObject; constexpr auto LastSensor = ContactLayer::SensorCustomReceiver; +constexpr bool isEntityGroundLayer(ContactLayer::ValueType layer) { + return layer == ContactLayer::EntityGround || layer == ContactLayer::EntityGroundSmooth || + layer == ContactLayer::EntityGroundRough; +} + SEAD_ENUM(Material, Undefined,\ Soil,\ @@ -180,6 +185,22 @@ enum class MotionType { Invalid = -1, }; +enum class GroundCollisionMode { + /// Ground collision is not handled in any special way. + Normal = 0, + /// Any collision with a non-ground layer is ignored. + IgnoreNonGround = 1, + /// Any collision with a ground layer is ignored. + IgnoreGround = 2, +}; + +enum class WaterCollisionMode { + /// Water collision is not handled in any special way. + Normal = 0, + /// Any collision with a water layer is ignored. + IgnoreWater = 1, +}; + union ReceiverMask { union Data { util::BitField<0, 5, u32> layer; @@ -229,6 +250,12 @@ union EntityCollisionFilterInfo { u32 raw; util::BitField<0, 5, u32> layer; + // TODO: figure out what this is + util::BitField<5, 5, u32> unk5; + util::BitField<10, 5, u32> unk10; + /// Layers to collide with for EntityQueryCustomReceiver entities. + // XXX: was 17 chosen because ContactLayer::EntityQueryCustomReceiver = 17? + util::BitField<5, 17, u32> query_custom_receiver_layer_mask; util::BitField<24, 1, u32> unk24; util::BitField<25, 1, u32> unk25; util::BitField<26, 4, u32> ground_hit; @@ -244,7 +271,7 @@ union EntityCollisionFilterInfo { u32 raw; util::BitField<0, 1, u32> unk; util::BitField<8, 16, u32> ground_hit_types; - util::BitField<24, 1, u32> unk24; + util::BitField<23, 1, u32> unk23; util::BitField<25, 5, u32> layer; }; @@ -281,17 +308,35 @@ union EntityCollisionFilterInfo { u32 raw; Data data; GroundHitMask ground_hit; - util::BitField<5, 1, bool, u32> unk5; - /// Whether ground collision is disabled. - util::BitField<6, 1, bool, u32> no_ground_collision; - /// Whether water collision is disabled. - util::BitField<7, 1, bool, u32> no_water_collision; + util::BitField<5, 2, GroundCollisionMode, u32> ground_col_mode; + util::BitField<7, 1, WaterCollisionMode, u32> water_col_mode; util::BitField<16, 10, u32> group_handler_index; + /// If this flag is set, then this entity will always collide with ground or water, + /// regardless of the configured GroundCollisionMode or WaterCollisionMode modes. + // TODO: is this "is_ragdoll"? See EntitySystemGroupHandler::makeCollisionFilterInfo. util::BitField<30, 1, bool, u32> unk30; util::BitField<31, 1, bool, u32> is_ground_hit_mask; }; static_assert(sizeof(EntityCollisionFilterInfo) == sizeof(u32)); +/// Collision mask that is used for raycast-based queries. +union RayCastCollisionMask { + constexpr explicit RayCastCollisionMask(u32 raw_ = 0) : raw(raw_) {} + constexpr RayCastCollisionMask(const RayCastCollisionMask&) = default; + constexpr RayCastCollisionMask& operator=(const RayCastCollisionMask& m) { + raw = m.raw; + return *this; + } + constexpr bool operator==(RayCastCollisionMask rhs) const { return raw == rhs.raw; } + constexpr bool operator!=(RayCastCollisionMask rhs) const { return raw != rhs.raw; } + + util::BitField<0, 17, u32> layer_mask; + util::BitField<17, 1, u32> unk; + util::BitField<18, 10, u32> group_handler_index; + util::BitField<28, 4, GroundHit::ValueType, u32> ground_hit_type; + u32 raw; +}; + ContactLayerType getContactLayerType(ContactLayer layer); u32 makeContactLayerMask(ContactLayer layer); u32 getContactLayerBase(ContactLayerType type); diff --git a/src/KingSystem/Utils/BitField.h b/src/KingSystem/Utils/BitField.h index 6bff3986..45a4645d 100644 --- a/src/KingSystem/Utils/BitField.h +++ b/src/KingSystem/Utils/BitField.h @@ -132,8 +132,7 @@ struct BitField { template > inline constexpr void SetBit(bool set) { - const auto mask = set ? ((static_cast(1) << position) & GetMask()) : 0; - storage = (storage & ~GetMask()) | mask; + storage = (storage & ~GetMask()) | (set ? GetMask() : 0); } /// @warning This does *not* check whether the value fits within the mask,