summaryrefslogtreecommitdiff
path: root/src/KingSystem/Physics/System/physContactMgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/KingSystem/Physics/System/physContactMgr.cpp')
-rw-r--r--src/KingSystem/Physics/System/physContactMgr.cpp216
1 files changed, 213 insertions, 3 deletions
diff --git a/src/KingSystem/Physics/System/physContactMgr.cpp b/src/KingSystem/Physics/System/physContactMgr.cpp
index c7ea0aab..4a3c8de1 100644
--- a/src/KingSystem/Physics/System/physContactMgr.cpp
+++ b/src/KingSystem/Physics/System/physContactMgr.cpp
@@ -1,5 +1,8 @@
#include "KingSystem/Physics/System/physContactMgr.h"
+#include <memory>
#include <prim/seadScopedLock.h>
+#include "KingSystem/Physics/RigidBody/physRigidBody.h"
+#include "KingSystem/Physics/RigidBody/physRigidBodyRequestMgr.h"
#include "KingSystem/Physics/System/physCollisionInfo.h"
#include "KingSystem/Physics/System/physContactLayerCollisionInfo.h"
#include "KingSystem/Physics/System/physContactPointInfo.h"
@@ -9,17 +12,48 @@
#include "KingSystem/Physics/System/physQueryContactPointInfo.h"
#include "KingSystem/Physics/System/physSystem.h"
#include "KingSystem/Utils/Debug.h"
+#include "KingSystem/Utils/MathUtil.h"
namespace ksys::phys {
+struct ContactMgr::ImpulseEntry {
+ static constexpr size_t getListNodeOffset() { return offsetof(ImpulseEntry, list_node); }
+
+ RigidBody* bodies[2];
+ sead::Vector3f linear_vels[2];
+ bool is_dynamic_motion[2];
+ float magnitude;
+ float impulses[2];
+ sead::Vector3f contact_point_pos;
+ bool is_entity_motion_flag_20_off;
+ bool valid;
+ sead::ListNode list_node;
+};
+
ContactMgr::ContactMgr() {
mContactPointInfoInstances.initOffset(ContactPointInfo::getListNodeOffset());
- // FIXME: figure out what these offsets are
mCollisionInfoInstances.initOffset(CollisionInfo::getListNodeOffset());
+ // FIXME: figure out what this offset is
mList3.initOffset(0x40);
mCollidingBodiesFreeList.initOffset(CollidingBodies::getListNodeOffset());
- mList4.initOffset(0x48);
- mList5.initOffset(0x48);
+ mImpulseEntriesFreeList.initOffset(ImpulseEntry::getListNodeOffset());
+ mImpulseEntries.initOffset(ImpulseEntry::getListNodeOffset());
+}
+
+ContactMgr::~ContactMgr() {
+ for (int i = 0; i < mCollidingBodiesCapacity; ++i)
+ delete mCollidingBodiesFreeList.popFront();
+}
+
+void ContactMgr::init(sead::Heap* heap) {
+ mCollidingBodiesCapacity = 0x2000;
+ for (int i = 0; i < mCollidingBodiesCapacity; ++i) {
+ mCollidingBodiesFreeList.pushBack(new (heap) CollidingBodies());
+ }
+
+ for (int i = 0; i < 0x100; ++i) {
+ mImpulseEntriesFreeList.pushBack(new (heap) ImpulseEntry());
+ }
}
void ContactMgr::initContactPointPool(sead::Heap* heap, IsIndoorStage indoor) {
@@ -189,6 +223,17 @@ void ContactMgr::removeCollisionEntriesWithBody(RigidBody* body) {
}
}
+void ContactMgr::removeImpulseEntriesWithBody(RigidBody* body) {
+ auto lock = sead::makeScopedLock(mImpulseEntriesMutex);
+
+ for (auto& entry : mImpulseEntries.robustRange()) {
+ if (entry.bodies[0] == body || entry.bodies[1] == body) {
+ mImpulseEntries.erase(&entry);
+ mImpulseEntriesFreeList.pushBack(&entry);
+ }
+ }
+}
+
int ContactMgr::allocateContactPoint() {
if (mNumContactPoints >= mContactPointPool.size() - 1) {
util::PrintDebugFmt("contact point pool exhausted (current number of points: %d)",
@@ -389,6 +434,171 @@ void ContactMgr::clearCollisionEntries(CollisionInfo* info) {
}
}
+void ContactMgr::addImpulseEntry(RigidBody* body_a, RigidBody* body_b) {
+ auto lock = sead::makeScopedLock(mImpulseEntriesMutex);
+
+ auto* entry = mImpulseEntriesFreeList.popFront();
+ if (!entry)
+ return;
+
+ entry->bodies[0] = body_a;
+ entry->bodies[1] = body_b;
+ body_a->getLinearVelocity(&entry->linear_vels[0]);
+ body_b->getLinearVelocity(&entry->linear_vels[1]);
+ entry->is_dynamic_motion[0] = body_a->getMotionType() == MotionType::Dynamic;
+ entry->is_dynamic_motion[1] = body_b->getMotionType() == MotionType::Dynamic;
+ entry->magnitude = 0;
+ entry->impulses[0] = 0;
+ entry->impulses[1] = 0;
+ entry->is_entity_motion_flag_20_off = true;
+ entry->valid = false;
+ mImpulseEntries.pushBack(entry);
+}
+
+static sead::Vector3f computeLinearVelocity(const RigidBody& body, const sead::Vector3f& linear_vel,
+ const sead::Vector3f& pos) {
+ const auto center = body.getCenterOfMassInWorld();
+ const auto rel_pos = pos - center;
+ sead::Vector3f correction;
+ correction.setCross(body.getAngularVelocity(), rel_pos);
+ return linear_vel + correction;
+}
+
+void ContactMgr::setImpulseEntryContactInfo(RigidBody* body_a, RigidBody* body_b,
+ const sead::Vector3f& contact_point_pos,
+ const sead::Vector3f& contact_point_normal,
+ u32 material_a, u32 material_b) {
+ auto lock = sead::makeScopedLock(mImpulseEntriesMutex);
+
+ ImpulseEntry* entry = nullptr;
+ for (auto& impulse_entry : mImpulseEntries) {
+ if ((impulse_entry.bodies[0] == body_a && impulse_entry.bodies[1] == body_b) ||
+ (impulse_entry.bodies[1] == body_a && impulse_entry.bodies[0] == body_b)) {
+ entry = std::addressof(impulse_entry);
+ break;
+ }
+ }
+
+ if (!entry)
+ return;
+
+ const MaterialMask mat_mask_a{material_a};
+ const MaterialMask mat_mask_b{material_b};
+
+ if (mat_mask_a.getData().flag30 || mat_mask_b.getData().flag30)
+ return;
+
+ if (mat_mask_a.getFloorCode() == FloorCode::NoImpulseUpperMove && entry->linear_vels[0].y > 0)
+ return;
+
+ if (mat_mask_b.getFloorCode() == FloorCode::NoImpulseUpperMove && entry->linear_vels[1].y > 0)
+ return;
+
+ /// The velocities of the rigid bodies at the contact point.
+ const auto linvel_a =
+ computeLinearVelocity(*entry->bodies[0], entry->linear_vels[0], contact_point_pos);
+ const auto linvel_b =
+ computeLinearVelocity(*entry->bodies[1], entry->linear_vels[1], contact_point_pos);
+
+ const bool is_flag_off =
+ entry->bodies[0]->isEntityMotionFlag20Off() && entry->bodies[1]->isEntityMotionFlag20Off();
+
+ /// The pre-collision relative velocity.
+ const auto relative_vel = linvel_a - linvel_b;
+ const auto dot_neg = [&](const auto& vec) { return vec.dot(-contact_point_normal); };
+
+ float magnitude = is_flag_off ? sead::Mathf::max(0.0, relative_vel.dot(-contact_point_normal)) :
+ sead::Mathf::max(0.0, relative_vel.length());
+
+ if (magnitude >= entry->magnitude) {
+ float i1, i2;
+ if (is_flag_off) {
+ i1 = sead::Mathf::min(sead::Mathf::max(0.0, dot_neg(linvel_a)), magnitude);
+ i2 = sead::Mathf::min(sead::Mathf::max(0.0, linvel_b.dot(contact_point_normal)),
+ sead::Mathf::max(0.0, dot_neg(relative_vel)));
+ } else {
+ i1 = sead::Mathf::min(sead::Mathf::max(0.0, linvel_a.length()), magnitude);
+ i2 = sead::Mathf::min(sead::Mathf::max(0.0, linvel_b.length()),
+ sead::Mathf::max(0.0, relative_vel.length()));
+ }
+
+ entry->magnitude = magnitude;
+ entry->impulses[0] = i1;
+ entry->impulses[1] = i2;
+ entry->contact_point_pos = contact_point_pos;
+ entry->is_entity_motion_flag_20_off = is_flag_off;
+ entry->valid = true;
+ }
+}
+
+void ContactMgr::processImpulseEntry(const ImpulseEntry& entry) {
+ if (!entry.valid)
+ return;
+
+ RigidBody* body_a = entry.bodies[0];
+ RigidBody* body_b = entry.bodies[1];
+
+ if (body_a->isEntityMotionFlag40On())
+ body_a->setEntityMotionFlag40(false);
+
+ if (body_b->isEntityMotionFlag40On())
+ body_b->setEntityMotionFlag40(false);
+
+ float impulse_a = 0.0f;
+ float impulse_b = 0.0f;
+
+ const auto compute_impulse = [&entry](int index, RigidBody* body) {
+ if (!entry.is_dynamic_motion[index])
+ return 0.0f;
+
+ const auto impulse = entry.impulses[index];
+ if (!(impulse > 5.0f || !entry.is_entity_motion_flag_20_off))
+ return 0.0f;
+
+ const float scaled_impulse = impulse * body->getMass() * body->getColImpulseScale();
+
+ float volume_factor = 1.0f;
+ if (body->getMotionType() == MotionType::Dynamic)
+ volume_factor += util::clampAndRemapRange(body->getVolume(), 0.2, 1.0, 1.0, 0.0);
+ return scaled_impulse * volume_factor;
+ };
+
+ impulse_b = compute_impulse(0, body_a);
+ impulse_a = compute_impulse(1, body_b);
+
+ const auto add_impulse = [impulse_a, impulse_b](RigidBody* body1, RigidBody* body2,
+ float impulse) {
+ if (body1->isEntityMotionFlag10Off())
+ impulse = impulse_a + impulse_b;
+
+ if (body2->isEntityMotionFlag8On() ||
+ (body1->getMaxImpulse() >= 0 && impulse > body1->getMaxImpulse())) {
+ System::instance()->getRigidBodyRequestMgr()->addImpulse(body1, body2, impulse);
+ }
+ };
+ add_impulse(body_a, body_b, impulse_a);
+ add_impulse(body_b, body_a, impulse_b);
+}
+
+void ContactMgr::processImpulseEntries() {
+ auto lock = sead::makeScopedLock(mImpulseEntriesMutex);
+
+ for (auto& entry : mImpulseEntries.robustRange()) {
+ processImpulseEntry(entry);
+ mImpulseEntries.erase(&entry);
+ mImpulseEntriesFreeList.pushBack(&entry);
+ }
+}
+
+void ContactMgr::clearImpulseEntries() {
+ auto lock = sead::makeScopedLock(mImpulseEntriesMutex);
+
+ for (auto& entry : mImpulseEntries.robustRange()) {
+ mImpulseEntries.erase(&entry);
+ mImpulseEntriesFreeList.pushBack(&entry);
+ }
+}
+
void ContactInfoTable::Receiver::postRead_() {
layer_mask = 0;
layer_mask2 = 0;