diff options
| author | Léo Lam <leo@leolam.fr> | 2022-03-14 01:41:28 +0100 |
|---|---|---|
| committer | Léo Lam <leo@leolam.fr> | 2022-03-15 01:20:41 +0100 |
| commit | 0c3df3ed97101d91df6bfa7522e268f35885e58b (patch) | |
| tree | d2c5f6a4c2529bb7dbe7ca8ef0adc7d872ebf779 /src/KingSystem/Physics/System/physShapeCast.cpp | |
| parent | 2a9e3318b5b7892bb2c398293138ee1553c96ec0 (diff) | |
ksys/phys: Add ShapeCast
Diffstat (limited to 'src/KingSystem/Physics/System/physShapeCast.cpp')
| -rw-r--r-- | src/KingSystem/Physics/System/physShapeCast.cpp | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/KingSystem/Physics/System/physShapeCast.cpp b/src/KingSystem/Physics/System/physShapeCast.cpp new file mode 100644 index 00000000..cefed97b --- /dev/null +++ b/src/KingSystem/Physics/System/physShapeCast.cpp @@ -0,0 +1,256 @@ +#include "KingSystem/Physics/System/physShapeCast.h" +#include <Havok/Physics2012/Collide/Agent/Collidable/hkpCdPoint.h> +#include <Havok/Physics2012/Collide/Agent/hkpProcessCollisionInput.h> +#include <Havok/Physics2012/Collide/Query/CastUtil/hkpLinearCastInput.h> +#include <Havok/Physics2012/Collide/Query/Collector/PointCollector/hkpAllCdPointCollector.h> +#include "KingSystem/Physics/System/physContactMgr.h" +#include "KingSystem/Physics/System/physEntityContactListener.h" +#include "KingSystem/Physics/System/physGroupFilter.h" +#include "KingSystem/Physics/System/physQueryContactPointInfo.h" +#include "KingSystem/Physics/System/physSystem.h" +#include "KingSystem/Physics/physConversions.h" +#include "KingSystem/Physics/physLayerMaskBuilder.h" + +namespace ksys::phys { + +ShapeCast::ShapeCast(RigidBody* body, QueryContactPointInfo* contact_point_info, Mode mode) + : mContactPointInfo(contact_point_info), mMode(mode) { + if (body) + mBody = body; + + mTo = sead::Vector3f::zero; + mFrom = sead::Vector3f::zero; + mRotation.set(1, 0, 0, 0); + reset(); +} + +ShapeCast::~ShapeCast() = default; + +void ShapeCast::reset() { + _40 = {}; + _41 = {}; + _42 = {}; + _44 = {}; + + if (mContactPointInfo) + mContactPointInfo->getNumContactPoints() = 0; +} + +void ShapeCast::setRotation(sead::Quatf rotation) { + mRotation = rotation; +} + +void ShapeCast::setRotation(const sead::Matrix33f& rotation_matrix) { + auto quat = sead::Quatf::unit; + rotation_matrix.toQuat(quat); + setRotation(quat); +} + +void ShapeCast::setRotation(const sead::Matrix34f& transform_matrix) { + auto quat = sead::Quatf::unit; + transform_matrix.toQuat(quat); + setRotation(quat); +} + +void ShapeCast::setStartAndEnd(const sead::Vector3f& start, const sead::Vector3f& end) { + mFrom = start; + mTo = end; +} + +void ShapeCast::setStartAndDisplacement(const sead::Vector3f& start, + const sead::Vector3f& displacement) { + mFrom = start; + mTo = start + displacement; +} + +void ShapeCast::setStartAndDisplacementScaled(const sead::Vector3f& start, + const sead::Vector3f& displacement, + float displacement_scale) { + mFrom = start; + mTo = start + displacement * displacement_scale; +} + +void ShapeCast::setLayerMasks(const LayerMaskBuilder& mask_builder) { + mContactPointInfo->setLayerMasks(mask_builder); +} + +void ShapeCast::setColors(const sead::Color4f& color1, const sead::Color4f& color2, + const sead::Color4f& color3, bool enabled) { + // Stubbed in release builds. +} + +// NON_MATCHING: the last if block is a complete mess. Should be equivalent, though. +bool ShapeCast::doExecuteQuery(hkpCdPointCollector& cast_collector, + hkpAllCdPointCollector* start_collector, + ShapeCast::WeldClosestPoints weld_closest_points, + OnlyLockIfNeeded only_lock_if_needed) { + const auto layer_type = mBody->getLayerType(); + + System::instance()->lockWorld(layer_type, "shape_cast", 0, only_lock_if_needed); + + hkpLinearCastInput input; + loadFromVec3(&input.m_to, mTo); + + // Reset internal state. + cast_collector.reset(); + if (_44 == 1) + reset(); + + System::instance()->getHavokWorld(layer_type)->m_collisionInput->m_weldClosestPoints = + bool(weld_closest_points); + + doCast(input, cast_collector, start_collector); + + System::instance()->unlockWorld(layer_type, "shape_cast", 0, only_lock_if_needed); + + // Register every point that is in start_collector. + auto* body = mBody; + for (int i = 0, n = start_collector->getNumHits(); i < n; ++i) { + if (registerContactPoint(start_collector->getHits()[i], body, ClampDistance::Yes)) { + _40 = true; + if (mMode == Mode::_0) + break; + } + } + + _41 = _40; + + if (mMode == Mode::_2 || !_40) { + int num_hits = 0; + FilteredClosestCdPointCollector* filtered_collector = nullptr; + hkpAllCdPointCollector* all_point_collector = nullptr; + bool ok = false; + + if (mMode == Mode::_1 || mMode == Mode::_0) { + filtered_collector = static_cast<FilteredClosestCdPointCollector*>(&cast_collector); + num_hits = 1; + ok = filtered_collector->hasHit(); + } else { + all_point_collector = static_cast<hkpAllCdPointCollector*>(&cast_collector); + num_hits = all_point_collector->getNumHits(); + ok = num_hits >= 0; + } + + if (ok) { + for (int i = 0; i < num_hits; ++i) { + const auto& point = mMode == Mode::_2 ? all_point_collector->getHits()[i] : + filtered_collector->getHit(); + if (registerContactPoint(point, body, ClampDistance::No)) + _40 = true; + } + } + + _42 = _40; + } + + _44 = 1; + return _40; +} + +bool ShapeCast::registerContactPoint(const hkpRootCdPoint& point, RigidBody* body, + ClampDistance clamp_distance) { + auto* hit_entity = getHkpEntity(*point.m_rootCollidableB); + if (!hit_entity) + return false; + + if (!mContactPointInfo->testContactPointDistance(point.getContact().getDistance())) + return false; + + auto* hit_body = getRigidBody(hit_entity); + + if (System::instance()->getEntityContactListenerField91() && hit_body->isEntity() && + EntityContactListener::isObjectOrGroundOrNPCOrTree(*hit_body)) { + return false; + } + + RigidBodyCollisionMasks masks_a, masks_b; + sead::Vector3f contact_position; + storeToVec3(&contact_position, point.getContact().getPosition()); + + body->getCollisionMasks(&masks_a, &point.m_shapeKeyA, contact_position); + hit_body->getCollisionMasks(&masks_b, &point.m_shapeKeyB, contact_position); + + auto* filter = System::instance()->getGroupFilter(hit_body->getLayerType()); + auto layer_b = filter->getCollisionFilterInfoLayer(masks_b.collision_filter_info); + + if (!mContactPointInfo->isLayerSubscribed(layer_b)) + return false; + + ContactPoint contact_point; + contact_point.flags.makeAllZero(); + contact_point.body_a = body; + contact_point.body_b = hit_body; + contact_point.material_mask_a = MaterialMaskData(masks_a.material_mask); + contact_point.material_mask_b = MaterialMaskData(masks_b.material_mask); + storeToVec3(&contact_point.position, point.getContact().getPosition()); + storeToVec3(&contact_point.separating_normal, point.getContact().getSeparatingNormal()); + + float distance = point.getContact().getDistance(); + if (distance < 0 && bool(clamp_distance)) + distance = 0; + + contact_point.separating_distance = distance; + contact_point.shape_key_a = point.m_shapeKeyA; + contact_point.shape_key_b = point.m_shapeKeyB; + contact_point.separating_normal *= -1; + + return System::instance()->getContactMgr()->registerContactPoint(mContactPointInfo, + contact_point, true); +} + +FilteredClosestCdPointCollector::FilteredClosestCdPointCollector( + RigidBody* body, QueryContactPointInfo* contact_point_info) + : mBody(body), mContactPointInfo(contact_point_info) {} + +// NON_MATCHING: the original code has a useless `if (this == nullptr)` check for some reason +FilteredClosestCdPointCollector::~FilteredClosestCdPointCollector() = default; + +void FilteredClosestCdPointCollector::addCdPoint(const hkpCdPoint& point) { + auto* body = mBody; + const auto point_distance = point.getContact().getDistanceSimdReal(); + + if (hasHit() && !point_distance.isLess(getHitContact().getDistanceSimdReal())) + return; + + auto* hit_entity = getHkpEntity(*point.m_cdBodyB.getRootCollidable()); + if (!hit_entity) + return; + + auto* hit_body = getRigidBody(hit_entity); + + if (System::instance()->getEntityContactListenerField91() && hit_body->isEntity() && + EntityContactListener::isObjectOrGroundOrNPCOrTree(*hit_body)) { + return; + } + + RigidBodyCollisionMasks masks_a, masks_b; + sead::Vector3f contact_position; + storeToVec3(&contact_position, point.getContact().getPosition()); + auto shape_key_a = point.m_cdBodyA.getShapeKey(); + auto shape_key_b = point.m_cdBodyB.getShapeKey(); + + body->getCollisionMasks(&masks_a, &shape_key_a, contact_position); + hit_body->getCollisionMasks(&masks_b, &shape_key_b, contact_position); + + auto* filter = System::instance()->getGroupFilter(hit_body->getLayerType()); + auto layer_b = filter->getCollisionFilterInfoLayer(masks_b.collision_filter_info); + + if (!mContactPointInfo->isLayerSubscribed(layer_b)) + return; + + m_hitPoint.setContact(point.getContact()); + m_hitPoint.m_rootCollidableA = point.m_cdBodyA.getRootCollidable(); + m_hitPoint.m_shapeKeyA = point.m_cdBodyA.getShapeKey(); + m_hitPoint.m_rootCollidableB = point.m_cdBodyB.getRootCollidable(); + m_hitPoint.m_shapeKeyB = point.m_cdBodyB.getShapeKey(); + // Set m_earlyOutDistance to the current distance as an optimisation. + m_earlyOutDistance = point_distance; + m_unweldedNormal = point.getUnweldedNormal(); +} + +void FilteredClosestCdPointCollector::reset() { + hkpClosestCdPointCollector::reset(); + m_earlyOutDistance = 1.0f; +} + +} // namespace ksys::phys |
