mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-04-23 09:04:21 +00:00
484 lines
16 KiB
C++
484 lines
16 KiB
C++
#include "Library/Collision/Collider.h"
|
|
|
|
#include "Library/Collision/Angle.h"
|
|
#include "Library/Collision/CollisionDirector.h"
|
|
#include "Library/Collision/CollisionPartsKeeperUtil.h"
|
|
#include "Library/Collision/KCollisionServer.h"
|
|
#include "Library/Math/MathUtil.h"
|
|
|
|
namespace al {
|
|
|
|
Collider::Collider(CollisionDirector* director, const sead::Matrix34f* actorBaseMtx,
|
|
const sead::Vector3f* actorTrans, const sead::Vector3f* actorGravity, f32 radius,
|
|
f32 offsetY, u32 planeNum)
|
|
: mCollisionDirector(director), mActorBaseMtx(actorBaseMtx), mActorTrans(actorTrans),
|
|
mActorGravity(actorGravity), mRadius(radius), mOffsetY(offsetY), mPlaneNum(planeNum) {
|
|
mCurrentTrans.set(*actorTrans);
|
|
mCurrentRadius = radius;
|
|
if (mPlaneNum != 0) {
|
|
mPlanes = new SphereHitInfo[mPlaneNum];
|
|
__asm("");
|
|
} else
|
|
mPlanes = nullptr;
|
|
clear();
|
|
flags2 = (flags2 & 0x80) | 3;
|
|
}
|
|
|
|
void Collider::clear() {
|
|
clearStoredPlaneNum();
|
|
clearContactPlane();
|
|
}
|
|
|
|
void Collider::setTriangleFilter(const TriangleFilterBase* filter) {
|
|
mTriangleFilter = filter;
|
|
}
|
|
|
|
void Collider::setCollisionPartsFilter(const CollisionPartsFilterBase* filter) {
|
|
mCollisionPartsFilter = filter;
|
|
}
|
|
|
|
void Collider::updateRecentOnGroundInfo() {
|
|
if (mFloor_70 >= 0.0f) {
|
|
mNoGroundCounter = 0;
|
|
mRecentOnGroundNormal = mFloorHit.triangle.getFaceNormal();
|
|
} else if (mNoGroundCounter != -1) {
|
|
mNoGroundCounter++;
|
|
}
|
|
}
|
|
|
|
void Collider::clearStoredPlaneNum() {
|
|
mStoredPlaneNum = 0;
|
|
}
|
|
|
|
void Collider::clearContactPlane() {
|
|
mFloor_70 = -99999.0f;
|
|
mWall_70 = -99999.0f;
|
|
mCeiling_70 = -99999.0f;
|
|
mFixReaction = {0.0f, 0.0f, 0.0f};
|
|
mMovePower = {0.0f, 0.0f, 0.0f};
|
|
}
|
|
|
|
void Collider::onInvalidate() {
|
|
clear();
|
|
mNoGroundCounter = -1;
|
|
calcCheckPos(&mCurrentTrans);
|
|
mCurrentRadius = mRadius;
|
|
}
|
|
|
|
void Collider::calcCheckPos(sead::Vector3f* outPos) const {
|
|
// many things are done manually here, which is visible in the assembly as unoptimized
|
|
// reading/writing to memory - for example, the matrix multiplication here ends up writing to
|
|
// outPos 4 times
|
|
|
|
*outPos = *mActorTrans;
|
|
|
|
if (unknown) {
|
|
if ((flags2 & 0x4) && mActorBaseMtx) {
|
|
outPos->x += mActorBaseMtx->getBase(0).x * unknown->x;
|
|
outPos->x += mActorBaseMtx->getBase(1).x * unknown->y;
|
|
outPos->x += mActorBaseMtx->getBase(2).x * unknown->z;
|
|
outPos->y += mActorBaseMtx->getBase(0).y * unknown->x;
|
|
outPos->y += mActorBaseMtx->getBase(1).y * unknown->y;
|
|
outPos->y += mActorBaseMtx->getBase(2).y * unknown->z;
|
|
outPos->z += mActorBaseMtx->getBase(0).z * unknown->x;
|
|
outPos->z += mActorBaseMtx->getBase(1).z * unknown->y;
|
|
outPos->z += mActorBaseMtx->getBase(2).z * unknown->z;
|
|
return;
|
|
}
|
|
outPos->x += unknown->x;
|
|
outPos->y += unknown->y;
|
|
outPos->z += unknown->z;
|
|
return;
|
|
}
|
|
|
|
if (mActorBaseMtx) {
|
|
outPos->x += mActorBaseMtx->getBase(1).x * mOffsetY;
|
|
outPos->y += mActorBaseMtx->getBase(1).y * mOffsetY;
|
|
outPos->z += mActorBaseMtx->getBase(1).z * mOffsetY;
|
|
return;
|
|
}
|
|
|
|
outPos->y += mOffsetY;
|
|
}
|
|
|
|
static const sead::Vector3f sDefaultGroundNormal = {0.0f, 1.0f, 0.0f};
|
|
|
|
const sead::Vector3f& Collider::getRecentOnGroundNormal(u32 coyoteTime) const {
|
|
if (mFloor_70 >= 0.0f)
|
|
return mFloorHit.triangle.getFaceNormal();
|
|
|
|
if ((u32)mNoGroundCounter <= coyoteTime)
|
|
return mRecentOnGroundNormal;
|
|
|
|
return sDefaultGroundNormal;
|
|
}
|
|
|
|
const Triangle& Collider::getPlane(s32 index) const {
|
|
return mPlanes[index].triangle;
|
|
}
|
|
|
|
bool Collider::calcMovePowerByContact(sead::Vector3f* movePower, const sead::Vector3f& pos) {
|
|
if (mFloor_70 < 0.0f)
|
|
return false;
|
|
if (!mFloorHit.triangle.isHostMoved())
|
|
return false;
|
|
|
|
mFloorHit.triangle.calcForceMovePower(movePower, pos);
|
|
const sead::Vector3f& normal = mFloorHit.triangle.getFaceNormal();
|
|
if (normal.dot(*movePower) > 0.0f)
|
|
verticalizeVec(movePower, normal, *movePower);
|
|
return true;
|
|
}
|
|
|
|
u32 Collider::storeCurrentHitInfo(SphereHitInfo* buffer, u32 bufferSize) {
|
|
u32 strikeInfoNum = alCollisionUtil::getStrikeSphereInfoNum(this);
|
|
u32 i;
|
|
for (i = 0; i < strikeInfoNum; i++) {
|
|
if (i + mStoredPlaneNum >= bufferSize) {
|
|
u32 stored = bufferSize - mStoredPlaneNum;
|
|
mStoredPlaneNum = bufferSize;
|
|
return stored;
|
|
}
|
|
|
|
const al::SphereHitInfo& info = alCollisionUtil::getStrikeSphereInfo(this, i);
|
|
buffer[i + mStoredPlaneNum] = info;
|
|
}
|
|
|
|
mStoredPlaneNum += i;
|
|
return i;
|
|
}
|
|
|
|
// TODO: cleanup
|
|
void al::Collider::obtainMomentFixReaction(al::SphereHitInfo* a2, sead::Vector3f* a3,
|
|
sead::Vector3f* a4, bool a5, u32 a6) {
|
|
flags2 &= ~(0x10 | 0x20 | 0x40);
|
|
for (u32 i = a6; i < mStoredPlaneNum; i++) {
|
|
const sead::Vector3f& normal = a2[i].triangle.getFaceNormal();
|
|
if (al::isFloorPolygon(normal, *mActorGravity)) {
|
|
if (a2[i].isCollisionAtFace())
|
|
flags2 |= 0x10;
|
|
} else if (al::isWallPolygon(normal, *mActorGravity)) {
|
|
if (a2[i].isCollisionAtFace())
|
|
flags2 |= 0x20;
|
|
} else {
|
|
if (a2[i].isCollisionAtFace())
|
|
flags2 |= 0x40;
|
|
}
|
|
}
|
|
|
|
f32 maxAX = 0.0f;
|
|
f32 maxAY = 0.0f;
|
|
f32 maxAZ = 0.0f;
|
|
f32 minAX = 0.0f;
|
|
f32 minAY = 0.0f;
|
|
f32 minAZ = 0.0f;
|
|
f32 maxBX = 0.0f;
|
|
f32 maxBY = 0.0f;
|
|
f32 maxBZ = 0.0f;
|
|
f32 minBX = 0.0f;
|
|
f32 minBY = 0.0f;
|
|
f32 minBZ = 0.0f;
|
|
|
|
for (u32 i = a6; i < mStoredPlaneNum; i++) {
|
|
const sead::Vector3f& normal = a2[i].triangle.getFaceNormal();
|
|
sead::Vector3f a;
|
|
sead::Vector3f b;
|
|
if (al::isFloorPolygon(normal, *mActorGravity))
|
|
if (!flags1 || (flags2 & 0x10) != 0)
|
|
a2[i].calcFixVectorNormal(&a, &b);
|
|
else
|
|
a2[i].calcFixVector(&a, &b);
|
|
else if (al::isWallPolygon(normal, *mActorGravity))
|
|
if ((flags2 & 0x20) == 0)
|
|
a2[i].calcFixVector(&a, &b);
|
|
else
|
|
a2[i].calcFixVectorNormal(&a, &b);
|
|
else if ((flags2 & 0x40) == 0)
|
|
a2[i].calcFixVector(&a, &b);
|
|
else
|
|
a2[i].calcFixVectorNormal(&a, &b);
|
|
|
|
if (maxAX < a.x)
|
|
maxAX = a.x;
|
|
else if (a.x < minAX)
|
|
minAX = a.x;
|
|
|
|
if (maxAY < a.y)
|
|
maxAY = a.y;
|
|
else if (a.y < minAY)
|
|
minAY = a.y;
|
|
|
|
if (maxAZ < a.z)
|
|
maxAZ = a.z;
|
|
else if (a.z < minAZ)
|
|
minAZ = a.z;
|
|
|
|
if (a4) {
|
|
if (maxBX < b.x)
|
|
maxBX = b.x;
|
|
else if (b.x < minBX)
|
|
minBX = b.x;
|
|
|
|
if (maxBY < b.y)
|
|
maxBY = b.y;
|
|
else if (b.y < minBY)
|
|
minBY = b.y;
|
|
|
|
if (maxBZ < b.z)
|
|
maxBZ = b.z;
|
|
else if (b.z < minBZ)
|
|
minBZ = b.z;
|
|
}
|
|
|
|
if ((flags2 & 2) == 0 || !a2[i].triangle.isHostMoved())
|
|
continue;
|
|
|
|
sead::Vector3f collisionMovingReaction = a2[i].collisionMovingReaction;
|
|
if (a.dot(collisionMovingReaction) < 0.0f)
|
|
continue;
|
|
|
|
if (maxAX < collisionMovingReaction.x)
|
|
maxAX = collisionMovingReaction.x;
|
|
else if (collisionMovingReaction.x < minAX)
|
|
minAX = collisionMovingReaction.x;
|
|
|
|
if (maxAY < collisionMovingReaction.y)
|
|
maxAY = collisionMovingReaction.y;
|
|
else if (collisionMovingReaction.y < minAY)
|
|
minAY = collisionMovingReaction.y;
|
|
|
|
if (maxAZ < collisionMovingReaction.z)
|
|
maxAZ = collisionMovingReaction.z;
|
|
else if (collisionMovingReaction.z < minAZ)
|
|
minAZ = collisionMovingReaction.z;
|
|
|
|
if (a4) {
|
|
if (maxBX < collisionMovingReaction.x)
|
|
maxBX = collisionMovingReaction.x;
|
|
else if (collisionMovingReaction.x < minBX)
|
|
minBX = collisionMovingReaction.x;
|
|
|
|
if (maxBY < collisionMovingReaction.y)
|
|
maxBY = collisionMovingReaction.y;
|
|
else if (collisionMovingReaction.y < minBY)
|
|
minBY = collisionMovingReaction.y;
|
|
|
|
if (maxBZ < collisionMovingReaction.z)
|
|
maxBZ = collisionMovingReaction.z;
|
|
else if (!(collisionMovingReaction.z < minBZ)) // FIXME: wrong way around?
|
|
minBZ = collisionMovingReaction.z;
|
|
}
|
|
}
|
|
|
|
a3->x = maxAX + minAX;
|
|
a3->y = maxAY + minAY;
|
|
a3->z = maxAZ + minAZ;
|
|
if (a4) {
|
|
a4->x = maxBX + minBX;
|
|
a4->y = maxBY + minBY;
|
|
a4->z = maxBZ + minBZ;
|
|
}
|
|
}
|
|
|
|
void Collider::storeContactPlane(SphereHitInfo* hitInfos) {
|
|
for (u32 i = 0; i < mStoredPlaneNum; i++) {
|
|
const sead::Vector3f& normal = hitInfos[i].triangle.getNormal(0);
|
|
if (isFloorPolygon(normal, *mActorGravity)) {
|
|
if (mFloor_70 < hitInfos[i]._70) {
|
|
mFloorHit = hitInfos[i];
|
|
mFloor_70 = hitInfos[i]._70;
|
|
}
|
|
} else if (isWallPolygon(normal, *mActorGravity)) {
|
|
if (mWall_70 < hitInfos[i]._70) {
|
|
mWallHit = hitInfos[i];
|
|
mWall_70 = hitInfos[i]._70;
|
|
}
|
|
} else if (mCeiling_70 < hitInfos[i]._70) {
|
|
mCeilingHit = hitInfos[i];
|
|
mCeiling_70 = hitInfos[i]._70;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: cleanup
|
|
sead::Vector3f al::Collider::collide(const sead::Vector3f& velocity) {
|
|
al::SphereHitInfo* planes;
|
|
u32 planeNum;
|
|
if (mPlaneNum) {
|
|
planes = mPlanes;
|
|
planeNum = mPlaneNum;
|
|
} else {
|
|
getCollisionDirector()->getSphereHitInfoArrayForCollider(&planes, &planeNum);
|
|
}
|
|
|
|
sead::Vector3f checkPos = {0.0f, 0.0f, 0.0f};
|
|
calcCheckPos(&checkPos);
|
|
|
|
/*-----------------*/
|
|
|
|
sead::Vector3f transStart = mCurrentTrans;
|
|
f32 currentRadius = mCurrentRadius;
|
|
f32 v8 = sead::Mathf::clampMax(sead::Mathf::min(0.9f * mRadius, 0.9f * mCurrentRadius), 35.0f);
|
|
sead::Vector3f movePower = {0.0f, 0.0f, 0.0f};
|
|
if ((flags2 & 1) != 0)
|
|
calcMovePowerByContact(&movePower, checkPos);
|
|
|
|
clear();
|
|
mMovePower = movePower;
|
|
al::SphereInterpolator interpolator;
|
|
interpolator.startInterp(mCurrentTrans, checkPos + movePower, mCurrentRadius, mRadius, v8);
|
|
s32 v56 = 0;
|
|
sead::Vector3f moveDist = (checkPos + movePower) - mCurrentTrans;
|
|
if (!al::isNearZero(moveDist) || !al::isNearZero(mCurrentRadius - mRadius))
|
|
preCollide(&interpolator, &transStart, ¤tRadius, moveDist, planes, planeNum);
|
|
|
|
f32 v19 = sead::Mathf::clampMax(mCurrentRadius * 0.9f, 35.0f);
|
|
interpolator.startInterp(transStart, transStart + velocity, mRadius, mRadius, v19);
|
|
v56 = 0;
|
|
sead::Vector3f v232526;
|
|
if (!findCollidePos(&v56, &interpolator, planes, planeNum) &&
|
|
interpolator.getPrevStep() == 1.0 && interpolator.getCurrentStep() == 1.0) {
|
|
v232526 = transStart - checkPos + velocity;
|
|
} else {
|
|
sead::Vector3f v272829 = {0.0f, 0.0f, 0.0f};
|
|
s32 v30 = 0;
|
|
bool v31 = true;
|
|
sead::Vector3f v53;
|
|
do {
|
|
sead::Vector3f v55 = {0.0f, 0.0f, 0.0f};
|
|
sead::Vector3f v54 = {0.0f, 0.0f, 0.0f};
|
|
obtainMomentFixReaction(planes, &v55, &v54, v31, v30);
|
|
v30 += v56;
|
|
|
|
interpolator.calcInterp(&transStart, ¤tRadius, &v53);
|
|
transStart += v55;
|
|
v272829 += v55;
|
|
sead::Vector3f a1a = v55;
|
|
if (al::isNearZero(a1a))
|
|
a1a = v54;
|
|
|
|
al::tryNormalizeOrZero(&a1a);
|
|
f32 v42 = v53.dot(a1a);
|
|
if (v42 < 0.0f)
|
|
v53 -= a1a * v42;
|
|
|
|
if (velocity.dot(v53) < 0.0f)
|
|
break;
|
|
|
|
interpolator.startInterp(transStart, transStart + v53, currentRadius, mRadius, v19);
|
|
interpolator.nextStep();
|
|
v56 = 0;
|
|
v31 = false;
|
|
bool CollidePos = findCollidePos(&v56, &interpolator, planes, planeNum);
|
|
if (!CollidePos || interpolator.getCurrentStep() >= 1.0f) {
|
|
interpolator.calcInterpPos(&transStart);
|
|
if (CollidePos && v56 > 0) {
|
|
obtainMomentFixReaction(planes, &v55, nullptr, false, v30);
|
|
transStart += v55;
|
|
v30 += v56;
|
|
}
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} while (true);
|
|
|
|
storeContactPlane(planes);
|
|
mFixReaction = v272829;
|
|
v232526 = transStart - checkPos;
|
|
}
|
|
|
|
mCurrentTrans = v232526 + checkPos;
|
|
mCurrentRadius = mRadius;
|
|
updateRecentOnGroundInfo();
|
|
|
|
return v232526;
|
|
}
|
|
|
|
// TODO: cleanup
|
|
bool Collider::preCollide(al::SphereInterpolator* interpolator, sead::Vector3f* trans,
|
|
f32* currentRadius, const sead::Vector3f& moveDist,
|
|
al::SphereHitInfo* buffer, u32 bufferSize) {
|
|
sead::Vector3f v71415 = {0.0f, 0.0f, 0.0f};
|
|
const al::TriangleFilterBase* triangleFilter = mTriangleFilter;
|
|
const al::CollisionPartsFilterBase* collisionPartsFilter = mCollisionPartsFilter;
|
|
bool foundHit = false;
|
|
u32 totalStored = 0;
|
|
while (interpolator->getPrevStep() != 1.0f || interpolator->getCurrentStep() != 1.0f) {
|
|
sead::Vector3f pos;
|
|
f32 size;
|
|
interpolator->calcInterp(&pos, &size, nullptr);
|
|
|
|
s32 hits;
|
|
if ((flags2 & 2) != 0) {
|
|
hits = alCollisionUtil::checkStrikeSphereMovingReaction(
|
|
this, v71415 + pos, size, moveDist, collisionPartsFilter, triangleFilter);
|
|
} else {
|
|
hits = alCollisionUtil::checkStrikeSphere(this, pos, size, collisionPartsFilter,
|
|
triangleFilter);
|
|
}
|
|
|
|
if (hits != 0) {
|
|
s32 stored = storeCurrentHitInfo(buffer, bufferSize);
|
|
sead::Vector3f a2a = {0.0f, 0.0f, 0.0f};
|
|
obtainMomentFixReaction(buffer, &a2a, nullptr, false, totalStored);
|
|
v71415 += a2a;
|
|
foundHit = true;
|
|
if (interpolator->getCurrentStep() >= 1.0f)
|
|
break;
|
|
|
|
if (flags2 & 0x8)
|
|
totalStored += stored;
|
|
}
|
|
|
|
interpolator->nextStep();
|
|
}
|
|
|
|
interpolator->calcInterp(trans, currentRadius, nullptr);
|
|
*trans += v71415;
|
|
if (foundHit) {
|
|
storeContactPlane(buffer);
|
|
mStoredPlaneNum = 0;
|
|
}
|
|
|
|
return foundHit;
|
|
}
|
|
|
|
bool Collider::findCollidePos(s32* bufferStored, al::SphereInterpolator* interpolator,
|
|
al::SphereHitInfo* buffer, u32 bufferSize) {
|
|
const al::TriangleFilterBase* triangleFilter = mTriangleFilter;
|
|
const al::CollisionPartsFilterBase* collisionPartsFilter = mCollisionPartsFilter;
|
|
while (interpolator->getPrevStep() != 1.0 || interpolator->getCurrentStep() != 1.0) {
|
|
sead::Vector3f pos, remainMoveVec;
|
|
f32 size;
|
|
interpolator->calcInterp(&pos, &size, &remainMoveVec);
|
|
|
|
s32 hits;
|
|
if (flags2 & 0x2) {
|
|
hits = alCollisionUtil::checkStrikeSphereMovingReaction(
|
|
this, pos, size, remainMoveVec, collisionPartsFilter, triangleFilter);
|
|
} else {
|
|
hits = alCollisionUtil::checkStrikeSphere(this, pos, size, collisionPartsFilter,
|
|
triangleFilter);
|
|
}
|
|
|
|
if (hits != 0) {
|
|
u32 stored = storeCurrentHitInfo(buffer, bufferSize);
|
|
if (bufferStored)
|
|
*bufferStored = stored;
|
|
|
|
return true;
|
|
}
|
|
|
|
interpolator->nextStep();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CollisionDirector* Collider::getCollisionDirector() const {
|
|
return mCollisionDirector;
|
|
}
|
|
|
|
} // namespace al
|