Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up KCL collision functions #74

Merged
merged 1 commit into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/game/field/CourseColMgr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ bool CourseColMgr::checkSphereFullPush(f32 scalar, f32 radius, KColData *data,
data->lookupSphere(radius, scaled_position, vStack88, flags);

if (info) {
return doCheckWithFullInfoPush(data, &KColData::checkSphere, info, kcl_flags_out);
return doCheckWithFullInfoPush(data, &KColData::checkSphereCollision, info, kcl_flags_out);
}
return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, kcl_flags_out);
}
Expand Down
195 changes: 43 additions & 152 deletions source/game/field/KColData.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ void KColData::narrowPolygon_EachBlock(const u16 *prismArray) {
m_prismIter = prismArray;

while (checkSphereSingle(nullptr, nullptr, nullptr)) {
*(m_prismCacheTop++) = parse<u16>(*m_prismIter);
/// We assume the cache has same endianness as the archive file,
/// so do not parse out the prism index and directly store it in the cache.
*(m_prismCacheTop++) = *m_prismIter;

if (m_prismCacheTop == m_prismCache.end()) {
--m_prismCacheTop;
Expand Down Expand Up @@ -121,7 +123,7 @@ bool KColData::checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut)
// Check collision for all triangles, and continuously call the function until we're out
while (*++m_prismIter != 0) {
const KCollisionPrism prism = getPrism(parse<u16>(*m_prismIter));
if (checkSphereTriCollision(prism, distOut, fnrmOut, flagsOut)) {
if (checkCollision(prism, distOut, fnrmOut, flagsOut, CollisionCheckType::Plane)) {
return true;
}
}
Expand Down Expand Up @@ -151,7 +153,7 @@ bool KColData::checkSphereSingle(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flag
}

const KCollisionPrism prism = getPrism(parse<u16>(*m_prismIter));
if (checkSphereTriCollision(prism, distOut, fnrmOut, flagsOut)) {
if (checkCollision(prism, distOut, fnrmOut, flagsOut, CollisionCheckType::Edge)) {
return true;
}
}
Expand All @@ -175,7 +177,7 @@ void KColData::lookupSphereCached(const EGG::Vector3f &p1, const EGG::Vector3f &
EGG::Sphere3f sphere1(p1, radius);
EGG::Sphere3f sphere2(m_cachedPos, m_cachedRadius);

if (sphere1.isInsideOtherSphere(sphere2)) {
if (!sphere1.isInsideOtherSphere(sphere2)) {
m_prismIter = searchBlock(p1);
m_radius = std::min(m_sphereRadius, radius);
} else {
Expand Down Expand Up @@ -287,9 +289,13 @@ u16 KColData::prismCache(u32 idx) const {
return m_prismCache[idx];
}

// Returns whether or not our sphere is colliding with the triangle
bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOut,
EGG::Vector3f *fnrmOut, u16 *flagsOut) {
// This is a combination of the three collision checks in the base game.
// The three checks vary only by a few if-statements, related to whether we are checking for:
// 1. A collision with at least the triangle edge (0x807C0F00)
// 2. A collision with the triangle plane (0x807C1514)
// 3. A collision such that we are inside the triangle (0x807C0884)
bool KColData::checkCollision(const KCollisionPrism &prism, f32 *distOut, EGG::Vector3f *fnrmOut,
u16 *flagsOut, CollisionCheckType type) {
// Responsible for updating the output params
auto out = [&](f32 dist) {
if (distOut) {
Expand All @@ -306,7 +312,8 @@ bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOu

// The flag check occurs earlier than in the base game here. We don't want to do math if the tri
// we're checking doesn't have matching flags.
if ((KCL_ATTRIBUTE_TYPE_BIT(prism.attribute) & m_typeMask) == 0) {
u32 attributeMask = KCL_ATTRIBUTE_TYPE_BIT(prism.attribute);
if ((attributeMask & m_typeMask) == 0) {
return false;
}

Expand Down Expand Up @@ -338,9 +345,22 @@ bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOu
return false;
}

if (type == CollisionCheckType::Movement) {
if ((attributeMask & KCL_TYPE_DIRECTIONAL) != 0 && m_movement.dot(fnrm) > 0.0f) {
return false;
}
}

// Originally part of the edge searching, but moved out for simplicity
// If these are all zero, then we're inside the triangle
if (dist_ab <= 0.0f && dist_bc <= 0.0f && dist_ca <= 0.0f) {
if (type == CollisionCheckType::Movement) {
EGG::Vector3f lastPos = relativePos - m_movement;
// We're only colliding if we are moving towards the face
if (plane_dist < 0.0f && lastPos.dot(fnrm) < 0.0f) {
return false;
}
}
return out(dist_in_plane);
}

Expand Down Expand Up @@ -391,168 +411,39 @@ bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOu
f32 cos = edge_nor.ps_dot(other_edge_nor);
f32 sq_dist;
if (cos * edge_dist > other_edge_dist) {
if (edge_dist > plane_dist) {
return false;
if (type == CollisionCheckType::Plane) {
if (edge_dist > plane_dist) {
return false;
}
}

sq_dist = m_radius * m_radius - edge_dist * edge_dist;
} else {
f32 sq_sin = cos * cos - 1.0f;
f32 t = (cos * edge_dist - other_edge_dist) / sq_sin;
f32 s = edge_dist - t * cos;
const EGG::Vector3f corner_pos = edge_nor * s + other_edge_nor * t;
f32 corner_sq_dist = corner_pos.dot();

if (corner_sq_dist > plane_dist * plane_dist) {
return false;
if (type == CollisionCheckType::Plane) {
if (corner_pos.dot() > plane_dist * plane_dist) {
return false;
}
}

sq_dist = m_radius * m_radius - corner_sq_dist;
sq_dist = m_radius * m_radius - corner_pos.dot();
}

if (sq_dist < plane_dist * plane_dist || sq_dist < 0.0f) {
if (sq_dist < plane_dist * plane_dist || sq_dist <= 0.0f) {
return false;
}

f32 dist = EGG::Mathf::sqrt(sq_dist) - plane_dist;
if (dist <= 0.0f) {
return false;
}

return out(dist);
}

bool KColData::checkSphereMovementCollision(const KCollisionPrism &prism, f32 *distOut,
EGG::Vector3f *fnrmOut, u16 *flagsOut) {
// Responsible for updating the output params
auto out = [&](f32 dist) {
if (distOut) {
*distOut = dist;
}
if (fnrmOut) {
*fnrmOut = getNrm(prism.fnrm_i);
}
if (flagsOut) {
*flagsOut = prism.attribute;
}
return true;
};

// The flag check occurs earlier than in the base game here. We don't want to do math if the tri
// we're checking doesn't have matching flags.
if ((KCL_ATTRIBUTE_TYPE_BIT(prism.attribute) & m_typeMask) == 0) {
return false;
}

const EGG::Vector3f relativePos = m_pos - getPos(prism.pos_i);

// Edge normals point outside the triangle
const EGG::Vector3f enrm1 = getNrm(prism.enrm1_i);
f32 dist_ca = relativePos.ps_dot(enrm1);
if (m_radius <= dist_ca) {
return false;
}

const EGG::Vector3f enrm2 = getNrm(prism.enrm2_i);
f32 dist_ab = relativePos.ps_dot(enrm2);
if (m_radius <= dist_ab) {
return false;
}

const EGG::Vector3f enrm3 = getNrm(prism.enrm3_i);
f32 dist_bc = relativePos.ps_dot(enrm3) - prism.height;
if (m_radius <= dist_bc) {
return false;
}

const EGG::Vector3f fnrm = getNrm(prism.fnrm_i);
f32 plane_dist = relativePos.ps_dot(fnrm);
f32 dist_in_plane = m_radius - plane_dist;
if (dist_in_plane <= 0.0f || dist_in_plane >= m_prismThickness) {
return false;
}

// Originally part of the edge searching, but moved out for simplicity
// If these are all zero, then we're inside the triangle
if (dist_ab <= 0.0f && dist_bc <= 0.0f && dist_ca <= 0.0f) {
EGG::Vector3f lastPos = relativePos - (m_pos - m_prevPos);
if (plane_dist < 0.0f && lastPos.dot(fnrm) < 0.0f) {
if (type == CollisionCheckType::Movement) {
EGG::Vector3f lastPos = relativePos - m_movement;
// We're only colliding if we are moving towards the face
if (lastPos.dot(fnrm) < 0.0f) {
return false;
}

return out(dist_in_plane);
}

EGG::Vector3f edge_nor, other_edge_nor;
f32 edge_dist, other_edge_dist;
// > means further, < means closer, = means same distance
if (dist_ab >= dist_ca && dist_ab > dist_bc) {
// AB is the furthest edge
edge_nor = enrm2;
edge_dist = dist_ab;
if (dist_ca >= dist_bc) {
// CA is the second furthest edge
other_edge_nor = enrm1;
other_edge_dist = dist_ca;
} else {
// BC is the second furthest edge
other_edge_nor = enrm3;
other_edge_dist = dist_bc;
}
} else if (dist_bc >= dist_ca) {
// BC is the furthest edge
edge_nor = enrm3;
edge_dist = dist_bc;
if (dist_ab >= dist_ca) {
// AB is the second furthest edge
other_edge_nor = enrm2;
other_edge_dist = dist_ab;
} else {
// CA is the second furthest edge
other_edge_nor = enrm1;
other_edge_dist = dist_ca;
}
} else {
// CA is the furthest edge
edge_nor = enrm1;
edge_dist = dist_ca;
if (dist_bc >= dist_ab) {
// BC is the second furthest edge
other_edge_nor = enrm3;
other_edge_dist = dist_bc;
} else {
// AB is the second furthest edge
other_edge_nor = enrm2;
other_edge_dist = dist_ab;
}
}

f32 cos = edge_nor.ps_dot(other_edge_nor);
f32 sq_dist;
if (cos * edge_dist > other_edge_dist) {
sq_dist = m_radius * m_radius - edge_dist * edge_dist;
} else {
f32 sq_sin = cos * cos - 1.0f;
f32 t = (cos * edge_dist - other_edge_dist) / sq_sin;
f32 s = edge_dist - t * cos;
const EGG::Vector3f corner_pos = edge_nor * s + other_edge_nor * t;
f32 corner_sq_dist = corner_pos.dot();
sq_dist = m_radius * m_radius - corner_sq_dist;
}

if (sq_dist < plane_dist * plane_dist || sq_dist < 0.0f) {
return false;
}

f32 dist = EGG::Mathf::sqrt(sq_dist) - plane_dist;
if (dist <= 0.0f) {
return false;
}

EGG::Vector3f lastPos = relativePos - (m_pos - m_prevPos);

if (lastPos.dot(fnrm) < 0.0f) {
return false;
}

return out(dist);
Expand All @@ -567,7 +458,7 @@ bool KColData::checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *at
// Check collision for all triangles, and continuously call the function until we're out
while (*++m_prismIter != 0) {
const KCollisionPrism prism = getPrism(parse<u16>(*m_prismIter));
if (checkSphereMovementCollision(prism, distOut, fnrmOut, attributeOut)) {
if (checkCollision(prism, distOut, fnrmOut, attributeOut, CollisionCheckType::Movement)) {
return true;
}
}
Expand Down
12 changes: 8 additions & 4 deletions source/game/field/KColData.hh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ namespace Field {

class KColData {
public:
enum class CollisionCheckType {
Edge,
Plane,
Movement,
};

struct KCollisionPrism {
KCollisionPrism();
KCollisionPrism(f32 height, u16 posIndex, u16 faceNormIndex, u16 edge1NormIndex,
Expand Down Expand Up @@ -54,10 +60,8 @@ public:
u16 prismCache(u32 idx) const;

private:
bool checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOut, EGG::Vector3f *fnrmOut,
u16 *flagsOut);
bool checkSphereMovementCollision(const KCollisionPrism &prism, f32 *distOut,
EGG::Vector3f *fnrmOut, u16 *flagsOut);
bool checkCollision(const KCollisionPrism &prism, f32 *distOut, EGG::Vector3f *fnrmOut,
u16 *flagsOut, CollisionCheckType type);
bool checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut);

const void *m_posData;
Expand Down
Loading