diff --git a/source/abstract/Archive.cc b/source/abstract/Archive.cc index bded503c..fc6aa443 100644 --- a/source/abstract/Archive.cc +++ b/source/abstract/Archive.cc @@ -8,11 +8,11 @@ ArchiveHandle::ArchiveHandle(void *archiveStart) : m_startAddress(archiveStart) RawArchive *rawArchive = reinterpret_cast(archiveStart); assert(rawArchive->isValidSignature()); - m_nodesAddress = static_cast(archiveStart) + parse(rawArchive->m_nodesOffset); - m_filesAddress = static_cast(archiveStart) + parse(rawArchive->m_filesOffset); + m_nodesAddress = static_cast(archiveStart) + parse(rawArchive->nodesOffset); + m_filesAddress = static_cast(archiveStart) + parse(rawArchive->filesOffset); // "The right bound of the root node is the number of nodes" - m_count = parse(node(0)->m_directory.m_next); + m_count = parse(node(0)->m_directory.next); // Strings exist directly after the last node m_strings = reinterpret_cast(reinterpret_cast(m_nodesAddress) + m_count); m_currentNode = 0; @@ -38,11 +38,11 @@ s32 ArchiveHandle::convertPathToEntryId(const char *path) const { if (path[0] == '.') { if (path[1] == '.') { if (path[2] == '/') { - entryId = node(entryId)->m_directory.m_parent; + entryId = node(entryId)->m_directory.parent; path += 3; continue; } else if (path[2] == '\0') { - return node(entryId)->m_directory.m_parent; + return node(entryId)->m_directory.parent; } else { // Malformed "..*" case return -1; @@ -64,7 +64,7 @@ s32 ArchiveHandle::convertPathToEntryId(const char *path) const { bool found = false; const u32 anchor = entryId++; - while (entryId < parse(node(anchor)->m_directory.m_next)) { + while (entryId < parse(node(anchor)->m_directory.next)) { if (!node(anchor)->isDirectory() && endOfPath) { entryId++; continue; @@ -107,13 +107,13 @@ bool ArchiveHandle::open(s32 entryId, FileInfo &info) const { return false; } - info.m_startOffset = parse(node_->m_file.m_startAddress); - info.m_length = parse(node_->m_file.m_length); + info.startOffset = parse(node_->file.startAddress); + info.length = parse(node_->file.length); return true; } void *ArchiveHandle::getFileAddress(const FileInfo &info) const { - return static_cast(m_startAddress) + info.m_startOffset; + return static_cast(m_startAddress) + info.startOffset; } ArchiveHandle::Node *ArchiveHandle::node(s32 entryId) const { @@ -126,16 +126,15 @@ void *ArchiveHandle::startAddress() const { } bool ArchiveHandle::RawArchive::isValidSignature() const { - auto signature = parse(m_signature); - return signature == U8_SIGNATURE; + return parse(signature) == U8_SIGNATURE; } bool ArchiveHandle::Node::isDirectory() const { - return !!(m_str[0]); + return !!(str[0]); } u32 ArchiveHandle::Node::stringOffset() const { - return parse(m_val) & 0xFFFFFF; + return parse(val) & 0xFFFFFF; } } // namespace Abstract diff --git a/source/abstract/Archive.hh b/source/abstract/Archive.hh index 160fdfa0..0f6457c2 100644 --- a/source/abstract/Archive.hh +++ b/source/abstract/Archive.hh @@ -10,10 +10,10 @@ namespace Abstract { struct RawArchive { bool isValidSignature() const; - u32 m_signature; - u32 m_nodesOffset; - u32 m_nodesSize; - u32 m_filesOffset; + u32 signature; + u32 nodesOffset; + u32 nodesSize; + u32 filesOffset; }; class ArchiveHandle { @@ -21,10 +21,10 @@ public: struct RawArchive { bool isValidSignature() const; - u32 m_signature; - u32 m_nodesOffset; - u32 m_nodesSize; - u32 m_filesOffset; + u32 signature; + u32 nodesOffset; + u32 nodesSize; + u32 filesOffset; }; // TODO: union @@ -34,24 +34,24 @@ public: u32 stringOffset() const; union { - u32 m_val; - u8 m_str[4]; + u32 val; + u8 str[4]; }; union { struct { - u32 m_parent; - u32 m_next; + u32 parent; + u32 next; } m_directory; struct { - u32 m_startAddress; - u32 m_length; - } m_file; + u32 startAddress; + u32 length; + } file; }; }; struct FileInfo { - u32 m_startOffset; - u32 m_length; + u32 startOffset; + u32 length; }; ArchiveHandle(void *archiveStart); diff --git a/source/egg/geom/Sphere.cc b/source/egg/geom/Sphere.cc new file mode 100644 index 00000000..9627b72d --- /dev/null +++ b/source/egg/geom/Sphere.cc @@ -0,0 +1,16 @@ +#include "Sphere.hh" + +namespace EGG { + +Sphere3f::Sphere3f(const EGG::Vector3f &v, f32 r) : pos(v), radius(r) {} + +bool Sphere3f::isInsideOtherSphere(const Sphere3f &rhs) const { + f32 radiusDiff = rhs.radius - radius; + if (radiusDiff < 0.0f) { + return false; + } + + return rhs.pos.ps_sqDistance(this->pos) < radiusDiff * radiusDiff; +} + +} // namespace EGG diff --git a/source/egg/geom/Sphere.hh b/source/egg/geom/Sphere.hh new file mode 100644 index 00000000..ffa7eae0 --- /dev/null +++ b/source/egg/geom/Sphere.hh @@ -0,0 +1,16 @@ +#pragma once + +#include "egg/math/Vector.hh" + +namespace EGG { + +struct Sphere3f { + Sphere3f(const Vector3f &v, f32 r); + + bool isInsideOtherSphere(const Sphere3f &rhs) const; + + Vector3f pos; + f32 radius; +}; + +} // namespace EGG diff --git a/source/egg/math/BoundBox.cc b/source/egg/math/BoundBox.cc index a4d6abcb..5a9813e2 100644 --- a/source/egg/math/BoundBox.cc +++ b/source/egg/math/BoundBox.cc @@ -11,21 +11,21 @@ BoundBox2f::BoundBox2f() { BoundBox2f::~BoundBox2f() = default; void BoundBox2f::resetBound() { - mMin.set(std::numeric_limits::max()); - mMax.set(-std::numeric_limits::max()); + min.set(std::numeric_limits::max()); + max.set(-std::numeric_limits::max()); } -void BoundBox2f::setDirect(const Vector2f &min, const Vector2f &max) { - mMax = max; - mMin = min; +void BoundBox2f::setDirect(const Vector2f &vMin, const Vector2f &vMax) { + max = vMax; + min = vMin; } -void BoundBox2f::setMin(const Vector2f &min) { - mMin = min; +void BoundBox2f::setMin(const Vector2f &v) { + min = v; } -void BoundBox2f::setMax(const Vector2f &max) { - mMax = max; +void BoundBox2f::setMax(const Vector2f &v) { + max = v; } BoundBox3f::BoundBox3f() { @@ -35,21 +35,26 @@ BoundBox3f::BoundBox3f() { BoundBox3f::~BoundBox3f() = default; void BoundBox3f::resetBound() { - mMin.set(std::numeric_limits::max()); - mMax.set(-std::numeric_limits::max()); + min.set(std::numeric_limits::max()); + max.set(-std::numeric_limits::max()); } -void BoundBox3f::setDirect(const Vector3f &min, const Vector3f &max) { - mMax = max; - mMin = min; +void BoundBox3f::setZero() { + min.setZero(); + max.setZero(); } -void BoundBox3f::setMin(const Vector3f &min) { - mMin = min; +void BoundBox3f::setDirect(const Vector3f &vMin, const Vector3f &vMax) { + max = vMax; + min = vMin; } -void BoundBox3f::setMax(const Vector3f &max) { - mMax = max; +void BoundBox3f::setMin(const Vector3f &v) { + min = v; +} + +void BoundBox3f::setMax(const Vector3f &v) { + max = v; } } // namespace EGG diff --git a/source/egg/math/BoundBox.hh b/source/egg/math/BoundBox.hh index 72db7cfa..665e8a7b 100644 --- a/source/egg/math/BoundBox.hh +++ b/source/egg/math/BoundBox.hh @@ -9,12 +9,12 @@ struct BoundBox2f { ~BoundBox2f(); void resetBound(); - void setDirect(const Vector2f &max, const Vector2f &min); - void setMin(const Vector2f &min); - void setMax(const Vector2f &max); + void setDirect(const Vector2f &vMax, const Vector2f &vMin); + void setMin(const Vector2f &v); + void setMax(const Vector2f &v); - Vector2f mMin; - Vector2f mMax; + Vector2f min; + Vector2f max; }; struct BoundBox3f { @@ -22,12 +22,13 @@ struct BoundBox3f { ~BoundBox3f(); void resetBound(); - void setDirect(const Vector3f &max, const Vector3f &min); - void setMin(const Vector3f &min); - void setMax(const Vector3f &max); + void setZero(); + void setDirect(const Vector3f &vMin, const Vector3f &vMax); + void setMin(const Vector3f &V); + void setMax(const Vector3f &v); - Vector3f mMin; - Vector3f mMax; + Vector3f min; + Vector3f max; }; } // namespace EGG diff --git a/source/egg/math/Math.cc b/source/egg/math/Math.cc index d993603a..c40dda16 100644 --- a/source/egg/math/Math.cc +++ b/source/egg/math/Math.cc @@ -270,17 +270,11 @@ f32 sqrt(f32 x) { // CREDIT: Hanachan f32 frsqrt(f32 x) { - auto extract = [](f64 val) -> f64 { - u64 bits = std::bit_cast(val); - bits = (bits & 0xfffffffff8000000ULL) + (bits & 0x8000000); - return std::bit_cast(bits); - }; - // frsqrte instruction f64 est = frsqrte(x); // Newton-Raphson refinement - f32 tmp0 = static_cast(est * extract(est)); + f32 tmp0 = static_cast(est * force25Bit(est)); f32 tmp1 = static_cast(est * 0.5f); f32 tmp2 = static_cast(3.0f - static_cast(tmp0) * static_cast(x)); return tmp1 * tmp2; @@ -328,4 +322,19 @@ f32 acos(f32 x) { return std::acos(x); } +f32 abs(f32 x) { + return std::abs(x); +} + +f32 fma(f32 x, f32 y, f32 z) { + return static_cast( + static_cast(x) * force25Bit(static_cast(y)) + static_cast(z)); +} + +f64 force25Bit(f64 x) { + u64 bits = std::bit_cast(x); + bits = (bits & 0xfffffffff8000000ULL) + (bits & 0x8000000); + return std::bit_cast(bits); +} + } // namespace EGG::Mathf diff --git a/source/egg/math/Math.hh b/source/egg/math/Math.hh index 0a33bd4d..58f05282 100644 --- a/source/egg/math/Math.hh +++ b/source/egg/math/Math.hh @@ -4,6 +4,7 @@ #define F_PI 3.1415927f #define DEG2RAD (F_PI / 180.0f) +#define RAD2DEG (180.0f / F_PI) #define DEG2FIDX (256.0f / 360.0f) #define RAD2FIDX (128.0f / F_PI) @@ -18,6 +19,12 @@ f32 sin(f32 x); f32 cos(f32 x); f32 acos(f32 x); +f32 abs(f32 x); + +f32 fma(f32 x, f32 y, f32 z); + +f64 force25Bit(f64 x); + // sin/cos struct struct SinCosEntry { f32 sinVal, cosVal, sinDt, cosDt; @@ -25,8 +32,8 @@ struct SinCosEntry { // frsqrte matching struct BaseAndDec { - int m_base; - int m_dec; + int base; + int dec; }; union c64 { @@ -124,7 +131,7 @@ static inline f64 frsqrte(const f64 val) { const int i = static_cast(mantissa >> 37); const int index = i / 2048 + (odd_exponent ? 16 : 0); const auto &entry = frsqrte_expected[index]; - input.u |= static_cast(entry.m_base - entry.m_dec * (i % 2048)) << 26; + input.u |= static_cast(entry.base - entry.dec * (i % 2048)) << 26; return input.f; } diff --git a/source/egg/math/Matrix.cc b/source/egg/math/Matrix.cc index 892aa979..b811c2dc 100644 --- a/source/egg/math/Matrix.cc +++ b/source/egg/math/Matrix.cc @@ -1,7 +1,11 @@ #include "Matrix.hh" +#include "egg/math/Math.hh" + namespace EGG { +using namespace Mathf; + Matrix34f::Matrix34f() { makeZero(); } @@ -24,28 +28,210 @@ Matrix34f::Matrix34f(f32 _e00, f32 _e01, f32 _e02, f32 _e03, f32 _e10, f32 _e11, Matrix34f::~Matrix34f() = default; +// Credit: elijah-thomas774/bba-wd void Matrix34f::makeQT(const Quatf &q, const Vector3f &t) { - mtx[0][0] = 1.0f - 2.0f * (q.v.y * q.v.y + q.v.z * q.v.z); - mtx[0][1] = 2.0f * (q.v.x * q.v.y - q.w * q.v.z); - mtx[0][2] = 2.0f * (q.v.x * q.v.z + q.w * q.v.y); + f32 yy = 2.0f * q.v.y * q.v.y; + f32 zz = 2.0f * q.v.z * q.v.z; + f32 xx = 2.0f * q.v.x * q.v.x; + f32 xy = 2.0f * q.v.x * q.v.y; + f32 xz = 2.0f * q.v.x * q.v.z; + f32 yz = 2.0f * q.v.y * q.v.z; + f32 wz = 2.0f * q.w * q.v.z; + f32 wx = 2.0f * q.w * q.v.x; + f32 wy = 2.0f * q.w * q.v.y; + + mtx[0][0] = 1.0f - yy - zz; + mtx[0][1] = xy - wz; + mtx[0][2] = xz + wy; + + mtx[1][0] = xy + wz; + mtx[1][1] = 1.0f - xx - zz; + mtx[1][2] = yz - wx; + + mtx[2][0] = xz - wy; + mtx[2][1] = yz + wx; + mtx[2][2] = 1.0f - xx - yy; + mtx[0][3] = t.x; - mtx[1][0] = 2.0f * (q.v.x * q.v.y + q.w * q.v.y); - mtx[1][1] = 1.0 - 2.0f * (q.v.x * q.v.x + q.v.z * q.v.z); - mtx[1][2] = 2.0f * (q.v.y * q.v.z - q.w * q.v.x); mtx[1][3] = t.y; - mtx[2][0] = 2.0f * (q.v.x * q.v.z - q.w * q.v.y); - mtx[2][1] = 2.0f * (q.v.y * q.v.z + q.w * q.v.x); - mtx[2][2] = 1.0 - 2.0f * (q.v.x * q.v.x + q.v.y * q.v.y); mtx[2][3] = t.z; } +void Matrix34f::makeQ(const Quatf &q) { + f32 yy = 2.0f * q.v.y * q.v.y; + f32 zz = 2.0f * q.v.z * q.v.z; + f32 xx = 2.0f * q.v.x * q.v.x; + f32 xy = 2.0f * q.v.x * q.v.y; + f32 xz = 2.0f * q.v.x * q.v.z; + f32 yz = 2.0f * q.v.y * q.v.z; + f32 wz = 2.0f * q.w * q.v.z; + f32 wx = 2.0f * q.w * q.v.x; + f32 wy = 2.0f * q.w * q.v.y; + + mtx[0][0] = 1.0f - yy - zz; + mtx[0][1] = xy - wz; + mtx[0][2] = xz + wy; + + mtx[1][0] = xy + wz; + mtx[1][1] = 1.0f - xx - zz; + mtx[1][2] = yz - wx; + + mtx[2][0] = xz - wy; + mtx[2][1] = yz + wx; + mtx[2][2] = 1.0f - xx - yy; + + mtx[0][3] = 0.0f; + mtx[1][3] = 0.0f; + mtx[2][3] = 0.0f; +} + +void Matrix34f::makeRT(const Vector3f &r, const Vector3f &t) { + EGG::Vector3f s = EGG::Vector3f(sin(r.x), sin(r.y), sin(r.z)); + EGG::Vector3f c = EGG::Vector3f(cos(r.x), cos(r.y), cos(r.z)); + + const f32 c0_c2 = c.x * c.z; + const f32 s0_s1 = s.x * s.y; + const f32 c0_s2 = c.x * s.z; + + mtx[0][0] = (c.y * c.z); + mtx[1][0] = (c.y * s.z); + mtx[2][0] = (-s.y); + + mtx[0][1] = (s0_s1 * c.z) - c0_s2; + mtx[1][1] = (s0_s1 * s.z) + c0_c2; + mtx[2][1] = (s.x * c.y); + + mtx[0][2] = (c0_c2 * s.y) + (s.x * s.z); + mtx[1][2] = (c0_s2 * s.y) - (s.x * c.z); + mtx[2][2] = (c.x * c.y); + + mtx[0][3] = t.x; + mtx[1][3] = t.y; + mtx[2][3] = t.z; +} + +void Matrix34f::makeR(const Vector3f &r) { + EGG::Vector3f s = EGG::Vector3f(sin(r.x), sin(r.y), sin(r.z)); + EGG::Vector3f c = EGG::Vector3f(cos(r.x), cos(r.y), cos(r.z)); + + const f32 c0_c2 = c.x * c.z; + const f32 s0_s1 = s.x * s.y; + const f32 c0_s2 = c.x * s.z; + + mtx[0][0] = (c.y * c.z); + mtx[1][0] = (c.y * s.z); + mtx[2][0] = (-s.y); + + mtx[0][1] = (s0_s1 * c.z) - c0_s2; + mtx[1][1] = (s0_s1 * s.z) + c0_c2; + mtx[2][1] = (s.x * c.y); + + mtx[0][2] = (c0_c2 * s.y) + (s.x * s.z); + mtx[1][2] = (c0_s2 * s.y) - (s.x * c.z); + mtx[2][2] = (c.x * c.y); + + mtx[0][3] = 0.0f; + mtx[1][3] = 0.0f; + mtx[2][3] = 0.0f; +} + void Matrix34f::makeZero() { - for (auto &n : a) { - n = 0.0f; + *this = Matrix34f::zero; +} + +Matrix34f Matrix34f::multiplyTo(const Matrix34f &rhs) const { + Matrix34f mat; + + mat(0, 0) = fma(rhs(2, 0), mtx[0][2], fma(rhs(1, 0), mtx[0][1], rhs(0, 0) * mtx[0][0])); + mat(0, 1) = fma(rhs(2, 1), mtx[0][2], fma(rhs(1, 1), mtx[0][1], rhs(0, 1) * mtx[0][0])); + mat(1, 0) = fma(rhs(2, 0), mtx[1][2], fma(rhs(1, 0), mtx[1][1], rhs(0, 0) * mtx[1][0])); + mat(1, 1) = fma(rhs(2, 1), mtx[1][2], fma(rhs(1, 1), mtx[1][1], rhs(0, 1) * mtx[1][0])); + mat(0, 2) = fma(rhs(2, 2), mtx[0][2], fma(rhs(1, 2), mtx[0][1], rhs(0, 2) * mtx[0][0])); + mat(0, 3) = fma(1.0f, mtx[0][3], + fma(rhs(2, 3), mtx[0][2], fma(rhs(1, 3), mtx[0][1], rhs(0, 3) * mtx[0][0]))); + mat(1, 2) = + mtx[1][3] + fma(rhs(2, 2), mtx[1][2], fma(rhs(1, 2), mtx[1][1], rhs(0, 2) * mtx[1][0])); + mat(1, 3) = fma(1.0f, mtx[1][3], + fma(rhs(2, 3), mtx[1][2], fma(rhs(1, 3), mtx[1][1], rhs(0, 3) * mtx[1][0]))); + mat(2, 0) = fma(rhs(2, 0), mtx[2][2], fma(rhs(1, 0), mtx[2][1], rhs(0, 0) * mtx[2][0])); + mat(2, 1) = fma(rhs(2, 1), mtx[2][2], fma(rhs(1, 1), mtx[2][1], rhs(0, 1) * mtx[2][0])); + mat(2, 2) = + mtx[2][3] + fma(rhs(2, 2), mtx[2][2], fma(rhs(1, 2), mtx[2][1], rhs(0, 2) * mtx[2][0])); + mat(2, 3) = fma(1.0f, mtx[2][3], + fma(rhs(2, 3), mtx[2][2], fma(rhs(1, 3), mtx[2][1], rhs(0, 3) * mtx[2][0]))); + + return mat; +} + +Vector3f Matrix34f::multVector(const Vector3f &vec) const { + Vector3f ret; + + ret.x = mtx[0][2] * vec.z + mtx[0][0] * vec.x + mtx[0][3] + mtx[0][1] * vec.y; + ret.y = mtx[1][2] * vec.z + mtx[1][0] * vec.x + mtx[1][3] + mtx[1][1] * vec.y; + ret.z = mtx[2][2] * vec.z + mtx[2][0] * vec.x + mtx[2][3] + mtx[2][1] * vec.y; + + return ret; +} + +Vector3f Matrix34f::ps_multVector(const Vector3f &vec) const { + Vector3f ret; + + ret.x = fma(mtx[0][2], vec.z, mtx[0][0] * vec.x) + fma(mtx[0][3], 1.0f, mtx[0][1] * vec.y); + ret.y = fma(mtx[1][2], vec.z, mtx[1][0] * vec.x) + fma(mtx[1][3], 1.0f, mtx[1][1] * vec.y); + ret.z = fma(mtx[2][2], vec.z, mtx[2][0] * vec.x) + fma(mtx[2][3], 1.0f, mtx[2][1] * vec.y); + + return ret; +} + +Vector3f Matrix34f::multVector33(const Vector3f &vec) const { + Vector3f ret; + + ret.x = mtx[0][2] * vec.z + mtx[0][0] * vec.x + mtx[0][1] * vec.y; + + ret.y = mtx[1][2] * vec.z + mtx[1][0] * vec.x + mtx[1][1] * vec.y; + ret.z = mtx[2][2] * vec.z + mtx[2][0] * vec.x + mtx[2][1] * vec.y; + + return ret; +} + +Matrix34f Matrix34f::inverseTo() const { + f32 determinant = (((mtx[2][1] * mtx[0][2] * mtx[1][0] + mtx[2][2] * mtx[0][0] * mtx[1][1] + + mtx[2][0] * mtx[0][1] * mtx[1][2]) - + mtx[0][2] * mtx[2][0] * mtx[1][1]) - + mtx[2][2] * mtx[1][0] * mtx[0][1]) - + mtx[1][2] * mtx[0][0] * mtx[2][1]; + + if (determinant == 0.0f) { + return Matrix34f::ident; } + + f32 invDet = 1.0f / determinant; + + Matrix34f ret; + + ret(0, 2) = (mtx[0][1] * mtx[1][2] - mtx[1][1] * mtx[0][2]) * invDet; + ret(1, 2) = -(mtx[0][0] * mtx[1][2] - mtx[0][2] * mtx[1][0]) * invDet; + ret(2, 1) = -(mtx[0][0] * mtx[2][1] - mtx[2][0] * mtx[0][1]) * invDet; + ret(2, 2) = (mtx[0][0] * mtx[1][1] - mtx[1][0] * mtx[0][1]) * invDet; + ret(2, 0) = (mtx[1][0] * mtx[2][1] - mtx[2][0] * mtx[1][1]) * invDet; + ret(0, 0) = (mtx[1][1] * mtx[2][2] - mtx[2][1] * mtx[1][2]) * invDet; + ret(0, 1) = -(mtx[0][1] * mtx[2][2] - mtx[2][1] * mtx[0][2]) * invDet; + ret(1, 0) = -(mtx[1][0] * mtx[2][2] - mtx[2][0] * mtx[1][2]) * invDet; + ret(1, 1) = (mtx[0][0] * mtx[2][2] - mtx[2][0] * mtx[0][2]) * invDet; + + return ret; +} + +void Matrix34f::setAxisRotation(f32 angle, const EGG::Vector3f &axis) { + EGG::Quatf q; + q.setAxisRotation(angle, axis); + makeQ(q); } const Matrix34f Matrix34f::ident(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); +const Matrix34f Matrix34f::zero(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f); + } // namespace EGG diff --git a/source/egg/math/Matrix.hh b/source/egg/math/Matrix.hh index 865a9c57..b899cc37 100644 --- a/source/egg/math/Matrix.hh +++ b/source/egg/math/Matrix.hh @@ -11,15 +11,34 @@ public: f32 _e20, f32 _e21, f32 _e22, f32 _e23); ~Matrix34f(); + bool operator==(const Matrix34f &rhs) const { + return mtx == rhs.mtx; + } + f32 &operator()(int i, int j) { return mtx[i][j]; } + f32 operator()(int i, int j) const { + return mtx[i][j]; + } + // Q for Quaternion, T for translation void makeQT(const Quatf &q, const Vector3f &t); + void makeQ(const Quatf &q); + void makeRT(const Vector3f &r, const Vector3f &t); + void makeR(const Vector3f &r); void makeZero(); + void setAxisRotation(f32 angle, const EGG::Vector3f &axis); + + Matrix34f multiplyTo(const Matrix34f &rhs) const; + Vector3f multVector(const Vector3f &vec) const; + Vector3f ps_multVector(const Vector3f &vec) const; + Vector3f multVector33(const Vector3f &vec) const; + Matrix34f inverseTo() const; static const Matrix34f ident; + static const Matrix34f zero; private: union { diff --git a/source/egg/math/Quat.cc b/source/egg/math/Quat.cc index 81174320..f5003e6b 100644 --- a/source/egg/math/Quat.cc +++ b/source/egg/math/Quat.cc @@ -47,16 +47,23 @@ Vector3f Quatf::rotateVector(const Vector3f &vec) const { return res.v; } +Vector3f Quatf::rotateVectorInv(const Vector3f &vec) const { + Quatf conj = conjugate(); + Quatf res = conj * vec; + res *= *this; + return res.v; +} + Quatf Quatf::slerpTo(const Quatf &q1, f32 t) const { f32 dot_ = std::max(-1.0f, std::min(1.0f, dot(q1))); bool bDot = dot_ < 0.0f; - dot_ = std::abs(dot_); + dot_ = Mathf::abs(dot_); f32 acos = Mathf::acos(dot_); f32 sin = Mathf::sin(acos); f32 s; - if (std::abs(sin) < 0.00001f) { + if (Mathf::abs(sin) < 0.00001f) { s = 1.0f - t; } else { f32 invSin = 1.0f / sin; @@ -80,6 +87,15 @@ f32 Quatf::dot(const Quatf &q) const { return w * q.w + v.dot(q.v); } +void Quatf::setAxisRotation(f32 angle, const EGG::Vector3f &axis) { + const f32 half_angle = angle * 0.5f; + const f32 c = Mathf::cos(half_angle); + const f32 s = Mathf::sin(half_angle); + + w = c; + v = axis * s; +} + void Quatf::read(Stream &stream) { v.read(stream); w = stream.read_f32(); diff --git a/source/egg/math/Quat.hh b/source/egg/math/Quat.hh index 7f490e0a..b7b10bdf 100644 --- a/source/egg/math/Quat.hh +++ b/source/egg/math/Quat.hh @@ -17,12 +17,28 @@ struct Quatf { return *this; } + Quatf operator+(const Quatf &rhs) const { + return Quatf(w + rhs.w, v + rhs.v); + } + + Quatf &operator+=(const Quatf &rhs) { + return *this = *this + rhs; + } + Quatf operator*(const Vector3f &vec) const { Vector3f cross = v.cross(vec); Vector3f scale = vec * w; return Quatf(-v.dot(vec), cross + scale); } + Quatf operator*(f32 scalar) const { + return Quatf(w * scalar, v * scalar); + } + + Quatf &operator*=(f32 scalar) { + return *this = *this * scalar; + } + Quatf operator*(const Quatf &rhs) const { Vector3f cross = v.cross(rhs.v); Vector3f scaleLhs = v * rhs.w; @@ -47,9 +63,11 @@ struct Quatf { void normalise(); Quatf conjugate() const; Vector3f rotateVector(const Vector3f &vec) const; + Vector3f rotateVectorInv(const Vector3f &vec) const; Quatf slerpTo(const Quatf &q2, f32 t) const; f32 dot() const; f32 dot(const Quatf &q) const; + void setAxisRotation(f32 angle, const EGG::Vector3f &axis); void read(Stream &stream); diff --git a/source/egg/math/Vector.cc b/source/egg/math/Vector.cc index fe5c5225..a706b885 100644 --- a/source/egg/math/Vector.cc +++ b/source/egg/math/Vector.cc @@ -49,9 +49,15 @@ f32 Vector3f::dot(const Vector3f &rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; } +f32 Vector3f::ps_dot() const { + f32 y_ = y * y; + f32 xy = Mathf::fma(x, x, y_); + return xy + z * z; +} + f32 Vector3f::ps_dot(const Vector3f &rhs) const { f32 y_ = y * rhs.y; - f32 xy = static_cast(static_cast(x) * static_cast(rhs.x) + static_cast(y_)); + f32 xy = Mathf::fma(x, rhs.x, y_); return xy + z * rhs.z; } @@ -60,12 +66,12 @@ Vector3f Vector3f::cross(const Vector3f &rhs) const { } f32 Vector3f::length() const { - return dot() > FLT_EPSILON ? Mathf::sqrt(dot()) : 0.0f; + return Mathf::sqrt(dot()); } f32 Vector3f::normalise() { f32 len = length(); - if (len != 0.0f) { + if (FLT_EPSILON < dot()) { *this = *this * (1.0f / len); } @@ -104,6 +110,38 @@ std::pair Vector3f::projAndRej(const Vector3f &rhs) { return std::pair(proj(rhs), rej(rhs)); } +f32 Vector3f::sqDistance(const Vector3f &rhs) const { + const EGG::Vector3f diff = *this - rhs; + return diff.dot(); +} + +f32 Vector3f::ps_sqDistance(const Vector3f &rhs) const { + const EGG::Vector3f diff = *this - rhs; + return diff.ps_dot(); +} + +Vector3f Vector3f::abs() const { + return Vector3f(Mathf::abs(x), Mathf::abs(y), Mathf::abs(z)); +} + +Vector3f Vector3f::perpInPlane(const EGG::Vector3f &rhs, bool normalise) const { + if (Mathf::abs(dot(rhs)) == 1.0f) { + return EGG::Vector3f::zero; + } + + f32 _x = (rhs.z * x - rhs.x * z) * rhs.z - (rhs.x * y - rhs.y * x) * rhs.y; + f32 _y = (rhs.x * y - rhs.y * x) * rhs.x - (rhs.y * z - rhs.z * y) * rhs.z; + f32 _z = (rhs.y * z - rhs.z * y) * rhs.y - (rhs.z * x - rhs.x * z) * rhs.x; + + EGG::Vector3f ret(_x, _y, _z); + + if (normalise) { + ret.normalise(); + } + + return ret; +} + void Vector3f::read(Stream &stream) { x = stream.read_f32(); y = stream.read_f32(); diff --git a/source/egg/math/Vector.hh b/source/egg/math/Vector.hh index 2404eed1..18548179 100644 --- a/source/egg/math/Vector.hh +++ b/source/egg/math/Vector.hh @@ -63,6 +63,10 @@ struct Vector3f { return Vector3f(x - rhs.x, y - rhs.y, z - rhs.z); } + Vector3f &operator-=(const Vector3f &rhs) { + return *this = *this - rhs; + } + Vector3f operator+(const Vector3f &rhs) const { return Vector3f(x + rhs.x, y + rhs.y, z + rhs.z); } @@ -71,6 +75,19 @@ struct Vector3f { return *this = *this + rhs; } + Vector3f operator+(f32 val) const { + return Vector3f(x + val, y + val, z + val); + } + + Vector3f &operator+=(f32 val) { + return *this = *this + val; + } + + // TODO: This is surely not an actual operation, but I imagine this will be used often + Vector3f operator*(const Vector3f &rhs) const { + return Vector3f(x * rhs.x, y * rhs.y, z * rhs.z); + } + Vector3f operator*(f32 scalar) const { return Vector3f(x * scalar, y * scalar, z * scalar); } @@ -102,6 +119,7 @@ struct Vector3f { Vector3f cross(const EGG::Vector3f &rhs) const; f32 dot() const; f32 dot(const EGG::Vector3f &rhs) const; + f32 ps_dot() const; f32 ps_dot(const EGG::Vector3f &rhs) const; f32 length() const; f32 normalise(); @@ -110,6 +128,10 @@ struct Vector3f { Vector3f proj(const Vector3f &rhs) const; Vector3f rej(const Vector3f &rhs) const; std::pair projAndRej(const Vector3f &rhs); + f32 sqDistance(const Vector3f &rhs) const; + f32 ps_sqDistance(const Vector3f &rhs) const; + Vector3f abs() const; + Vector3f perpInPlane(const EGG::Vector3f &rhs, bool normalise) const; void read(Stream &stream); diff --git a/source/game/field/CollisionDirector.cc b/source/game/field/CollisionDirector.cc index 63b6e953..ab2bae1e 100644 --- a/source/game/field/CollisionDirector.cc +++ b/source/game/field/CollisionDirector.cc @@ -2,16 +2,21 @@ namespace Field { +void CollisionDirector::checkCourseColNarrScLocal(f32 radius, const EGG::Vector3f &pos, + KCLTypeMask mask, u32 /*unused*/) { + CourseColMgr::Instance()->scaledNarrowScopeLocal(1.0f, radius, nullptr, pos, mask); +} + bool CollisionDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CourseColMgr::CollisionInfo *pInfo, KCLTypeMask *pFlagsOut, u32 /*param_8*/) { if (pInfo) { - pInfo->m_bbox.mMin = EGG::Vector3f::zero; - pInfo->m_bbox.mMax = EGG::Vector3f::zero; + pInfo->bbox.min = EGG::Vector3f::zero; + pInfo->bbox.max = EGG::Vector3f::zero; pInfo->_50 = -std::numeric_limits::min(); - pInfo->m_wallDist = -std::numeric_limits::min(); - pInfo->m_floorDist = -std::numeric_limits::min(); - pInfo->m_perpendicularity = 0.0f; + pInfo->wallDist = -std::numeric_limits::min(); + pInfo->floorDist = -std::numeric_limits::min(); + pInfo->perpendicularity = 0.0f; } if (pFlagsOut) { @@ -27,12 +32,55 @@ bool CollisionDirector::checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, } if (colliding && pInfo) { - pInfo->m_minPlusMax = pInfo->m_bbox.mMin + pInfo->m_bbox.mMax; + pInfo->tangentOff = pInfo->bbox.min + pInfo->bbox.max; } return colliding; } +bool CollisionDirector::checkSphereCachedPartialPush(const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CourseColMgr::CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 radius, u32 /*start*/) { + if (colInfo) { + colInfo->bbox.setZero(); + } + + if (typeMaskOut) { + resetCollisionEntries(typeMaskOut); + } + + bool hasCourseCol = CourseColMgr::Instance()->checkSphereCachedPartialPush(nullptr, pos, + prevPos, typeMask, colInfo, typeMaskOut, 1.0f, radius); + + return hasCourseCol; +} + +bool CollisionDirector::checkSphereCachedFullPush(const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CourseColMgr::CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 radius, u32 /*start*/) { + if (colInfo) { + colInfo->bbox.min.setZero(); + colInfo->bbox.max.setZero(); + colInfo->_50 = -std::numeric_limits::min(); + colInfo->wallDist = -std::numeric_limits::min(); + colInfo->floorDist = -std::numeric_limits::min(); + colInfo->perpendicularity = 0.0f; + } + + if (typeMaskOut) { + resetCollisionEntries(typeMaskOut); + } + + bool hasCourseCol = CourseColMgr::Instance()->checkSphereCachedFullPush(nullptr, pos, prevPos, + typeMask, colInfo, typeMaskOut, 1.0f, radius); + + if (hasCourseCol && colInfo) { + colInfo->tangentOff = colInfo->bbox.min + colInfo->bbox.max; + } + + return hasCourseCol; +} + void CollisionDirector::resetCollisionEntries(KCLTypeMask *ptr) { *ptr = 0; m_collisionEntryCount = 0; @@ -49,6 +97,25 @@ void CollisionDirector::pushCollisionEntry(f32 dist, KCLTypeMask *typeMask, KCLT m_entries[m_collisionEntryCount++] = CollisionEntry(kclTypeBit, attribute, dist); } +const CollisionDirector::CollisionEntry *CollisionDirector::closestCollisionEntry() const { + return m_closestCollisionEntry; +} + +bool CollisionDirector::findClosestCollisionEntry(KCLTypeMask * /*typeMask*/, KCLTypeMask type) { + m_closestCollisionEntry = nullptr; + f32 minDist = -std::numeric_limits::min(); + + for (const auto &entry : m_entries) { + u32 typeMask = entry.typeMask & type; + if (typeMask != 0 && entry.dist > minDist) { + minDist = entry.dist; + m_closestCollisionEntry = &entry; + } + } + + return !!m_closestCollisionEntry; +} + CollisionDirector *CollisionDirector::CreateInstance() { assert(!s_instance); s_instance = new CollisionDirector; diff --git a/source/game/field/CollisionDirector.hh b/source/game/field/CollisionDirector.hh index 990a2c6c..4fc86971 100644 --- a/source/game/field/CollisionDirector.hh +++ b/source/game/field/CollisionDirector.hh @@ -16,13 +16,26 @@ public: f32 dist; }; + void checkCourseColNarrScLocal(f32 radius, const EGG::Vector3f &pos, KCLTypeMask mask, + u32 /*unused*/); + bool checkSphereFullPush(f32 radius, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CourseColMgr::CollisionInfo *pInfo, KCLTypeMask *pFlagsOut, - u32 /*param_8*/); + u32 /*start*/); + + bool checkSphereCachedPartialPush(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, + KCLTypeMask typeMask, CourseColMgr::CollisionInfo *colInfo, KCLTypeMask *typeMaskOut, + f32 radius, u32 start); + bool checkSphereCachedFullPush(const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, + KCLTypeMask typeMask, CourseColMgr::CollisionInfo *colInfo, KCLTypeMask *typeMaskOut, + f32 radius, u32 start); void resetCollisionEntries(KCLTypeMask *ptr); void pushCollisionEntry(f32 dist, KCLTypeMask *typeMask, KCLTypeMask kclTypeBit, u16 attribute); + const CollisionEntry *closestCollisionEntry() const; + bool findClosestCollisionEntry(KCLTypeMask *typeMask, KCLTypeMask type); + static CollisionDirector *CreateInstance(); static CollisionDirector *Instance(); static void DestroyInstance(); @@ -31,7 +44,7 @@ private: CollisionDirector(); ~CollisionDirector(); - CollisionEntry *m_closestCollisionEntry; + const CollisionEntry *m_closestCollisionEntry; std::array m_entries; size_t m_collisionEntryCount; diff --git a/source/game/field/CourseColMgr.cc b/source/game/field/CourseColMgr.cc index 5a96247c..86059a8e 100644 --- a/source/game/field/CourseColMgr.cc +++ b/source/game/field/CourseColMgr.cc @@ -9,11 +9,21 @@ namespace Field { void CourseColMgr::init() { + // In the base game, this file is loaded in CollisionDirector::CreateInstance and passed into + // this function. It's simpler to just keep it here. void *file = LoadFile("course.kcl"); m_data = new KColData(file); } -/* 0x807c3e84 - CourseColMgr::checkCollisionOther */ +void CourseColMgr::scaledNarrowScopeLocal(f32 scale, f32 radius, KColData *data, + const EGG::Vector3f &pos, KCLTypeMask mask) { + if (!data) { + data = m_data; + } + + data->narrowScopeLocal(pos / scale, radius / scale, mask); +} + bool CourseColMgr::checkSphereFullPush(f32 scalar, f32 radius, KColData *data, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *info, KCLTypeMask *kcl_flags_out) { @@ -29,8 +39,53 @@ bool CourseColMgr::checkSphereFullPush(f32 scalar, f32 radius, KColData *data, if (info) { return doCheckWithFullInfoPush(data, &KColData::checkSphere, info, kcl_flags_out); } - return false; // doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, - // kcl_flags_out); + return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, kcl_flags_out); +} + +bool CourseColMgr::checkSphereCachedPartialPush(KColData *data, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 scale, f32 radius) { + if (!data) { + data = m_data; + } + + if (data->prismCache(0) == 0) { + return false; + } + + m_kclScale = scale; + + data->lookupSphereCached(pos / scale, prevPos / scale, typeMask, radius / scale); + + if (colInfo) { + return doCheckWithPartialInfoPush(data, &KColData::checkSphereCollision, colInfo, + typeMaskOut); + } + + return doCheckMaskOnlyPush(data, &KColData::checkSphereCollision, typeMaskOut); +} + +bool CourseColMgr::checkSphereCachedFullPush(KColData *data, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 scale, f32 radius) { + if (!data) { + data = m_data; + } + + if (data->prismCache(0) == 0) { + return false; + } + + m_kclScale = scale; + + data->lookupSphereCached(pos / scale, prevPos / scale, typeMask, radius / scale); + + if (colInfo) { + return doCheckWithFullInfoPush(data, &KColData::checkSphereCollision, colInfo, typeMaskOut); + } else { + // Not needed currently + return false; + } } void *CourseColMgr::LoadFile(const char *filename) { @@ -62,26 +117,43 @@ CourseColMgr::~CourseColMgr() { delete m_data; } -bool CourseColMgr::doCheckWithFullInfoPush(KColData *colMgr, CollisionCheckFunc collisionCheckFunc, - CollisionInfo *colInfo, u32 *flagsOut) { +bool CourseColMgr::doCheckWithPartialInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + CollisionInfo *colInfo, KCLTypeMask *typeMask) { f32 dist; EGG::Vector3f fnrm; u16 attribute; bool hasCol = false; - while ((colMgr->*collisionCheckFunc)(&dist, &fnrm, &attribute)) { + while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) { dist *= m_kclScale; + EGG::Vector3f offset; - /*if (m_noBounceWallInfo && (attribute & KCL_SOFT_WALL_MASK)) { - if (m_localMtx) { - nw4r::math::VEC3TransformNormal(&fnrm, m_localMtx, &fnrm); - } - + u32 flags = KCL_ATTRIBUTE_TYPE_BIT(attribute); + if (typeMask) { + CollisionDirector::Instance()->pushCollisionEntry(dist, typeMask, flags, attribute); + } + if ((flags & KCL_TYPE_SOLID_SURFACE) != 0) { offset = fnrm * dist; - softWallColInfo->update(dist, offset, fnrm); + colInfo->bbox.min = colInfo->bbox.min.minimize(offset); + colInfo->bbox.max = colInfo->bbox.max.maximize(offset); + } + hasCol = true; + } + + m_localMtx = nullptr; + + return hasCol; +} + +bool CourseColMgr::doCheckWithFullInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + CollisionInfo *colInfo, KCLTypeMask *flagsOut) { + f32 dist; + EGG::Vector3f fnrm; + u16 attribute; + bool hasCol = false; - hasCol = true; - } else {*/ + while ((data->*collisionCheckFunc)(&dist, &fnrm, &attribute)) { + dist *= m_kclScale; u32 kclAttributeTypeBit = KCL_ATTRIBUTE_TYPE_BIT(attribute); if (flagsOut) { CollisionDirector::Instance()->pushCollisionEntry(dist, flagsOut, kclAttributeTypeBit, @@ -91,38 +163,57 @@ bool CourseColMgr::doCheckWithFullInfoPush(KColData *colMgr, CollisionCheckFunc colInfo->update(dist, fnrm * dist, fnrm, kclAttributeTypeBit); } hasCol = true; - /*}*/ } + m_localMtx = nullptr; + + return hasCol; +} + +bool CourseColMgr::doCheckMaskOnlyPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + KCLTypeMask *typeMaskOut) { + bool hasCol = false; + f32 dist; + u16 attribute; + + while ((data->*collisionCheckFunc)(&dist, nullptr, &attribute)) { + KCLTypeMask mask = KCL_ATTRIBUTE_TYPE_BIT(attribute); + + if (typeMaskOut) { + CollisionDirector::Instance()->pushCollisionEntry(dist, typeMaskOut, mask, attribute); + } + hasCol = true; + } + return hasCol; } void CourseColMgr::CollisionInfo::updateFloor(f32 dist, const EGG::Vector3f &fnrm) { - if (dist > m_floorDist) { - m_floorDist = dist; - m_floorNrm = fnrm; + if (dist > floorDist) { + floorDist = dist; + floorNrm = fnrm; } } void CourseColMgr::CollisionInfo::updateWall(f32 dist, const EGG::Vector3f &fnrm) { - if (dist > m_wallDist) { - m_wallDist = dist; - m_wallNrm = fnrm; + if (dist > wallDist) { + wallDist = dist; + wallNrm = fnrm; } } void CourseColMgr::CollisionInfo::update(f32 now_dist, const EGG::Vector3f &offset, const EGG::Vector3f &fnrm, u32 kclAttributeTypeBit) { - m_bbox.mMin = m_bbox.mMin.minimize(offset); - m_bbox.mMax = m_bbox.mMax.maximize(offset); + bbox.min = bbox.min.minimize(offset); + bbox.max = bbox.max.maximize(offset); if (kclAttributeTypeBit & KCL_TYPE_FLOOR) { updateFloor(now_dist, fnrm); } else if (kclAttributeTypeBit & KCL_TYPE_WALL) { - if (m_wallDist > -std::numeric_limits::min()) { - f32 perpendicularity = 1.0f - m_wallNrm.ps_dot(fnrm); - if (perpendicularity > m_perpendicularity) { - m_perpendicularity = std::min(perpendicularity, 1.0f); + if (wallDist > -std::numeric_limits::min()) { + f32 dot = 1.0f - wallNrm.ps_dot(fnrm); + if (dot > perpendicularity) { + perpendicularity = std::min(dot, 1.0f); } } diff --git a/source/game/field/CourseColMgr.hh b/source/game/field/CourseColMgr.hh index 7b6078ed..13915b1b 100644 --- a/source/game/field/CourseColMgr.hh +++ b/source/game/field/CourseColMgr.hh @@ -15,16 +15,15 @@ typedef bool ( class CourseColMgr { public: struct CollisionInfo { - EGG::BoundBox3f m_bbox; - EGG::Vector3f m_minPlusMax; - EGG::Vector3f m_floorNrm; - EGG::Vector3f m_wallNrm; + EGG::BoundBox3f bbox; + EGG::Vector3f tangentOff; + EGG::Vector3f floorNrm; + EGG::Vector3f wallNrm; EGG::Vector3f _3c; - f32 m_floorDist; - f32 m_wallDist; + f32 floorDist; + f32 wallDist; f32 _50; - f32 m_perpendicularity; - void *astruct_7; // TODO + f32 perpendicularity; void updateFloor(f32 dist, const EGG::Vector3f &fnrm); void updateWall(f32 dist, const EGG::Vector3f &fnrm); @@ -33,20 +32,30 @@ public: }; struct NoBounceWallColInfo { - EGG::Vector3f m_bboxLow; - EGG::Vector3f m_bboxHigh; - EGG::Vector3f m_lowPlusHigh; // bboxLow + bboxHigh, see 0x805998c0 - f32 m_dist; - EGG::Vector3f m_fnrm; + EGG::Vector3f bboxLow; + EGG::Vector3f bboxHigh; + EGG::Vector3f lowPlusHigh; // bboxLow + bboxHigh, see 0x805998c0 + f32 dist; + EGG::Vector3f fnrm; }; static_assert(sizeof(NoBounceWallColInfo) == 0x34); void init(); + void scaledNarrowScopeLocal(f32 scale, f32 radius, KColData *data, const EGG::Vector3f &pos, + KCLTypeMask mask); + bool checkSphereFullPush(f32 scalar, f32 radius, KColData *data, const EGG::Vector3f &v0, const EGG::Vector3f &v1, KCLTypeMask flags, CollisionInfo *info, KCLTypeMask *kcl_flags_out); + bool checkSphereCachedPartialPush(KColData *data, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 scale, f32 radius); + bool checkSphereCachedFullPush(KColData *data, const EGG::Vector3f &pos, + const EGG::Vector3f &prevPos, KCLTypeMask typeMask, CollisionInfo *colInfo, + KCLTypeMask *typeMaskOut, f32 scale, f32 radius); + static void *LoadFile(const char *filename); static CourseColMgr *CreateInstance(); @@ -57,8 +66,12 @@ private: CourseColMgr(); ~CourseColMgr(); - bool doCheckWithFullInfoPush(KColData *colMgr, CollisionCheckFunc collisionCheckFunc, - CollisionInfo *colInfo, u32 *flagsOut); + bool doCheckWithPartialInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + CollisionInfo *colInfo, KCLTypeMask *typeMask); + bool doCheckWithFullInfoPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + CollisionInfo *colInfo, KCLTypeMask *flagsOut); + bool doCheckMaskOnlyPush(KColData *data, CollisionCheckFunc collisionCheckFunc, + KCLTypeMask *typeMaskOut); KColData *m_data; f32 m_kclScale; diff --git a/source/game/field/KColData.cc b/source/game/field/KColData.cc index 2bf70652..f3b2a89f 100644 --- a/source/game/field/KColData.cc +++ b/source/game/field/KColData.cc @@ -1,5 +1,6 @@ #include "KColData.hh" +#include #include #include @@ -53,9 +54,37 @@ KColData::KColData(const void *file) { computeBBox(); } +void KColData::narrowScopeLocal(const EGG::Vector3f &pos, f32 radius, KCLTypeMask mask) { + m_prismCacheTop = m_prismCache.data(); + m_pos = pos; + m_radius = radius; + m_typeMask = mask; + m_cachedPos = pos; + m_cachedRadius = radius; + + if (radius <= m_sphereRadius) { + narrowPolygon_EachBlock(searchBlock(pos)); + } + + *m_prismCacheTop = 0; +} + +void KColData::narrowPolygon_EachBlock(const u16 *prismArray) { + m_prismIter = prismArray; + + while (checkSphereSingle(nullptr, nullptr, nullptr)) { + *(m_prismCacheTop++) = parse(*m_prismIter); + + if (m_prismCacheTop == m_prismCache.end()) { + --m_prismCacheTop; + return; + } + } +} + void KColData::computeBBox() { - m_bbox.mMax.set(-999999.0f); - m_bbox.mMin.set(999999.0f); + m_bbox.max.set(-999999.0f); + m_bbox.min.set(999999.0f); for (size_t i = 0; i < m_prismCount; i++) { const KCollisionPrism prism = getPrism(i); @@ -69,22 +98,66 @@ void KColData::computeBBox() { const EGG::Vector3f vtx2 = GetVertex(prism.height, vtx1, fnrm, enrm3, enrm1); const EGG::Vector3f vtx3 = GetVertex(prism.height, vtx1, fnrm, enrm3, enrm2); - m_bbox.mMin = m_bbox.mMin.minimize(vtx1); - m_bbox.mMin = m_bbox.mMin.minimize(vtx2); - m_bbox.mMin = m_bbox.mMin.minimize(vtx3); - m_bbox.mMax = m_bbox.mMax.maximize(vtx1); - m_bbox.mMax = m_bbox.mMax.maximize(vtx2); - m_bbox.mMax = m_bbox.mMax.maximize(vtx3); + m_bbox.min = m_bbox.min.minimize(vtx1); + m_bbox.min = m_bbox.min.minimize(vtx2); + m_bbox.min = m_bbox.min.minimize(vtx3); + m_bbox.max = m_bbox.max.maximize(vtx1); + m_bbox.max = m_bbox.max.maximize(vtx2); + m_bbox.max = m_bbox.max.maximize(vtx3); } } -EGG::Vector3f KColData::GetVertex(f32 height, const EGG::Vector3f &vertex1, - const EGG::Vector3f &fnrm, const EGG::Vector3f &enrm3, const EGG::Vector3f &enrm) { - EGG::Vector3f cross = fnrm.cross(enrm); - f32 dp = cross.ps_dot(enrm3); - cross *= (height / dp); +bool KColData::checkSphereCollision(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) { + return std::isfinite(m_prevPos.y) ? checkSphereMovement(distOut, fnrmOut, flagsOut) : + checkSphere(distOut, fnrmOut, flagsOut); +} - return cross + vertex1; +bool KColData::checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) { + // If there's no list of triangles to check, there's no collision + if (!m_prismIter) { + return false; + } + + // Check collision for all triangles, and continuously call the function until we're out + while (*++m_prismIter != 0) { + const KCollisionPrism prism = getPrism(parse(*m_prismIter)); + if (checkSphereTriCollision(prism, distOut, fnrmOut, flagsOut)) { + return true; + } + } + + // We're out of triangles to check - another list must be prepared for subsequent calls + m_prismIter = nullptr; + return false; +} + +bool KColData::checkSphereSingle(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) { + if (!m_prismIter) { + return false; + } + + while (*++m_prismIter != 0) { + if (m_prismCacheTop != m_prismCache.begin()) { + u16 *puVar10 = m_prismCacheTop - 1; + while (*m_prismIter != *puVar10) { + if (puVar10-- < m_prismCache.begin()) { + break; + } + } + + if (puVar10 >= m_prismCache.begin()) { + continue; + } + } + + const KCollisionPrism prism = getPrism(parse(*m_prismIter)); + if (checkSphereTriCollision(prism, distOut, fnrmOut, flagsOut)) { + return true; + } + } + + m_prismIter = nullptr; + return false; } void KColData::lookupSphere(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, @@ -97,6 +170,25 @@ void KColData::lookupSphere(f32 radius, const EGG::Vector3f &pos, const EGG::Vec m_typeMask = typeMask; } +void KColData::lookupSphereCached(const EGG::Vector3f &p1, const EGG::Vector3f &p2, u32 typeMask, + f32 radius) { + EGG::Sphere3f sphere1(p1, radius); + EGG::Sphere3f sphere2(m_cachedPos, m_cachedRadius); + + if (sphere1.isInsideOtherSphere(sphere2)) { + m_prismIter = searchBlock(p1); + m_radius = std::min(m_sphereRadius, radius); + } else { + m_radius = radius; + m_prismIter = m_cachedPrismArray; + } + + m_pos = p1; + m_prevPos = p2; + m_movement = p1 - p2; + m_typeMask = typeMask; +} + const u16 *KColData::searchBlock(const EGG::Vector3f &point) { // Calculate the x, y, and z offsets of the point from the minimum // corner of the tree's bounding box. @@ -146,11 +238,53 @@ const u16 *KColData::searchBlock(const EGG::Vector3f &point) { return reinterpret_cast(curBlock + (offset & ~0x80000000)); } -/* 807c2410 - checkCollision */ -bool KColData::checkSphereCollision(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) { - return std::isfinite(m_prevPos.y) ? - false /* checkSphereMovement(distOut, fnrmOut, flagsOut) */ : - checkSphere(distOut, fnrmOut, flagsOut); +EGG::Vector3f KColData::getPos(u16 posIdx) const { + const EGG::Vector3f *vec = &reinterpret_cast(m_posData)[posIdx]; + u8 *unsafeData = reinterpret_cast(const_cast(vec)); + EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(EGG::Vector3f)); + EGG::Vector3f pos; + pos.read(stream); + return pos; +} + +EGG::Vector3f KColData::getNrm(u16 nrmIdx) const { + const EGG::Vector3f *vec = &reinterpret_cast(m_nrmData)[nrmIdx]; + u8 *unsafeData = reinterpret_cast(const_cast(vec)); + EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(EGG::Vector3f)); + EGG::Vector3f nrm; + nrm.read(stream); + return nrm; +} + +KColData::KCollisionPrism KColData::getPrism(u16 prismIdx) const { + const KCollisionPrism *prism = + &reinterpret_cast(m_prismData)[prismIdx]; + u8 *unsafeData = reinterpret_cast(const_cast(prism)); + EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(KCollisionPrism)); + + f32 height = stream.read_f32(); + u16 posIndex = stream.read_u16(); + u16 faceNormIndex = stream.read_u16(); + u16 edge1NormIndex = stream.read_u16(); + u16 edge2NormIndex = stream.read_u16(); + u16 edge3NormIndex = stream.read_u16(); + u16 attribute = stream.read_u16(); + + return KCollisionPrism(height, posIndex, faceNormIndex, edge1NormIndex, edge2NormIndex, + edge3NormIndex, attribute); +} + +EGG::Vector3f KColData::GetVertex(f32 height, const EGG::Vector3f &vertex1, + const EGG::Vector3f &fnrm, const EGG::Vector3f &enrm3, const EGG::Vector3f &enrm) { + EGG::Vector3f cross = fnrm.cross(enrm); + f32 dp = cross.ps_dot(enrm3); + cross *= (height / dp); + + return cross + vertex1; +} + +u16 KColData::prismCache(u32 idx) const { + return m_prismCache[idx]; } // Returns whether or not our sphere is colliding with the triangle @@ -176,29 +310,29 @@ bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOu return false; } - const EGG::Vector3f vert = m_pos - getPos(prism.pos_i); + 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 = vert.ps_dot(enrm1); + 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 = vert.ps_dot(enrm2); + 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 = vert.ps_dot(enrm3) - prism.height; + 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 = vert.ps_dot(fnrm); + 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; @@ -288,7 +422,143 @@ bool KColData::checkSphereTriCollision(const KCollisionPrism &prism, f32 *distOu return out(dist); } -bool KColData::checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) { +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) { + 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); +} + +bool KColData::checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut) { // If there's no list of triangles to check, there's no collision if (!m_prismIter) { return false; @@ -297,7 +567,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(*m_prismIter)); - if (checkSphereTriCollision(prism, distOut, fnrmOut, flagsOut)) { + if (checkSphereMovementCollision(prism, distOut, fnrmOut, attributeOut)) { return true; } } @@ -307,42 +577,6 @@ bool KColData::checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut) return false; } -EGG::Vector3f KColData::getPos(u16 posIdx) const { - const EGG::Vector3f *vec = &reinterpret_cast(m_posData)[posIdx]; - u8 *unsafeData = reinterpret_cast(const_cast(vec)); - EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(EGG::Vector3f)); - EGG::Vector3f pos; - pos.read(stream); - return pos; -} - -EGG::Vector3f KColData::getNrm(u16 nrmIdx) const { - const EGG::Vector3f *vec = &reinterpret_cast(m_nrmData)[nrmIdx]; - u8 *unsafeData = reinterpret_cast(const_cast(vec)); - EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(EGG::Vector3f)); - EGG::Vector3f nrm; - nrm.read(stream); - return nrm; -} - -KColData::KCollisionPrism KColData::getPrism(u16 prismIdx) const { - const KCollisionPrism *prism = - &reinterpret_cast(m_prismData)[prismIdx]; - u8 *unsafeData = reinterpret_cast(const_cast(prism)); - EGG::RamStream stream = EGG::RamStream(unsafeData, sizeof(KCollisionPrism)); - - f32 height = stream.read_f32(); - u16 posIndex = stream.read_u16(); - u16 faceNormIndex = stream.read_u16(); - u16 edge1NormIndex = stream.read_u16(); - u16 edge2NormIndex = stream.read_u16(); - u16 edge3NormIndex = stream.read_u16(); - u16 attribute = stream.read_u16(); - - return KCollisionPrism(height, posIndex, faceNormIndex, edge1NormIndex, edge2NormIndex, - edge3NormIndex, attribute); -} - KColData::KCollisionPrism::KCollisionPrism() = default; KColData::KCollisionPrism::KCollisionPrism(f32 height, u16 posIndex, u16 faceNormIndex, diff --git a/source/game/field/KColData.hh b/source/game/field/KColData.hh index 516d3962..c9127d91 100644 --- a/source/game/field/KColData.hh +++ b/source/game/field/KColData.hh @@ -29,13 +29,20 @@ public: KColData(); KColData(const void *file); + void narrowScopeLocal(const EGG::Vector3f &pos, f32 radius, KCLTypeMask mask); + void narrowPolygon_EachBlock(const u16 *prismArray); + void computeBBox(); bool checkSphereCollision(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut); bool checkSphere(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut); + bool checkSphereSingle(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *flagsOut); + void lookupSphere(f32 radius, const EGG::Vector3f &pos, const EGG::Vector3f &prevPos, KCLTypeMask typeMask); + void lookupSphereCached(const EGG::Vector3f &p1, const EGG::Vector3f &p2, u32 typeMask, + f32 radius); + const u16 *searchBlock(const EGG::Vector3f &pos); - /*bool checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut);*/ EGG::Vector3f getPos(u16 posIdx) const; EGG::Vector3f getNrm(u16 nrmIdx) const; @@ -44,9 +51,14 @@ public: static EGG::Vector3f GetVertex(f32 height, const EGG::Vector3f &vertex1, const EGG::Vector3f &fnrm, const EGG::Vector3f &enrm3, const EGG::Vector3f &enrm); + 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 checkSphereMovement(f32 *distOut, EGG::Vector3f *fnrmOut, u16 *attributeOut); const void *m_posData; const void *m_nrmData; diff --git a/source/game/field/KCollisionTypes.hh b/source/game/field/KCollisionTypes.hh index 946d9bde..2ef45e78 100644 --- a/source/game/field/KCollisionTypes.hh +++ b/source/game/field/KCollisionTypes.hh @@ -54,11 +54,13 @@ typedef enum { #define KCL_ANY 0xffffffff #define KCL_NONE 0x00000000 +/*05070000*/ #define KCL_TYPE_DIRECTIONAL \ (KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY) | KCL_TYPE_BIT(COL_TYPE_SOUND_TRIGGER) | \ KCL_TYPE_BIT(COL_TYPE_FORCE_RECALCULATE_ROUTE) | \ KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER) | KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER)) +/*F0F8FFFF*/ #define KCL_TYPE_SOLID_SURFACE \ (KCL_ANY & \ (~KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY) & ~KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER) & \ @@ -67,6 +69,7 @@ typedef enum { ~KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER) & \ ~KCL_TYPE_BIT(COL_TYPE_ITEM_STATE_MODIFIER))) +/*20E80FFF*/ #define KCL_TYPE_FLOOR \ (KCL_TYPE_BIT(COL_TYPE_ROAD) | KCL_TYPE_BIT(COL_TYPE_SLIPPERY_ROAD) | \ KCL_TYPE_BIT(COL_TYPE_WEAK_OFF_ROAD) | KCL_TYPE_BIT(COL_TYPE_OFF_ROAD) | \ @@ -78,14 +81,31 @@ typedef enum { KCL_TYPE_BIT(COL_TYPE_STICKY_ROAD) | KCL_TYPE_BIT(COL_TYPE_ROAD2) | \ KCL_TYPE_BIT(COL_TYPE_ROTATING_ROAD)) +/*D010F000*/ #define KCL_TYPE_WALL \ (KCL_TYPE_BIT(COL_TYPE_WALL) | KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL) | \ KCL_TYPE_BIT(COL_TYPE_ITEM_WALL) | KCL_TYPE_BIT(COL_TYPE_WALL_2) | \ KCL_TYPE_BIT(COL_TYPE_PLAYER_WALL) | KCL_TYPE_BIT(COL_TYPE_HALFPIPE_INVISIBLE_WALL) | \ KCL_TYPE_BIT(COL_TYPE_SPECIAL_WALL) | KCL_TYPE_BIT(COL_TYPE_INVISIBLE_WALL2)) -namespace Field { +/*EFFFBDFF*/ +#define KCL_TYPE_VEHICLE_INTERACTABLE \ + (~KCL_TYPE_BIT(COL_TYPE_ITEM_ROAD) & ~KCL_TYPE_BIT(COL_TYPE_ITEM_WALL) & \ + ~KCL_TYPE_BIT(COL_TYPE_HALFPIPE_INVISIBLE_WALL)) + +/*EAF8BDFF*/ +#define KCL_TYPE_VEHICLE_COLLIDEABLE \ + (KCL_TYPE_VEHICLE_INTERACTABLE & ~KCL_TYPE_BIT(COL_TYPE_SOUND_TRIGGER) & \ + ~KCL_TYPE_BIT(COL_TYPE_EFFECT_TRIGGER) & ~KCL_TYPE_BIT(COL_TYPE_FALL_BOUNDARY) & \ + ~KCL_TYPE_BIT(COL_TYPE_CANNON_TRIGGER) & \ + ~KCL_TYPE_BIT(COL_TYPE_FORCE_RECALCULATE_ROUTE)) +/*E0F8BDFF*/ +#define KCL_TYPE_NON_DIRECTIONAL \ + (KCL_TYPE_VEHICLE_COLLIDEABLE & ~KCL_TYPE_BIT(COL_TYPE_ITEM_STATE_MODIFIER) & \ + ~KCL_TYPE_BIT(COL_TYPE_WEAK_WALL)) + +namespace Field { struct KColHeader { u32 pos_data_offset; u32 nrm_data_offset; diff --git a/source/game/kart/CollisionGroup.cc b/source/game/kart/CollisionGroup.cc new file mode 100644 index 00000000..c135c4e8 --- /dev/null +++ b/source/game/kart/CollisionGroup.cc @@ -0,0 +1,200 @@ +#include "CollisionGroup.hh" + +namespace Kart { + +void CollisionData::reset() { + tangentOff.setZero(); + floorNrm.setZero(); + vel.setZero(); + relPos.setZero(); + movement.setZero(); + speedFactor = 1.0f; + rotFactor = 0.0f; + closestFloorFlags = 0; + closestFloorSettings = 0xffffffff; + intensity = 0.0f; + + floor = false; +} + +Hitbox::Hitbox() : m_bspHitbox(nullptr) {} + +Hitbox::~Hitbox() = default; + +void Hitbox::calc(f32 param_1, f32 totalScale, const EGG::Vector3f &scale, const EGG::Quatf &rot, + const EGG::Vector3f &pos) { + f32 fVar1 = 0.0f; + if (scale.y < param_1) { + fVar1 = (param_1 - scale.y) * m_bspHitbox->radius; + } + + EGG::Vector3f scaledPos = m_bspHitbox->position * scale; + scaledPos.y = (m_bspHitbox->position.y + totalScale) * scale.y + fVar1; + + m_relPos = rot.rotateVector(scaledPos); + m_worldPos = m_relPos + pos; +} + +void Hitbox::reset() { + m_worldPos.setZero(); + m_lastPos.setZero(); + m_relPos.setZero(); +} + +void Hitbox::setRadius(f32 radius) { + m_radius = radius; +} + +void Hitbox::setBspHitbox(const BSP::Hitbox *hitbox) { + m_bspHitbox = hitbox; +} + +void Hitbox::setWorldPos(const EGG::Vector3f &pos) { + m_worldPos = pos; +} + +void Hitbox::setLastPos(const EGG::Vector3f &pos) { + m_lastPos = pos; +} + +void Hitbox::setLastPos(const EGG::Vector3f &scale, const EGG::Matrix34f &pose) { + f32 yScaleFactor = scale.y; + EGG::Vector3f scaledPos = m_bspHitbox->position; + scaledPos.x *= scale.x; + scaledPos.z *= scale.z; + + if (scale.y != scale.z && scale.y < 1.0f) { + scaledPos.y += (1.0f - scale.y) * m_radius; + yScaleFactor = scale.z; + } + + scaledPos.y *= yScaleFactor; + m_lastPos = pose.ps_multVector(scaledPos); +} + +const BSP::Hitbox *Hitbox::bspHitbox() const { + return m_bspHitbox; +} + +EGG::Vector3f Hitbox::worldPos() const { + return m_worldPos; +} + +EGG::Vector3f Hitbox::lastPos() const { + return m_lastPos; +} + +f32 Hitbox::radius() const { + return m_radius; +} + +CollisionGroup::CollisionGroup() : m_hitboxes(nullptr), m_hitboxScale(1.0f) { + m_collisionData.reset(); +} + +CollisionGroup::~CollisionGroup() { + delete[] m_hitboxes->data(); + delete m_hitboxes; +} + +f32 CollisionGroup::initHitboxes(const std::array &hitboxes) { + u16 bspHitboxCount = 0; + + for (const auto &hitbox : hitboxes) { + if (parse(hitbox.enable)) { + ++bspHitboxCount; + } + } + + m_hitboxes = new std::span(new Hitbox[bspHitboxCount], bspHitboxCount); + + for (u16 hitboxIdx = 0; hitboxIdx < m_hitboxes->size(); ++hitboxIdx) { + (*m_hitboxes)[hitboxIdx].setBspHitbox(&hitboxes[hitboxIdx]); + } + + return computeCollisionLimits(); +} + +f32 CollisionGroup::computeCollisionLimits() { + EGG::Vector3f max; + + for (const auto &hitbox : *m_hitboxes) { + const BSP::Hitbox *bspHitbox = hitbox.bspHitbox(); + + if (bspHitbox->enable == 0) { + continue; + } + + max = max.maximize(bspHitbox->position.abs() + bspHitbox->radius); + } + + // Get largest component of the vector + f32 maxComponent = max.z; + + if (max.x <= max.y) { + if (max.z < max.y) { + maxComponent = max.y; + } + } else if (max.z < max.x) { + maxComponent = max.x; + } + + m_boundingRadius = maxComponent; + + return max.z * 0.5f; +} + +void CollisionGroup::createSingleHitbox(f32 radius, const EGG::Vector3f &relPos) { + m_hitboxes = new std::span(new Hitbox, 1); + + // TODO: Do we need for loop if this is just one? + // And how exactly will we identify to free the BSP::Hitbox on destruction? + for (auto &hitbox : *m_hitboxes) { + hitbox.reset(); + BSP::Hitbox *bspHitbox = new BSP::Hitbox; + hitbox.setBspHitbox(bspHitbox); + bspHitbox->position = relPos; + bspHitbox->radius = radius; + hitbox.setRadius(radius); + } + m_boundingRadius = radius; +} + +void CollisionGroup::reset() { + m_collisionData.reset(); + + for (auto &hitbox : *m_hitboxes) { + hitbox.reset(); + hitbox.setRadius(hitbox.bspHitbox()->radius * m_hitboxScale); + } +} + +void CollisionGroup::resetCollision() { + m_collisionData.reset(); +} + +Hitbox &CollisionGroup::hitbox(u16 hitboxIdx) { + return (*m_hitboxes)[hitboxIdx]; +} + +u16 CollisionGroup::hitboxCount() const { + return m_hitboxes->size(); +} + +CollisionData &CollisionGroup::collisionData() { + return m_collisionData; +} + +const CollisionData &CollisionGroup::collisionData() const { + return m_collisionData; +} + +void CollisionGroup::setHitboxScale(f32 scale) { + m_hitboxScale = scale; + + for (auto &hitbox : *m_hitboxes) { + hitbox.setRadius(hitbox.bspHitbox()->radius * m_hitboxScale); + } +} + +} // namespace Kart diff --git a/source/game/kart/CollisionGroup.hh b/source/game/kart/CollisionGroup.hh new file mode 100644 index 00000000..a92b796c --- /dev/null +++ b/source/game/kart/CollisionGroup.hh @@ -0,0 +1,86 @@ +#pragma once + +#include "game/kart/KartParam.hh" + +#include "game/field/KCollisionTypes.hh" + +#include + +#include +#include + +namespace Kart { + +struct CollisionData { + void reset(); + + EGG::Vector3f tangentOff; + EGG::Vector3f floorNrm; + EGG::Vector3f vel; + EGG::Vector3f relPos; + EGG::Vector3f movement; + f32 speedFactor; + f32 rotFactor; + Field::KCLTypeMask closestFloorFlags; + u32 closestFloorSettings; + s32 intensity; + + bool floor; +}; + +class Hitbox { +public: + Hitbox(); + ~Hitbox(); + + void calc(f32 param_1, f32 totalScale, const EGG::Vector3f &scale, const EGG::Quatf &rot, + const EGG::Vector3f &pos); + + void reset(); + void setScale(f32 scale); + void setRadius(f32 radius); + void setBspHitbox(const BSP::Hitbox *hitbox); + void setWorldPos(const EGG::Vector3f &pos); + void setLastPos(const EGG::Vector3f &pos); + void setLastPos(const EGG::Vector3f &scale, const EGG::Matrix34f &pose); + + const BSP::Hitbox *bspHitbox() const; + EGG::Vector3f worldPos() const; + EGG::Vector3f lastPos() const; + f32 radius() const; + +private: + const BSP::Hitbox *m_bspHitbox; + f32 m_radius; + EGG::Vector3f m_worldPos; + EGG::Vector3f m_lastPos; + EGG::Vector3f m_relPos; +}; + +class CollisionGroup { +public: + CollisionGroup(); + ~CollisionGroup(); + + f32 initHitboxes(const std::array &hitboxes); + f32 computeCollisionLimits(); + void createSingleHitbox(f32 radius, const EGG::Vector3f &relPos); + + void reset(); + void resetCollision(); + + Hitbox &hitbox(u16 hitboxIdx); + u16 hitboxCount() const; + CollisionData &collisionData(); + const CollisionData &collisionData() const; + + void setHitboxScale(f32 scale); + +private: + f32 m_boundingRadius; + CollisionData m_collisionData; + std::span *m_hitboxes; + f32 m_hitboxScale; +}; + +} // namespace Kart diff --git a/source/game/kart/KartBody.cc b/source/game/kart/KartBody.cc index 42ac7b13..97c0c328 100644 --- a/source/game/kart/KartBody.cc +++ b/source/game/kart/KartBody.cc @@ -1,14 +1,21 @@ #include "KartBody.hh" +#include + namespace Kart { KartBody::KartBody(KartPhysics *physics) : m_physics(physics) {} +// TODO LATER +EGG::Matrix34f KartBody::wheelMatrix(u16) { + return EGG::Matrix34f::ident; +} + void KartBody::reset() { m_physics->reset(); } -KartPhysics *KartBody::getPhysics() const { +KartPhysics *KartBody::physics() const { return m_physics; } @@ -16,4 +23,27 @@ KartBodyKart::KartBodyKart(KartPhysics *physics) : KartBody(physics) {} KartBodyBike::KartBodyBike(KartPhysics *physics) : KartBody(physics) {} +EGG::Matrix34f KartBodyBike::wheelMatrix(u16 wheelIdx) { + EGG::Matrix34f mat; + + mat.makeQT(fullRot(), pos()); + if (wheelIdx != 0) { + return mat; + } + + EGG::Vector3f position = param()->bikeDisp().m_handlePos * scale(); + EGG::Vector3f rotation = param()->bikeDisp().m_handleRot * DEG2RAD; + + EGG::Matrix34f handleMatrix; + handleMatrix.makeRT(rotation, position); + EGG::Matrix34f tmp = mat.multiplyTo(handleMatrix); + + EGG::Vector3f yRotation; + EGG::Matrix34f yRotMatrix; + yRotMatrix.makeR(yRotation); + mat = tmp.multiplyTo(yRotMatrix); + + return mat; +} + } // namespace Kart diff --git a/source/game/kart/KartBody.hh b/source/game/kart/KartBody.hh index 2ed648b5..95358538 100644 --- a/source/game/kart/KartBody.hh +++ b/source/game/kart/KartBody.hh @@ -10,9 +10,11 @@ public: KartBody(KartPhysics *physics); virtual ~KartBody() {} + virtual EGG::Matrix34f wheelMatrix(u16); + void reset(); - KartPhysics *getPhysics() const; + KartPhysics *physics() const; protected: KartPhysics *m_physics; @@ -26,6 +28,8 @@ public: class KartBodyBike : public KartBody { public: KartBodyBike(KartPhysics *physics); + + EGG::Matrix34f wheelMatrix(u16 wheelIdx) override; }; } // namespace Kart diff --git a/source/game/kart/KartCollide.cc b/source/game/kart/KartCollide.cc new file mode 100644 index 00000000..70f4a603 --- /dev/null +++ b/source/game/kart/KartCollide.cc @@ -0,0 +1,277 @@ +#include "KartCollide.hh" + +#include "game/kart/KartMove.hh" +#include "game/kart/KartPhysics.hh" + +#include "game/field/CollisionDirector.hh" + +#include + +namespace Kart { + +KartCollide::KartCollide() + : m_offRoad(false), m_groundBoostPanelOrRamp(false), m_notTrickable(false) {} + +KartCollide::~KartCollide() = default; + +void KartCollide::init() { + m_smoothedBack = 0.0f; + m_offRoad = false; + m_groundBoostPanelOrRamp = false; + m_notTrickable = false; +} + +void KartCollide::resetHitboxes() { + CollisionGroup *hitboxGroup = physics()->hitboxGroup(); + for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) { + hitboxGroup->hitbox(idx).setLastPos(scale(), pose()); + } +} + +void KartCollide::calcHitboxes() { + CollisionGroup *hitboxGroup = physics()->hitboxGroup(); + for (u16 idx = 0; idx < hitboxGroup->hitboxCount(); ++idx) { + hitboxGroup->hitbox(idx).calc(move()->totalScale(), 0.0f, scale(), fullRot(), pos()); + } +} + +void KartCollide::findCollision() { + calcBodyCollision(move()->totalScale(), fullRot(), scale()); +} + +void KartCollide::calcBodyCollision(f32 totalScale, const EGG::Quatf &rot, + const EGG::Vector3f &scale) { + CollisionGroup *hitboxGroup = physics()->hitboxGroup(); + CollisionData &collisionData = hitboxGroup->collisionData(); + collisionData.reset(); + + EGG::Vector3f posRel; + Field::CourseColMgr::CollisionInfo colInfo; + colInfo.bbox.setDirect(EGG::Vector3f::zero, EGG::Vector3f::zero); + Field::KCLTypeMask maskOut; + EGG::BoundBox3f minMax = colInfo.bbox; + + for (u16 hitboxIdx = 0; hitboxIdx < hitboxGroup->hitboxCount(); ++hitboxIdx) { + Field::KCLTypeMask flags = 0xEAFABDFF; + Hitbox &hitbox = hitboxGroup->hitbox(hitboxIdx); + + if (hitbox.bspHitbox()->wallsOnly != 0) { + flags = 0x4A109000; + } + + hitbox.calc(totalScale, 0.0f, scale, rot, pos()); + + Field::CollisionDirector::Instance()->checkSphereCachedFullPush(hitbox.worldPos(), + hitbox.lastPos(), flags, &colInfo, &maskOut, hitbox.radius(), 0); + } + + collisionData.speedFactor = 1.0f; + collisionData.rotFactor = 1.0f; +} + +void KartCollide::calcFloorEffect() { + m_offRoad = true; + m_groundBoostPanelOrRamp = true; + + Field::KCLTypeMask mask = KCL_NONE; + calcTriggers(&mask, pos(), false); + + mask = KCL_NONE; + calcTriggers(&mask, pos(), true); +} + +void KartCollide::calcTriggers(Field::KCLTypeMask *mask, const EGG::Vector3f &pos, bool twoPoint) { + EGG::Vector3f v1 = twoPoint ? physics()->pos() : EGG::Vector3f::inf; + Field::KCLTypeMask typeMask = twoPoint ? KCL_TYPE_DIRECTIONAL : KCL_TYPE_NON_DIRECTIONAL; + f32 fVar1 = twoPoint ? 80.0f : 100.0f * move()->totalScale(); + f32 scalar = -bsp().initialYPos * move()->totalScale() * 0.3f; + EGG::Vector3f scaledPos = pos + scalar * componentYAxis(); + EGG::Vector3f back = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez); + + m_smoothedBack += (back.dot(move()->smoothedUp()) - m_smoothedBack) * 0.3f; + + scalar = m_smoothedBack * -physics()->fc() * 1.8f * move()->totalScale(); + scaledPos += scalar * back; + + bool collide = Field::CollisionDirector::Instance()->checkSphereCachedPartialPush(scaledPos, v1, + typeMask, nullptr, mask, fVar1, 0); + + if (!collide || twoPoint) { + return; + } + + if (*mask & KCL_TYPE_FLOOR) { + Field::CollisionDirector::Instance()->findClosestCollisionEntry(mask, KCL_TYPE_FLOOR); + } +} + +void KartCollide::calcWheelCollision(u16 /*wheelIdx*/, CollisionGroup *hitboxGroup, + const EGG::Vector3f &colVel, const EGG::Vector3f ¢er, f32 radius) { + Hitbox &firstHitbox = hitboxGroup->hitbox(0); + BSP::Hitbox *bspHitbox = const_cast(firstHitbox.bspHitbox()); + bspHitbox->radius = radius; + hitboxGroup->resetCollision(); + firstHitbox.setWorldPos(center); + + Field::CourseColMgr::CollisionInfo colInfo; + colInfo.bbox.setZero(); + Field::KCLTypeMask kclOut; + + bool collided = Field::CollisionDirector::Instance()->checkSphereCachedFullPush( + firstHitbox.worldPos(), firstHitbox.lastPos(), KCL_TYPE_VEHICLE_COLLIDEABLE, &colInfo, + &kclOut, firstHitbox.radius(), 0); + + CollisionData &collisionData = hitboxGroup->collisionData(); + + if (!collided) { + collisionData.speedFactor = 1.0f; + collisionData.rotFactor = 1.0f; + return; + } + + collisionData.tangentOff = colInfo.tangentOff; + + if (kclOut & KCL_TYPE_FLOOR) { + collisionData.floor = true; + collisionData.floorNrm = colInfo.floorNrm; + } + + collisionData.relPos = firstHitbox.worldPos() - pos(); + collisionData.vel = colVel; + + processWheel(collisionData, firstHitbox, &colInfo, &kclOut); + + if (!(kclOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) { + return; + } + + Field::CollisionDirector::Instance()->findClosestCollisionEntry(&kclOut, + KCL_TYPE_VEHICLE_COLLIDEABLE); +} + +void KartCollide::processWheel(CollisionData &collisionData, Hitbox &hitbox, + Field::CourseColMgr::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut) { + processFloor(collisionData, hitbox, colInfo, maskOut, true); +} + +void KartCollide::processFloor(CollisionData &collisionData, Hitbox & /*hitbox*/, + Field::CourseColMgr::CollisionInfo * /*colInfo*/, Field::KCLTypeMask *maskOut, + bool /*wheel*/) { + if (!(*maskOut & KCL_TYPE_VEHICLE_COLLIDEABLE)) { + return; + } + + if (!Field::CollisionDirector::Instance()->findClosestCollisionEntry(maskOut, KCL_TYPE_FLOOR)) { + return; + } + + const Field::CollisionDirector::CollisionEntry *closestColEntry = + Field::CollisionDirector::Instance()->closestCollisionEntry(); + + if (!(closestColEntry->attribute & 0x2000)) { + m_notTrickable = true; + } + + collisionData.intensity = (closestColEntry->attribute >> 0xb) & 3; + collisionData.closestFloorFlags = closestColEntry->typeMask; + collisionData.closestFloorSettings = (closestColEntry->attribute >> 5) & 7; + + if (!(*maskOut & KCL_TYPE_BIT(COL_TYPE_BOOST_RAMP))) { + m_notTrickable = true; + } +} + +void KartCollide::applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup, + const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed, + bool b1, bool b2, bool b3) { + CollisionData &colData = hitboxGroup->collisionData(); + if (!colData.floor) { + return; + } + + f32 velDotFloorNrm = colData.vel.dot(colData.floorNrm); + + if (velDotFloorNrm >= 0.0f) { + return; + } + + EGG::Matrix34f rotMat; + rotMat.makeQ(dynamics()->mainRot()); + EGG::Matrix34f tmp = rotMat.multiplyTo(dynamics()->invInertiaTensor()); + tmp = tmp.multiplyTo(rotMat); + + EGG::Vector3f crossVec = colData.relPos.cross(colData.floorNrm); + crossVec = tmp.multVector(crossVec); + crossVec = crossVec.cross(colData.relPos); + + f32 scalar = -velDotFloorNrm / (1.0f + colData.floorNrm.dot(crossVec)); + EGG::Vector3f negSpeed = -speed; + crossVec = colData.floorNrm.cross(negSpeed); + crossVec = crossVec.cross(colData.floorNrm); + + if (FLT_EPSILON >= crossVec.dot()) { + return; + } + + crossVec.normalise(); + f32 speedDot = std::min(0.0f, speed.dot(crossVec)); + crossVec *= ((scalar * speedDot) / velDotFloorNrm); + + auto projAndRej = crossVec.projAndRej(forward); + + f32 projNorm = projAndRej.first.length(); + f32 rejNorm = projAndRej.second.length(); + f32 projNorm_ = projNorm; + f32 rejNorm_ = rejNorm; + + f32 dVar7 = down * EGG::Mathf::abs(scalar); + if (dVar7 < EGG::Mathf::abs(projNorm)) { + projNorm_ = dVar7; + if (projNorm < 0.0f) { + projNorm_ = -down * EGG::Mathf::abs(scalar); + } + } + + f32 dVar5 = rate * EGG::Mathf::abs(scalar); + if (dVar5 < EGG::Mathf::abs(rejNorm)) { + rejNorm_ = dVar5; + if (rejNorm < 0.0f) { + rejNorm = -rate * EGG::Mathf::abs(scalar); + } + } + + projAndRej.first.normalise(); + projAndRej.second.normalise(); + + projAndRej.first *= projNorm_; + projAndRej.second *= rejNorm_; + + EGG::Vector3f projRejSum = projAndRej.first + projAndRej.second; + EGG::Vector3f projRejSumOrig = projRejSum; + + if (!b1) { + projRejSum.x = 0.0f; + projRejSum.z = 0.0f; + } + if (!b2) { + projRejSum.y = 0.0f; + } + + projRejSum = projRejSum.rej(nextDir); + + dynamics()->setExtVel(dynamics()->extVel() + projRejSum); + + if (b3) { + EGG::Vector3f rotation = colData.relPos.cross(projRejSumOrig); + EGG::Vector3f rotation2 = dynamics()->mainRot().rotateVectorInv(tmp.multVector(rotation)); + + EGG::Vector3f angVel = rotation2; + angVel.y = 0.0f; + if (!b1) { + angVel.x = 0.0f; + } + dynamics()->setAngVel0(dynamics()->angVel0() + angVel); + } +} + +} // namespace Kart diff --git a/source/game/kart/KartCollide.hh b/source/game/kart/KartCollide.hh new file mode 100644 index 00000000..c9ad0122 --- /dev/null +++ b/source/game/kart/KartCollide.hh @@ -0,0 +1,44 @@ +#pragma once + +#include "game/kart/CollisionGroup.hh" +#include "game/kart/KartObjectProxy.hh" + +#include + +namespace Kart { + +class KartCollide : KartObjectProxy { +public: + KartCollide(); + ~KartCollide(); + + void init(); + void resetHitboxes(); + + void calcHitboxes(); + + void findCollision(); + void calcBodyCollision(f32 totalScale, const EGG::Quatf &rot, const EGG::Vector3f &scale); + void calcFloorEffect(); + void calcTriggers(Field::KCLTypeMask *mask, const EGG::Vector3f &pos, bool twoPoint); + void calcWheelCollision(u16 wheelIdx, CollisionGroup *hitboxGroup, const EGG::Vector3f &colVel, + const EGG::Vector3f ¢er, f32 radius); + + void processWheel(CollisionData &collisionData, Hitbox &hitbox, + Field::CourseColMgr::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut); + void processFloor(CollisionData &collisionData, Hitbox &hitbox, + Field::CourseColMgr::CollisionInfo *colInfo, Field::KCLTypeMask *maskOut, bool wheel); + + void applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitboxGroup, + const EGG::Vector3f &forward, const EGG::Vector3f &nextDir, const EGG::Vector3f &speed, + bool b1, bool b2, bool b3); + +private: + f32 m_smoothedBack; // 0x50 + + bool m_offRoad; + bool m_groundBoostPanelOrRamp; + bool m_notTrickable; +}; + +} // namespace Kart diff --git a/source/game/kart/KartDynamics.cc b/source/game/kart/KartDynamics.cc index a76739fe..faeeab21 100644 --- a/source/game/kart/KartDynamics.cc +++ b/source/game/kart/KartDynamics.cc @@ -1,8 +1,13 @@ #include "KartDynamics.hh" +#include + namespace Kart { KartDynamics::KartDynamics() { + m_angVel0Factor = 1.0f; + m_inertiaTensor = EGG::Matrix34f::ident; + m_invInertiaTensor = EGG::Matrix34f::ident; init(); } @@ -11,12 +16,31 @@ void KartDynamics::init() { m_gravity = -1.0f; m_forceUpright = true; m_noGravity = false; + m_angVel0YFactor = 0.0f; } void KartDynamics::resetInternalVelocity() { m_intVel.setZero(); } +void KartDynamics::setBspParams(f32 rotSpeed, const EGG::Vector3f &m, const EGG::Vector3f &n, + bool skipInertia) { + constexpr f32 TWELFTH = 1.0f / 12.0f; + + m_angVel0Factor = rotSpeed; + + if (skipInertia) { + return; + } + + m_inertiaTensor = EGG::Matrix34f::zero; + m_inertiaTensor(0, 0) = (m.y * m.y + m.z * m.z) * TWELFTH + n.y * n.y + n.z * n.z; + m_inertiaTensor(1, 1) = (m.z * m.z + m.x * m.x) * TWELFTH + n.z * n.z + n.x * n.x; + m_inertiaTensor(2, 2) = (m.x * m.x + m.y * m.y) * TWELFTH + n.x * n.x + n.y * n.y; + + m_invInertiaTensor = m_inertiaTensor.inverseTo(); +} + void KartDynamics::calc(f32 dt, f32 maxSpeed, bool /*air*/) { if (!m_noGravity) { m_totalForce.y += m_gravity; @@ -25,17 +49,73 @@ void KartDynamics::calc(f32 dt, f32 maxSpeed, bool /*air*/) { m_acceleration0 = m_totalForce; m_extVel += m_acceleration0 * dt; m_extVel *= 0.998f; + m_angVel0 *= 0.98f; + + EGG::Vector3f playerBackHoriz = m_mainRot.rotateVector(EGG::Vector3f::ez); + playerBackHoriz.y = 0.0f; + + if (FLT_EPSILON < playerBackHoriz.dot()) { + playerBackHoriz.normalise(); + m_extVel = m_extVel.rej(playerBackHoriz); + } + m_velocity = m_extVel * dt + m_intVel + m_movingObjVel + m_movingRoadVel; m_speedNorm = std::min(m_velocity.normalise(), maxSpeed); m_velocity *= m_speedNorm; m_pos += m_velocity; + EGG::Vector3f t1 = m_invInertiaTensor.multVector(m_totalTorque) * dt; + m_angVel0 += (t1 + m_invInertiaTensor.multVector(t1 + m_totalTorque) * dt) * 0.5f; + + m_angVel0.x = std::min(0.4f, std::max(-0.4f, m_angVel0.x)); + m_angVel0.y = std::min(0.4f, std::max(-0.4f, m_angVel0.y)) * m_angVel0YFactor; + m_angVel0.z = std::min(0.8f, std::max(-0.8f, m_angVel0.z)); + + EGG::Vector3f angVelSum = m_angVel2 + m_angVel1 + m_angVel0Factor * m_angVel0; + + if (FLT_EPSILON < angVelSum.dot()) { + m_mainRot += m_mainRot * angVelSum * dt * 0.5; + + if (EGG::Mathf::abs(m_mainRot.dot()) < FLT_EPSILON) { + m_mainRot = EGG::Quatf::ident; + } else { + m_mainRot.normalise(); + } + } + + // TODO: Can we prove we don't need this given above??? + if (EGG::Mathf::abs(m_mainRot.dot()) < FLT_EPSILON) { + m_mainRot = EGG::Quatf::ident; + } else { + m_mainRot.normalise(); + } + m_fullRot = m_extraRot * m_mainRot * m_specialRot; m_mainRot.normalise(); m_fullRot.normalise(); m_totalForce.setZero(); + m_totalTorque.setZero(); + m_angVel2.setZero(); +} + +void KartDynamics::applySuspensionWrench(const EGG::Vector3f &p, const EGG::Vector3f &Flinear, + const EGG::Vector3f &Frot, bool ignoreX) { + m_totalForce.y += Flinear.y; + EGG::Vector3f fBody = m_fullRot.rotateVectorInv(Frot); + EGG::Vector3f rBody = m_fullRot.rotateVectorInv(p - m_pos); + EGG::Vector3f torque = rBody.cross(fBody); + + if (ignoreX) { + torque.x = 0.0f; + } + torque.y = 0.0f; + m_totalTorque += torque; +} + +const EGG::Matrix34f &KartDynamics::invInertiaTensor() const { + return m_invInertiaTensor; } const EGG::Vector3f &KartDynamics::pos() const { @@ -46,6 +126,14 @@ const EGG::Vector3f &KartDynamics::velocity() const { return m_velocity; } +const EGG::Vector3f &KartDynamics::intVel() const { + return m_intVel; +} + +const EGG::Quatf &KartDynamics::mainRot() const { + return m_mainRot; +} + const EGG::Quatf &KartDynamics::fullRot() const { return m_fullRot; } @@ -54,6 +142,10 @@ const EGG::Vector3f &KartDynamics::extVel() const { return m_extVel; } +const EGG::Vector3f &KartDynamics::angVel0() const { + return m_angVel0; +} + void KartDynamics::setPos(const EGG::Vector3f &pos) { m_pos = pos; } @@ -82,4 +174,12 @@ void KartDynamics::setExtVel(const EGG::Vector3f &v) { m_extVel = v; } +void KartDynamics::setAngVel0(const EGG::Vector3f &v) { + m_angVel0 = v; +} + +void KartDynamics::setAngVel0YFactor(f32 val) { + m_angVel0YFactor = val; +} + } // namespace Kart diff --git a/source/game/kart/KartDynamics.hh b/source/game/kart/KartDynamics.hh index 4ccb05f2..2df00bcc 100644 --- a/source/game/kart/KartDynamics.hh +++ b/source/game/kart/KartDynamics.hh @@ -9,13 +9,21 @@ public: KartDynamics(); void init(); void resetInternalVelocity(); + void setBspParams(f32 rotSpeed, const EGG::Vector3f &m, const EGG::Vector3f &n, bool skipInertia); void calc(f32 dt, f32 maxSpeed, bool air); + void applySuspensionWrench(const EGG::Vector3f &p, const EGG::Vector3f &Flinear, + const EGG::Vector3f &Frot, bool ignoreX); + + const EGG::Matrix34f &invInertiaTensor() const; const EGG::Vector3f &pos() const; const EGG::Vector3f &velocity() const; + const EGG::Vector3f &intVel() const; + const EGG::Quatf &mainRot() const; const EGG::Quatf &fullRot() const; const EGG::Vector3f &extVel() const; + const EGG::Vector3f &angVel0() const; void setPos(const EGG::Vector3f &pos); void setGravity(f32 gravity); @@ -24,23 +32,32 @@ public: void setSpecialRot(const EGG::Quatf &q); void setExtraRot(const EGG::Quatf &q); void setExtVel(const EGG::Vector3f &v); + void setAngVel0(const EGG::Vector3f &v); + void setAngVel0YFactor(f32 val); private: - // These are the only vars needed in setInitialPhysicsValues + EGG::Matrix34f m_inertiaTensor; + EGG::Matrix34f m_invInertiaTensor; + f32 m_angVel0Factor; EGG::Vector3f m_pos; EGG::Vector3f m_extVel; EGG::Vector3f m_acceleration0; + EGG::Vector3f m_angVel0; EGG::Vector3f m_movingObjVel; + EGG::Vector3f m_angVel1; EGG::Vector3f m_movingRoadVel; EGG::Vector3f m_velocity; f32 m_speedNorm; + EGG::Vector3f m_angVel2; EGG::Quatf m_mainRot; EGG::Quatf m_fullRot; EGG::Vector3f m_totalForce; + EGG::Vector3f m_totalTorque; EGG::Quatf m_specialRot; EGG::Quatf m_extraRot; f32 m_gravity; EGG::Vector3f m_intVel; + f32 m_angVel0YFactor; bool m_forceUpright; bool m_noGravity; }; diff --git a/source/game/kart/KartMove.cc b/source/game/kart/KartMove.cc index 2c6f19b0..0b0bb296 100644 --- a/source/game/kart/KartMove.cc +++ b/source/game/kart/KartMove.cc @@ -2,7 +2,10 @@ #include "game/kart/KartDynamics.hh" #include "game/kart/KartParam.hh" +#include "game/kart/KartPhysics.hh" +#include "game/kart/KartState.hh" #include "game/kart/KartSub.hh" +#include "game/kart/KartSuspension.hh" #include "game/field/CollisionDirector.hh" #include "game/field/KCollisionTypes.hh" @@ -12,37 +15,110 @@ namespace Kart { -KartMove::KartMove() : m_smoothedUp(EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f) {} +KartMove::KartMove() + : m_smoothedUp(EGG::Vector3f::ey), m_scale(1.0f, 1.0f, 1.0f), m_totalScale(1.0f) {} -void KartMove::setInitialPhysicsValues(const EGG::Vector3f &pos, const EGG::Vector3f &angles) { +void KartMove::setTurnParams() { + init(false, false); +} + +void KartMove::init(bool b1, bool b2) { + setKartSpeedLimit(); + m_up = EGG::Vector3f::ey; + m_smoothedUp = EGG::Vector3f::ey; + m_dir = EGG::Vector3f::ez; + + if (!b2) { + m_floorCollisionCount = 0; + } + + if (!b1) { + m_scale.set(1.0f); + m_totalScale = 1.0f; + } + + m_realTurn = 0.0f; +} + +f32 KartMove::leanRot() const { + return 0.0f; +} + +void KartMove::setInitialPhysicsValues(const EGG::Vector3f &position, const EGG::Vector3f &angles) { EGG::Quatf quaternion; quaternion.setRPY(angles * DEG2RAD); - EGG::Vector3f newPos = pos; + EGG::Vector3f newPos = position; Field::CourseColMgr::CollisionInfo info; - info.astruct_7 = nullptr; Field::KCLTypeMask kcl_flags = KCL_NONE; bool bColliding = Field::CollisionDirector::Instance()->checkSphereFullPush(100.0f, newPos, EGG::Vector3f::inf, KCL_ANY, &info, &kcl_flags, 0); if (bColliding && (kcl_flags & KCL_TYPE_FLOOR)) { - newPos += info.m_minPlusMax + (info.m_floorNrm * -100.0f); - newPos += info.m_floorNrm * bsp().m_initialYPos; + newPos += info.tangentOff + (info.floorNrm * -100.0f); + newPos += info.floorNrm * bsp().initialYPos; } setPos(newPos); setRot(quaternion); sub()->initPhysicsValues(); + + physics()->setPos(pos()); + physics()->setVelocity(dynamics()->velocity()); + + m_dir = bodyFront(); + + for (u16 tireIdx = 0; tireIdx < suspCount(); ++tireIdx) { + suspension(tireIdx)->setInitialState(); + } } void KartMove::setKartSpeedLimit() { - constexpr f32 limit = 120.0f; - m_hardSpeedLimit = limit; + constexpr f32 LIMIT = 120.0f; + m_hardSpeedLimit = LIMIT; } void KartMove::calc() { dynamics()->resetInternalVelocity(); + calcTop(); + calcDirs(); + calcRotation(); +} + +void KartMove::calcTop() { + m_up += state()->top(); + + f32 topDotZ = 0.8f - 6.0f * (EGG::Mathf::abs(state()->top().dot(componentZAxis()))); + f32 scalar = std::min(0.8f, std::max(0.3f, topDotZ)); + + if (state()->isGround()) { + m_smoothedUp += (state()->top() - m_smoothedUp) * scalar; + m_smoothedUp.normalise(); + } +} + +void KartMove::calcDirs() { + EGG::Vector3f right = dynamics()->mainRot().rotateVector(EGG::Vector3f::ex); + EGG::Vector3f local_88 = right.cross(m_smoothedUp); + EGG::Matrix34f mat; + mat.setAxisRotation(0.0f, m_smoothedUp); + EGG::Vector3f local_b8 = mat.multVector(local_88); + local_b8 = local_b8.perpInPlane(m_smoothedUp, true); + + EGG::Vector3f dirDiff = local_b8 - m_dir; + + if (dirDiff.dot() <= FLT_EPSILON) { + m_dir = local_b8; + } +} + +void KartMove::calcRotation() { + calcVehicleRotation(0.0f); +} + +void KartMove::setFloorCollisionCount(u16 count) { + m_floorCollisionCount = count; } const EGG::Vector3f &KartMove::scale() const { @@ -57,4 +133,33 @@ const EGG::Vector3f &KartMove::smoothedUp() const { return m_smoothedUp; } +f32 KartMove::totalScale() const { + return m_totalScale; +} + +const EGG::Vector3f &KartMove::dir() const { + return m_dir; +} + +u16 KartMove::floorCollisionCount() const { + return m_floorCollisionCount; +} + +KartMoveBike::KartMoveBike() : m_leanRot(0.0f) {} + +KartMoveBike::~KartMoveBike() = default; + +void KartMoveBike::setTurnParams() { + KartMove::setTurnParams(); +} + +void KartMoveBike::init(bool b1, bool b2) { + KartMove::init(b1, b2); + m_leanRot = 0.0f; +} + +f32 KartMoveBike::leanRot() const { + return m_leanRot; +} + } // namespace Kart diff --git a/source/game/kart/KartMove.hh b/source/game/kart/KartMove.hh index f5607dd2..4f9e46c1 100644 --- a/source/game/kart/KartMove.hh +++ b/source/game/kart/KartMove.hh @@ -8,21 +8,51 @@ class KartMove : public KartObjectProxy { public: KartMove(); - void setInitialPhysicsValues(const EGG::Vector3f &pos, const EGG::Vector3f &angles); + virtual void setTurnParams(); + virtual void init(bool b1, bool b2); + virtual f32 leanRot() const; + + void setInitialPhysicsValues(const EGG::Vector3f &position, const EGG::Vector3f &angles); void setKartSpeedLimit(); void calc(); + void calcTop(); + void calcDirs(); + void calcRotation(); + virtual void calcVehicleRotation(f32 /*turn*/) {} + + void setFloorCollisionCount(u16 count); const EGG::Vector3f &scale() const; f32 hardSpeedLimit() const; const EGG::Vector3f &smoothedUp() const; + f32 totalScale() const; + const EGG::Vector3f &dir() const; + u16 floorCollisionCount() const; private: + f32 m_hardSpeedLimit; EGG::Vector3f m_smoothedUp; + EGG::Vector3f m_up; + EGG::Vector3f m_dir; + u16 m_floorCollisionCount; + f32 m_realTurn; EGG::Vector3f m_scale; - f32 m_hardSpeedLimit; + f32 m_totalScale; }; -class KartMoveBike : public KartMove {}; +class KartMoveBike : public KartMove { +public: + KartMoveBike(); + ~KartMoveBike(); + + void setTurnParams() override; + void init(bool b1, bool b2) override; + + f32 leanRot() const override; + +private: + f32 m_leanRot; +}; } // namespace Kart diff --git a/source/game/kart/KartObject.cc b/source/game/kart/KartObject.cc index 2088b937..ea9c9bbd 100644 --- a/source/game/kart/KartObject.cc +++ b/source/game/kart/KartObject.cc @@ -1,6 +1,9 @@ #include "KartObject.hh" +#include "game/kart/KartParam.hh" #include "game/kart/KartSub.hh" +#include "game/kart/KartSuspension.hh" +#include "game/kart/KartTire.hh" #include "game/system/RaceConfig.hh" #include "game/system/RaceManager.hh" @@ -8,11 +11,11 @@ namespace Kart { KartObject::KartObject(KartParam *param) { - m_pointers.m_param = param; + m_pointers.param = param; } KartObject::~KartObject() { - delete m_pointers.m_param; + delete m_pointers.param; } KartBody *KartObject::createBody(KartPhysics *physics) { @@ -20,10 +23,15 @@ KartBody *KartObject::createBody(KartPhysics *physics) { } void KartObject::init() { + prepareTiresAndSuspensions(); createSub(); - auto *physics = KartPhysics::Create(*m_pointers.m_param); + auto *physics = KartPhysics::Create(*m_pointers.param); auto *body = createBody(physics); - m_pointers.m_body = body; + m_pointers.body = body; + createTires(); + for (u16 tireIdx = 0; tireIdx < m_pointers.param->tireCount(); ++tireIdx) { + m_pointers.tires[tireIdx]->init(tireIdx); + } } void KartObject::initImpl() { @@ -37,13 +45,42 @@ void KartObject::prepare() { move()->setInitialPhysicsValues(position, euler_angles_deg); } +void KartObject::prepareTiresAndSuspensions() { + constexpr u16 LOCAL_20[4] = {2, 1, 1, 1}; + constexpr u16 LOCAL_28[4] = {2, 1, 1, 2}; + + const BSP &rBsp = m_pointers.param->bsp(); + const KartParam::Stats::Body bodyWheels = m_pointers.param->stats().body; + u16 wheelCount = 0; + + if (rBsp.wheels[0].enable != 0) { + wheelCount += LOCAL_20[static_cast(bodyWheels)]; + } + if (rBsp.wheels[1].enable != 0) { + wheelCount += LOCAL_28[static_cast(bodyWheels)]; + } + if (rBsp.wheels[2].enable != 0) { + wheelCount += LOCAL_20[static_cast(bodyWheels)]; + } + if (rBsp.wheels[3].enable != 0) { + wheelCount += LOCAL_28[static_cast(bodyWheels)]; + } + + m_pointers.param->setTireCount(wheelCount); + m_pointers.param->setSuspCount(wheelCount); +} + void KartObject::createSub() { - m_pointers.m_sub = new KartSub; - m_pointers.m_sub->createSubsystems(m_pointers.m_param->isBike()); + m_pointers.sub = new KartSub; + m_pointers.sub->createSubsystems(m_pointers.param->isBike()); } void KartObject::calcSub() { - m_pointers.m_sub->calcPass0(); + sub()->calcPass0(); +} + +void KartObject::calc() { + sub()->calcPass1(); } KartObject *KartObject::Create(Character character, Vehicle vehicle, u8 playerIdx) { @@ -60,22 +97,51 @@ KartObject *KartObject::Create(Character character, Vehicle vehicle, u8 playerId } object->init(); - object->m_pointers.m_sub->copyPointers(object->m_pointers); + object->m_pointers.sub->copyPointers(object->m_pointers); // Applies a valid pointer to all of the proxies we create ApplyAll(&object->m_pointers); s_list = nullptr; + + for (u16 i = 0; i < object->suspCount(); ++i) { + object->suspension(i)->initPhysics(); + } + + for (u16 i = 0; i < object->tireCount(); ++i) { + object->tire(i)->initBsp(); + } + return object; } KartObjectBike::KartObjectBike(KartParam *param) : KartObject(param) {} KartObjectBike::~KartObjectBike() { - delete m_pointers.m_param; + delete m_pointers.param; } KartBody *KartObjectBike::createBody(KartPhysics *physics) { return new KartBodyBike(physics); } +void KartObjectBike::createTires() { + for (u16 wheelIdx = 0; wheelIdx < m_pointers.param->suspCount(); ++wheelIdx) { + KartSuspension *sus = nullptr; + KartTire *tire = nullptr; + + if (wheelIdx == 0 || wheelIdx == 2) { + sus = new KartSuspensionFrontBike; + tire = new KartTireFrontBike(0); + } else { + sus = new KartSuspensionRearBike; + tire = new KartTireRearBike(1); + } + + m_pointers.suspensions.push_back(sus); + m_pointers.tires.push_back(tire); + + sus->init(wheelIdx, wheelIdx); + } +} + } // namespace Kart diff --git a/source/game/kart/KartObject.hh b/source/game/kart/KartObject.hh index e138f3a7..d91898fe 100644 --- a/source/game/kart/KartObject.hh +++ b/source/game/kart/KartObject.hh @@ -12,14 +12,17 @@ public: KartObject(KartParam *param); virtual ~KartObject(); virtual KartBody *createBody(KartPhysics *physics); + virtual void createTires() {} void init(); void initImpl(); void prepare(); + void prepareTiresAndSuspensions(); void createSub(); void calcSub(); + void calc(); static KartObject *Create(Character character, Vehicle vehicle, u8 playerIdx); @@ -32,6 +35,7 @@ public: KartObjectBike(KartParam *param); ~KartObjectBike() override; KartBody *createBody(KartPhysics *physics) override; + void createTires() override; }; } // namespace Kart diff --git a/source/game/kart/KartObjectManager.cc b/source/game/kart/KartObjectManager.cc index e0a38a5f..6e4d88db 100644 --- a/source/game/kart/KartObjectManager.cc +++ b/source/game/kart/KartObjectManager.cc @@ -14,7 +14,9 @@ void KartObjectManager::init() { void KartObjectManager::calc() { for (size_t i = 0; i < m_count; ++i) { - m_objects[i]->calcSub(); + KartObject *object = m_objects[i]; + object->calcSub(); + object->calc(); } } @@ -41,12 +43,12 @@ KartObjectManager *KartObjectManager::Instance() { KartObjectManager::KartObjectManager() { const auto &raceScenario = System::RaceConfig::Instance()->raceScenario(); - m_count = raceScenario.m_playerCount; + m_count = raceScenario.playerCount; m_objects = new KartObject *[m_count]; KartParamFileManager::CreateInstance(); for (size_t i = 0; i < m_count; ++i) { - const auto &player = raceScenario.m_players[i]; - m_objects[i] = KartObject::Create(player.m_character, player.m_vehicle, i); + const auto &player = raceScenario.players[i]; + m_objects[i] = KartObject::Create(player.character, player.vehicle, i); } } diff --git a/source/game/kart/KartObjectProxy.cc b/source/game/kart/KartObjectProxy.cc index 0dc9a350..b27f9bfe 100644 --- a/source/game/kart/KartObjectProxy.cc +++ b/source/game/kart/KartObjectProxy.cc @@ -1,7 +1,10 @@ #include "KartObjectProxy.hh" +#include "game/kart/CollisionGroup.hh" #include "game/kart/KartObject.hh" #include "game/kart/KartSub.hh" +#include "game/kart/KartSuspension.hh" +#include "game/kart/KartTire.hh" namespace Kart { @@ -14,27 +17,43 @@ KartObjectProxy::KartObjectProxy() : m_accessor(nullptr) { KartObjectProxy::~KartObjectProxy() = default; KartBody *KartObjectProxy::body() { - return m_accessor->m_body; + return m_accessor->body; } const KartBody *KartObjectProxy::body() const { - return m_accessor->m_body; + return m_accessor->body; +} + +KartCollide *KartObjectProxy::collide() { + return m_accessor->collide; +} + +const KartCollide *KartObjectProxy::collide() const { + return m_accessor->collide; +} + +CollisionGroup *KartObjectProxy::collisionGroup() { + return m_accessor->body->physics()->hitboxGroup(); +} + +const CollisionGroup *KartObjectProxy::collisionGroup() const { + return m_accessor->body->physics()->hitboxGroup(); } KartMove *KartObjectProxy::move() { - return m_accessor->m_move; + return m_accessor->move; } const KartMove *KartObjectProxy::move() const { - return m_accessor->m_move; + return m_accessor->move; } KartParam *KartObjectProxy::param() { - return m_accessor->m_param; + return m_accessor->param; } const KartParam *KartObjectProxy::param() const { - return m_accessor->m_param; + return m_accessor->param; } const BSP &KartObjectProxy::bsp() const { @@ -42,39 +61,109 @@ const BSP &KartObjectProxy::bsp() const { } KartPhysics *KartObjectProxy::physics() { - return m_accessor->m_body->getPhysics(); + return body()->physics(); } const KartPhysics *KartObjectProxy::physics() const { - return m_accessor->m_body->getPhysics(); + return body()->physics(); } KartDynamics *KartObjectProxy::dynamics() { - return physics()->getDynamics(); + return physics()->dynamics(); } const KartDynamics *KartObjectProxy::dynamics() const { - return physics()->getDynamics(); + return physics()->dynamics(); } KartState *KartObjectProxy::state() { - return m_accessor->m_state; + return m_accessor->state; } const KartState *KartObjectProxy::state() const { - return m_accessor->m_state; + return m_accessor->state; } KartSub *KartObjectProxy::sub() { - return m_accessor->m_sub; + return m_accessor->sub; } const KartSub *KartObjectProxy::sub() const { - return m_accessor->m_sub; + return m_accessor->sub; +} + +KartSuspension *KartObjectProxy::suspension(u16 suspIdx) { + return m_accessor->suspensions[suspIdx]; +} + +const KartSuspension *KartObjectProxy::suspension(u16 suspIdx) const { + return m_accessor->suspensions[suspIdx]; +} + +KartSuspensionPhysics *KartObjectProxy::suspensionPhysics(u16 suspIdx) { + return m_accessor->suspensions[suspIdx]->suspPhysics(); +} + +const KartSuspensionPhysics *KartObjectProxy::suspensionPhysics(u16 suspIdx) const { + return m_accessor->suspensions[suspIdx]->suspPhysics(); +} + +KartTire *KartObjectProxy::tire(u16 tireIdx) { + return m_accessor->tires[tireIdx]; +} + +const KartTire *KartObjectProxy::tire(u16 tireIdx) const { + return m_accessor->tires[tireIdx]; +} + +WheelPhysics *KartObjectProxy::tirePhysics(u16 tireIdx) { + return tire(tireIdx)->wheelPhysics(); +} + +const WheelPhysics *KartObjectProxy::tirePhysics(u16 tireIdx) const { + return m_accessor->tires[tireIdx]->wheelPhysics(); +} + +CollisionData &KartObjectProxy::collisionData() { + return physics()->hitboxGroup()->collisionData(); +} + +const CollisionData &KartObjectProxy::collisionData() const { + return m_accessor->body->physics()->hitboxGroup()->collisionData(); +} + +CollisionData &KartObjectProxy::collisionData(u16 tireIdx) { + return tirePhysics(tireIdx)->hitboxGroup()->collisionData(); +} + +const CollisionData &KartObjectProxy::collisionData(u16 tireIdx) const { + return m_accessor->tires[tireIdx]->wheelPhysics()->hitboxGroup()->collisionData(); } const EGG::Vector3f &KartObjectProxy::scale() const { - return m_accessor->m_move->scale(); + return move()->scale(); +} + +const EGG::Matrix34f &KartObjectProxy::pose() const { + return physics()->pose(); +} + +EGG::Vector3f KartObjectProxy::bodyFront() const { + const EGG::Matrix34f &mtx = pose(); + return EGG::Vector3f(mtx(0, 2), mtx(1, 2), mtx(2, 2)); +} + +EGG::Vector3f KartObjectProxy::bodyForward() const { + const EGG::Matrix34f &mtx = pose(); + return EGG::Vector3f(mtx(0, 0), mtx(1, 0), mtx(2, 0)); +} + +const EGG::Vector3f &KartObjectProxy::componentYAxis() const { + return physics()->yAxis(); +} + +const EGG::Vector3f &KartObjectProxy::componentZAxis() const { + return physics()->zAxis(); } void KartObjectProxy::setPos(const EGG::Vector3f &pos) { @@ -98,6 +187,18 @@ bool KartObjectProxy::isBike() const { return param()->isBike(); } +u16 KartObjectProxy::suspCount() const { + return param()->suspCount(); +} + +u16 KartObjectProxy::tireCount() const { + return param()->tireCount(); +} + +bool KartObjectProxy::hasFloorCollision(const WheelPhysics *wheelPhysics) const { + return wheelPhysics->hitboxGroup()->collisionData().floor; +} + Abstract::List *KartObjectProxy::list() const { return s_list; } diff --git a/source/game/kart/KartObjectProxy.hh b/source/game/kart/KartObjectProxy.hh index 756c84ca..43515ca0 100644 --- a/source/game/kart/KartObjectProxy.hh +++ b/source/game/kart/KartObjectProxy.hh @@ -1,12 +1,17 @@ #pragma once -#include +#include #include +#include + namespace Kart { +class CollisionGroup; +struct CollisionData; class KartBody; +class KartCollide; class KartDynamics; class KartMove; class KartParam; @@ -14,13 +19,21 @@ struct BSP; class KartPhysics; class KartState; class KartSub; +class KartSuspension; +class KartSuspensionPhysics; +class KartTire; +class WheelPhysics; struct KartAccessor { - KartParam *m_param; - KartBody *m_body; - KartSub *m_sub; - KartMove *m_move; - KartState *m_state; + KartParam *param; + KartBody *body; + KartSub *sub; + KartMove *move; + KartCollide *collide; + KartState *state; + + std::vector suspensions; + std::vector tires; }; class KartObjectProxy { @@ -32,6 +45,10 @@ public: KartBody *body(); const KartBody *body() const; + KartCollide *collide(); + const KartCollide *collide() const; + CollisionGroup *collisionGroup(); + const CollisionGroup *collisionGroup() const; KartMove *move(); const KartMove *move() const; KartParam *param(); @@ -45,8 +62,26 @@ public: const KartState *state() const; KartSub *sub(); const KartSub *sub() const; + KartSuspension *suspension(u16 suspIdx); + const KartSuspension *suspension(u16 suspIdx) const; + KartSuspensionPhysics *suspensionPhysics(u16 suspIdx); + const KartSuspensionPhysics *suspensionPhysics(u16 suspIdx) const; + KartTire *tire(u16 tireIdx); + const KartTire *tire(u16 tireIdx) const; + WheelPhysics *tirePhysics(u16 tireIdx); + const WheelPhysics *tirePhysics(u16 tireIdx) const; + CollisionData &collisionData(); + const CollisionData &collisionData() const; + CollisionData &collisionData(u16 tireIdx); + const CollisionData &collisionData(u16 tireIdx) const; const EGG::Vector3f &scale() const; + const EGG::Matrix34f &pose() const; + EGG::Vector3f bodyFront() const; + EGG::Vector3f bodyForward() const; + + const EGG::Vector3f &componentYAxis() const; + const EGG::Vector3f &componentZAxis() const; void setPos(const EGG::Vector3f &pos); void setRot(const EGG::Quatf &q); @@ -54,6 +89,9 @@ public: const EGG::Vector3f &pos() const; const EGG::Quatf &fullRot() const; bool isBike() const; + u16 suspCount() const; + u16 tireCount() const; + bool hasFloorCollision(const WheelPhysics *wheelPhysics) const; Abstract::List *list() const; diff --git a/source/game/kart/KartParam.cc b/source/game/kart/KartParam.cc index 3e10d621..db09dee9 100644 --- a/source/game/kart/KartParam.cc +++ b/source/game/kart/KartParam.cc @@ -6,6 +6,7 @@ namespace Kart { KartParam::KartParam(Character character, Vehicle vehicle, u8 playerIdx) { initStats(character, vehicle); + initBikeDispParams(vehicle); initHitboxes(vehicle); m_playerIdx = playerIdx; m_isBike = vehicle >= Vehicle::Standard_Bike_S; @@ -17,6 +18,14 @@ const BSP &KartParam::bsp() const { return m_bsp; } +const KartParam::Stats &KartParam::stats() const { + return m_stats; +} + +const KartParam::BikeDisp &KartParam::bikeDisp() const { + return m_bikeDisp; +} + u8 KartParam::playerIdx() const { return m_playerIdx; } @@ -25,6 +34,22 @@ bool KartParam::isBike() const { return m_isBike; } +u16 KartParam::suspCount() const { + return m_suspCount; +} + +u16 KartParam::tireCount() const { + return m_tireCount; +} + +void KartParam::setTireCount(u16 tireCount) { + m_tireCount = tireCount; +} + +void KartParam::setSuspCount(u16 suspCount) { + m_suspCount = suspCount; +} + void KartParam::initStats(Character character, Vehicle vehicle) { auto *fileManager = KartParamFileManager::Instance(); @@ -35,6 +60,13 @@ void KartParam::initStats(Character character, Vehicle vehicle) { m_stats.applyCharacterBonus(driverStream); } +void KartParam::initBikeDispParams(Vehicle vehicle) { + auto *fileManager = KartParamFileManager::Instance(); + + auto dispParamsStream = fileManager->getBikeDispParamsStream(vehicle); + m_bikeDisp = BikeDisp(dispParamsStream); +} + void KartParam::initHitboxes(Vehicle vehicle) { auto *fileManager = KartParamFileManager::Instance(); @@ -42,6 +74,18 @@ void KartParam::initHitboxes(Vehicle vehicle) { m_bsp = BSP(hitboxStream); } +KartParam::BikeDisp::BikeDisp() = default; + +KartParam::BikeDisp::BikeDisp(EGG::RamStream &stream) { + read(stream); +} + +void KartParam::BikeDisp::read(EGG::RamStream &stream) { + stream.skip(0xc); + m_handlePos.read(stream); + m_handleRot.read(stream); +} + KartParam::Stats::Stats() = default; KartParam::Stats::Stats(EGG::RamStream &stream) { @@ -49,86 +93,86 @@ KartParam::Stats::Stats(EGG::RamStream &stream) { } void KartParam::Stats::read(EGG::RamStream &stream) { - m_body = static_cast(stream.read_s32()); - m_driftType = static_cast(stream.read_s32()); - m_weightClass = static_cast(stream.read_s32()); + body = static_cast(stream.read_s32()); + driftType = static_cast(stream.read_s32()); + weightClass = static_cast(stream.read_s32()); _00c = stream.read_f32(); - m_weight = stream.read_f32(); - m_bumpDeviationLevel = stream.read_f32(); - m_speed = stream.read_f32(); - m_turningSpeed = stream.read_f32(); - m_tilt = stream.read_f32(); - m_accelerationStandardA[0] = stream.read_f32(); - m_accelerationStandardA[1] = stream.read_f32(); - m_accelerationStandardA[2] = stream.read_f32(); - m_accelerationStandardA[3] = stream.read_f32(); - m_accelerationStandardT[0] = stream.read_f32(); - m_accelerationStandardT[1] = stream.read_f32(); - m_accelerationStandardT[2] = stream.read_f32(); - m_accelerationDriftA[0] = stream.read_f32(); - m_accelerationDriftA[1] = stream.read_f32(); - m_accelerationDriftT[0] = stream.read_f32(); - m_handlingManualTightness = stream.read_f32(); - m_handlingAutomaticTightness = stream.read_f32(); - m_handlingReactivity = stream.read_f32(); - m_driftManualTightness = stream.read_f32(); - m_driftAutomaticTightness = stream.read_f32(); - m_driftReactivity = stream.read_f32(); - m_driftOutsideTargetAngle = stream.read_f32(); - m_driftOutsideDecrement = stream.read_f32(); - m_miniTurbo = stream.read_u32(); - - for (size_t i = 0; i < m_kclSpeed.size(); ++i) { - m_kclSpeed[i] = stream.read_f32(); + weight = stream.read_f32(); + bumpDeviationLevel = stream.read_f32(); + speed = stream.read_f32(); + turningSpeed = stream.read_f32(); + tilt = stream.read_f32(); + accelerationStandardA[0] = stream.read_f32(); + accelerationStandardA[1] = stream.read_f32(); + accelerationStandardA[2] = stream.read_f32(); + accelerationStandardA[3] = stream.read_f32(); + accelerationStandardT[0] = stream.read_f32(); + accelerationStandardT[1] = stream.read_f32(); + accelerationStandardT[2] = stream.read_f32(); + accelerationDriftA[0] = stream.read_f32(); + accelerationDriftA[1] = stream.read_f32(); + accelerationDriftT[0] = stream.read_f32(); + handlingManualTightness = stream.read_f32(); + handlingAutomaticTightness = stream.read_f32(); + handlingReactivity = stream.read_f32(); + driftManualTightness = stream.read_f32(); + driftAutomaticTightness = stream.read_f32(); + driftReactivity = stream.read_f32(); + driftOutsideTargetAngle = stream.read_f32(); + driftOutsideDecrement = stream.read_f32(); + miniTurbo = stream.read_u32(); + + for (size_t i = 0; i < kclSpeed.size(); ++i) { + kclSpeed[i] = stream.read_f32(); } - for (size_t i = 0; i < m_kclRot.size(); ++i) { - m_kclRot[i] = stream.read_f32(); + for (size_t i = 0; i < kclRot.size(); ++i) { + kclRot[i] = stream.read_f32(); } - m_itemUnk170 = stream.read_f32(); - m_itemUnk174 = stream.read_f32(); - m_itemUnk178 = stream.read_f32(); - m_itemUnk17c = stream.read_f32(); - m_maxNormalAcceleration = stream.read_f32(); - m_megaScale = stream.read_f32(); - m_wheelDistance = stream.read_f32(); + itemUnk170 = stream.read_f32(); + itemUnk174 = stream.read_f32(); + itemUnk178 = stream.read_f32(); + itemUnk17c = stream.read_f32(); + maxNormalAcceleration = stream.read_f32(); + megaScale = stream.read_f32(); + wheelDistance = stream.read_f32(); } void KartParam::Stats::applyCharacterBonus(EGG::RamStream &stream) { stream.skip(0x10); - m_weight += stream.read_f32(); + weight += stream.read_f32(); stream.skip(0x4); - m_speed += stream.read_f32(); - m_turningSpeed += stream.read_f32(); + speed += stream.read_f32(); + turningSpeed += stream.read_f32(); stream.skip(0x4); - m_accelerationStandardA[0] += stream.read_f32(); - m_accelerationStandardA[1] += stream.read_f32(); - m_accelerationStandardA[2] += stream.read_f32(); - m_accelerationStandardA[3] += stream.read_f32(); - m_accelerationStandardA[0] += stream.read_f32(); - m_accelerationStandardA[1] += stream.read_f32(); - m_accelerationStandardA[2] += stream.read_f32(); - m_accelerationDriftA[0] += stream.read_f32(); - m_accelerationDriftA[1] += stream.read_f32(); - m_accelerationDriftT[0] += stream.read_f32(); - m_handlingManualTightness += stream.read_f32(); - m_handlingAutomaticTightness += stream.read_f32(); - m_handlingReactivity += stream.read_f32(); - m_driftManualTightness += stream.read_f32(); - m_driftAutomaticTightness += stream.read_f32(); - m_driftReactivity += stream.read_f32(); - m_driftOutsideTargetAngle += stream.read_f32(); - m_driftOutsideDecrement += stream.read_f32(); - m_miniTurbo += stream.read_u32(); - - for (size_t i = 0; i < m_kclSpeed.size(); ++i) { - m_kclSpeed[i] += stream.read_f32(); + accelerationStandardA[0] += stream.read_f32(); + accelerationStandardA[1] += stream.read_f32(); + accelerationStandardA[2] += stream.read_f32(); + accelerationStandardA[3] += stream.read_f32(); + accelerationStandardA[0] += stream.read_f32(); + accelerationStandardA[1] += stream.read_f32(); + accelerationStandardA[2] += stream.read_f32(); + accelerationDriftA[0] += stream.read_f32(); + accelerationDriftA[1] += stream.read_f32(); + accelerationDriftT[0] += stream.read_f32(); + handlingManualTightness += stream.read_f32(); + handlingAutomaticTightness += stream.read_f32(); + handlingReactivity += stream.read_f32(); + driftManualTightness += stream.read_f32(); + driftAutomaticTightness += stream.read_f32(); + driftReactivity += stream.read_f32(); + driftOutsideTargetAngle += stream.read_f32(); + driftOutsideDecrement += stream.read_f32(); + miniTurbo += stream.read_u32(); + + for (size_t i = 0; i < kclSpeed.size(); ++i) { + kclSpeed[i] += stream.read_f32(); } - for (size_t i = 0; i < m_kclRot.size(); ++i) { - m_kclRot[i] += stream.read_f32(); + for (size_t i = 0; i < kclRot.size(); ++i) { + kclRot[i] += stream.read_f32(); } } @@ -139,37 +183,37 @@ BSP::BSP(EGG::RamStream &stream) { } void BSP::read(EGG::RamStream &stream) { - m_initialYPos = stream.read_f32(); + initialYPos = stream.read_f32(); - for (auto &hitbox : m_hitboxes) { - hitbox.m_enable = stream.read_u16(); + for (auto &hitbox : hitboxes) { + hitbox.enable = stream.read_u16(); stream.skip(2); - hitbox.m_position.read(stream); - hitbox.m_radius = stream.read_f32(); - hitbox.m_wallsOnly = stream.read_u16(); - hitbox.m_tireCollisionIdx = stream.read_u16(); + hitbox.position.read(stream); + hitbox.radius = stream.read_f32(); + hitbox.wallsOnly = stream.read_u16(); + hitbox.tireCollisionIdx = stream.read_u16(); } - m_cuboids[0].read(stream); - m_cuboids[1].read(stream); - m_angVel0Factor = stream.read_f32(); + cuboids[0].read(stream); + cuboids[1].read(stream); + angVel0Factor = stream.read_f32(); _1a0 = stream.read_f32(); - for (auto &wheel : m_wheels) { - wheel.m_enable = stream.read_u16(); + for (auto &wheel : wheels) { + wheel.enable = stream.read_u16(); stream.skip(2); - wheel.m_springStiffness = stream.read_f32(); - wheel.m_dampingFactor = stream.read_f32(); - wheel.m_maxTravel = stream.read_f32(); - wheel.m_relPosition.read(stream); - wheel.m_xRot = stream.read_f32(); - wheel.m_wheelRadius = stream.read_f32(); - wheel.m_sphereRadius = stream.read_f32(); + wheel.springStiffness = stream.read_f32(); + wheel.dampingFactor = stream.read_f32(); + wheel.maxTravel = stream.read_f32(); + wheel.relPosition.read(stream); + wheel.xRot = stream.read_f32(); + wheel.wheelRadius = stream.read_f32(); + wheel.sphereRadius = stream.read_f32(); wheel._28 = stream.read_u32(); } - m_rumbleHeight = stream.read_f32(); - m_rumbleSpeed = stream.read_f32(); + rumbleHeight = stream.read_f32(); + rumbleSpeed = stream.read_f32(); } } // namespace Kart diff --git a/source/game/kart/KartParam.hh b/source/game/kart/KartParam.hh index 9f7e1f24..8aefd871 100644 --- a/source/game/kart/KartParam.hh +++ b/source/game/kart/KartParam.hh @@ -6,23 +6,23 @@ namespace Kart { struct BSP { struct Hitbox { - u16 m_enable; - EGG::Vector3f m_position; - f32 m_radius; - u16 m_wallsOnly; - u16 m_tireCollisionIdx; + u16 enable; + EGG::Vector3f position; + f32 radius; + u16 wallsOnly; + u16 tireCollisionIdx; }; static_assert(sizeof(Hitbox) == 0x18); struct Wheel { - u16 m_enable; - f32 m_springStiffness; - f32 m_dampingFactor; - f32 m_maxTravel; - EGG::Vector3f m_relPosition; - f32 m_xRot; - f32 m_wheelRadius; - f32 m_sphereRadius; + u16 enable; + f32 springStiffness; + f32 dampingFactor; + f32 maxTravel; + EGG::Vector3f relPosition; + f32 xRot; + f32 wheelRadius; + f32 sphereRadius; u32 _28; }; static_assert(sizeof(Wheel) == 0x2c); @@ -32,19 +32,32 @@ struct BSP { void read(EGG::RamStream &stream); - f32 m_initialYPos; - std::array m_hitboxes; - EGG::Vector3f m_cuboids[2]; - f32 m_angVel0Factor; + f32 initialYPos; + std::array hitboxes; + EGG::Vector3f cuboids[2]; + f32 angVel0Factor; f32 _1a0; - std::array m_wheels; - f32 m_rumbleHeight; - f32 m_rumbleSpeed; + std::array wheels; + f32 rumbleHeight; + f32 rumbleSpeed; }; static_assert(sizeof(BSP) == 0x25c); class KartParam { public: + struct BikeDisp { + BikeDisp(); + BikeDisp(EGG::RamStream &stream); + + void read(EGG::RamStream &stream); + + u8 _00[0x0c - 0x00]; + EGG::Vector3f m_handlePos; + EGG::Vector3f m_handleRot; + u8 _24[0xb0 - 0x24]; + }; + static_assert(sizeof(BikeDisp) == 0xB0); + struct Stats { enum class Body { Four_Wheel_Kart = 0, // Used by most karts @@ -71,37 +84,37 @@ public: void read(EGG::RamStream &stream); void applyCharacterBonus(EGG::RamStream &stream); - Body m_body; - DriftType m_driftType; - WeightClass m_weightClass; + Body body; + DriftType driftType; + WeightClass weightClass; f32 _00c; // Unused - f32 m_weight; - f32 m_bumpDeviationLevel; - f32 m_speed; - f32 m_turningSpeed; - f32 m_tilt; - f32 m_accelerationStandardA[4]; - f32 m_accelerationStandardT[3]; - f32 m_accelerationDriftA[2]; - f32 m_accelerationDriftT[1]; - f32 m_handlingManualTightness; - f32 m_handlingAutomaticTightness; - f32 m_handlingReactivity; - f32 m_driftManualTightness; - f32 m_driftAutomaticTightness; - f32 m_driftReactivity; - f32 m_driftOutsideTargetAngle; - f32 m_driftOutsideDecrement; - u32 m_miniTurbo; - std::array m_kclSpeed; - std::array m_kclRot; - f32 m_itemUnk170; - f32 m_itemUnk174; - f32 m_itemUnk178; - f32 m_itemUnk17c; - f32 m_maxNormalAcceleration; - f32 m_megaScale; - f32 m_wheelDistance; + f32 weight; + f32 bumpDeviationLevel; + f32 speed; + f32 turningSpeed; + f32 tilt; + f32 accelerationStandardA[4]; + f32 accelerationStandardT[3]; + f32 accelerationDriftA[2]; + f32 accelerationDriftT[1]; + f32 handlingManualTightness; + f32 handlingAutomaticTightness; + f32 handlingReactivity; + f32 driftManualTightness; + f32 driftAutomaticTightness; + f32 driftReactivity; + f32 driftOutsideTargetAngle; + f32 driftOutsideDecrement; + u32 miniTurbo; + std::array kclSpeed; + std::array kclRot; + f32 itemUnk170; + f32 itemUnk174; + f32 itemUnk178; + f32 itemUnk17c; + f32 maxNormalAcceleration; + f32 megaScale; + f32 wheelDistance; }; static_assert(sizeof(Stats) == 0x18c); @@ -109,17 +122,28 @@ public: ~KartParam(); const BSP &bsp() const; + const Stats &stats() const; + const BikeDisp &bikeDisp() const; u8 playerIdx() const; bool isBike() const; + u16 suspCount() const; + u16 tireCount() const; + + void setTireCount(u16 tireCount); + void setSuspCount(u16 suspCount); private: void initStats(Character character, Vehicle vehicle); + void initBikeDispParams(Vehicle vehicle); void initHitboxes(Vehicle vehicle); Stats m_stats; + BikeDisp m_bikeDisp; BSP m_bsp; u8 m_playerIdx; bool m_isBike; + u16 m_suspCount; + u16 m_tireCount; }; } // namespace Kart diff --git a/source/game/kart/KartParamFileManager.cc b/source/game/kart/KartParamFileManager.cc index 28904de9..cc3a23ae 100644 --- a/source/game/kart/KartParamFileManager.cc +++ b/source/game/kart/KartParamFileManager.cc @@ -7,11 +7,13 @@ namespace Kart { void KartParamFileManager::clear() { m_kartParam.clear(); m_driverParam.clear(); + m_bikeDispParam.clear(); } void KartParamFileManager::init() { m_kartParam.load("kartParam.bin"); m_driverParam.load("driverParam.bin"); + m_bikeDispParam.load("bikePartsDispParam.bin"); if (!validate()) { K_PANIC("Parameter files could not be validated!"); } @@ -56,9 +58,9 @@ EGG::RamStream KartParamFileManager::getDriverStream(Character character) const break; } - auto *file = reinterpret_cast(m_driverParam.m_file); + auto *file = reinterpret_cast *>(m_driverParam.file); assert(file); - void *offset = &file->m_params[idx]; + void *offset = &file->params[idx]; u32 size = sizeof(KartParam::Stats); return EGG::RamStream(reinterpret_cast(offset), size); } @@ -69,9 +71,9 @@ EGG::RamStream KartParamFileManager::getVehicleStream(Vehicle vehicle) const { } s32 idx = static_cast(vehicle); - auto *file = reinterpret_cast(m_kartParam.m_file); + auto *file = reinterpret_cast *>(m_kartParam.file); assert(file); - void *offset = &file->m_params[idx]; + void *offset = &file->params[idx]; u32 size = sizeof(KartParam::Stats); return EGG::RamStream(reinterpret_cast(offset), size); } @@ -90,6 +92,22 @@ EGG::RamStream KartParamFileManager::getHitboxStream(Vehicle vehicle) const { return EGG::RamStream(reinterpret_cast(file), size); } +EGG::RamStream KartParamFileManager::getBikeDispParamsStream(Vehicle vehicle) const { + if (vehicle < Vehicle::Standard_Bike_S && vehicle >= Vehicle::Max) { + K_PANIC("Uh oh."); + } + + // We need to index at the correct offset + constexpr u32 KART_MAX = 18; + s32 idx = static_cast(vehicle) - KART_MAX; + + auto *file = reinterpret_cast *>(m_bikeDispParam.file); + assert(file); + void *offset = &file->params[idx]; + u32 size = sizeof(KartParam::BikeDisp); + return EGG::RamStream(reinterpret_cast(offset), size); +} + KartParamFileManager *KartParamFileManager::CreateInstance() { assert(!s_instance); s_instance = new KartParamFileManager; @@ -114,22 +132,32 @@ KartParamFileManager::~KartParamFileManager() = default; bool KartParamFileManager::validate() const { // Validate kartParam.bin - if (!m_kartParam.m_file || m_kartParam.m_size == 0) { + if (!m_kartParam.file || m_kartParam.size == 0) { return false; } - auto *file = reinterpret_cast(m_kartParam.m_file); - if (m_kartParam.m_size != parse(file->m_count) * sizeof(KartParam::Stats) + 4) { + auto *kartFile = reinterpret_cast *>(m_kartParam.file); + if (m_kartParam.size != parse(kartFile->count) * sizeof(KartParam::Stats) + 4) { return false; } // Validate driverParam.bin - if (!m_driverParam.m_file || m_driverParam.m_size == 0) { + if (!m_driverParam.file || m_driverParam.size == 0) { + return false; + } + + auto *driverFile = reinterpret_cast *>(m_driverParam.file); + if (m_driverParam.size != parse(driverFile->count) * sizeof(KartParam::Stats) + 4) { + return false; + } + + // Validate bikePartsDispParam.bin + if (!m_bikeDispParam.file || m_bikeDispParam.size == 0) { return false; } - file = reinterpret_cast(m_driverParam.m_file); - if (m_driverParam.m_size != parse(file->m_count) * sizeof(KartParam::Stats) + 4) { + auto *bikeDispFile = reinterpret_cast *>(m_bikeDispParam.file); + if (m_bikeDispParam.size != parse(bikeDispFile->count) * sizeof(KartParam::BikeDisp) + 4) { return false; } @@ -137,13 +165,13 @@ bool KartParamFileManager::validate() const { } void KartParamFileManager::FileInfo::clear() { - m_file = nullptr; - m_size = 0; + file = nullptr; + size = 0; } void KartParamFileManager::FileInfo::load(const char *filename) { auto *resourceManager = System::ResourceManager::Instance(); - m_file = resourceManager->getFile(filename, &m_size, System::ArchiveId::Core); + file = resourceManager->getFile(filename, &size, System::ArchiveId::Core); } KartParamFileManager *KartParamFileManager::s_instance = nullptr; diff --git a/source/game/kart/KartParamFileManager.hh b/source/game/kart/KartParamFileManager.hh index e2bb59f5..275003c7 100644 --- a/source/game/kart/KartParamFileManager.hh +++ b/source/game/kart/KartParamFileManager.hh @@ -11,23 +11,25 @@ public: EGG::RamStream getDriverStream(Character character) const; EGG::RamStream getVehicleStream(Vehicle vehicle) const; EGG::RamStream getHitboxStream(Vehicle vehicle) const; + EGG::RamStream getBikeDispParamsStream(Vehicle vehicle) const; static KartParamFileManager *CreateInstance(); static void DestroyInstance(); static KartParamFileManager *Instance(); private: + template struct ParamFile { - u32 m_count; - KartParam::Stats m_params[]; + u32 count; + T params[]; }; struct FileInfo { void clear(); void load(const char *filename); - void *m_file; - size_t m_size; + void *file; + size_t size; }; KartParamFileManager(); @@ -35,8 +37,9 @@ private: bool validate() const; - FileInfo m_kartParam; // kartParam.bin - FileInfo m_driverParam; // driverParam.bin + FileInfo m_kartParam; // kartParam.bin + FileInfo m_driverParam; // driverParam.bin + FileInfo m_bikeDispParam; // bikePartsDispParam.bin static KartParamFileManager *s_instance; }; diff --git a/source/game/kart/KartPhysics.cc b/source/game/kart/KartPhysics.cc index 42aa1eff..352ba6e3 100644 --- a/source/game/kart/KartPhysics.cc +++ b/source/game/kart/KartPhysics.cc @@ -5,10 +5,18 @@ namespace Kart { KartPhysics::KartPhysics(bool isBike) { m_pose = EGG::Matrix34f::ident; m_dynamics = isBike ? new KartDynamicsBike : new KartDynamics; + m_hitboxGroup = new CollisionGroup; + m_fc = 50.0f; // set immediately after in KartPhysics::Create() +} + +KartPhysics::~KartPhysics() { + delete m_dynamics; + delete m_hitboxGroup; } void KartPhysics::reset() { m_dynamics->init(); + m_hitboxGroup->reset(); m_decayingStuntRot = EGG::Quatf::ident; m_instantaneousStuntRot = EGG::Quatf::ident; m_specialRot = EGG::Quatf::ident; @@ -46,18 +54,38 @@ void KartPhysics::calc(f32 dt, f32 maxSpeed, const EGG::Vector3f & /*scale*/, bo m_instantaneousExtraRot = EGG::Quatf::ident; } -KartDynamics *KartPhysics::getDynamics() { +KartDynamics *KartPhysics::dynamics() { return m_dynamics; } -const KartDynamics *KartPhysics::getDynamics() const { +const KartDynamics *KartPhysics::dynamics() const { return m_dynamics; } -const EGG::Matrix34f &KartPhysics::getPose() const { +const EGG::Matrix34f &KartPhysics::pose() const { return m_pose; } +CollisionGroup *KartPhysics::hitboxGroup() { + return m_hitboxGroup; +} + +const EGG::Vector3f &KartPhysics::yAxis() const { + return m_yAxis; +} + +const EGG::Vector3f &KartPhysics::zAxis() const { + return m_zAxis; +} + +const EGG::Vector3f &KartPhysics::pos() const { + return m_pos; +} + +f32 KartPhysics::fc() const { + return m_fc; +} + void KartPhysics::setPos(const EGG::Vector3f &pos) { m_pos = pos; } @@ -66,9 +94,21 @@ void KartPhysics::setVelocity(const EGG::Vector3f &vel) { m_velocity = vel; } +void KartPhysics::set_fc(f32 val) { + m_fc = val; +} + KartPhysics *KartPhysics::Create(const KartParam ¶m) { - // TODO: Hitboxes and BSP params - return new KartPhysics(param.isBike()); + KartPhysics *physics = new KartPhysics(param.isBike()); + + const BSP &bsp = param.bsp(); + + physics->set_fc(physics->hitboxGroup()->initHitboxes(bsp.hitboxes)); + + physics->dynamics()->setBspParams(bsp.angVel0Factor, bsp.cuboids[0], bsp.cuboids[1], + false); + + return physics; } } // namespace Kart diff --git a/source/game/kart/KartPhysics.hh b/source/game/kart/KartPhysics.hh index dee1054a..42e480ca 100644 --- a/source/game/kart/KartPhysics.hh +++ b/source/game/kart/KartPhysics.hh @@ -1,5 +1,6 @@ #pragma once +#include "game/kart/CollisionGroup.hh" #include "game/kart/KartDynamics.hh" #include "game/kart/KartParam.hh" @@ -10,23 +11,31 @@ namespace Kart { class KartPhysics { public: KartPhysics(bool isBike); + ~KartPhysics(); void reset(); void updatePose(); void calc(f32 dt, f32 maxSpeed, const EGG::Vector3f &scale, bool air); - KartDynamics *getDynamics(); - const KartDynamics *getDynamics() const; - const EGG::Matrix34f &getPose() const; + KartDynamics *dynamics(); + const KartDynamics *dynamics() const; + const EGG::Matrix34f &pose() const; + CollisionGroup *hitboxGroup(); + const EGG::Vector3f &yAxis() const; + const EGG::Vector3f &zAxis() const; + const EGG::Vector3f &pos() const; + f32 fc() const; void setPos(const EGG::Vector3f &pos); void setVelocity(const EGG::Vector3f &vel); + void set_fc(f32 val); static KartPhysics *Create(const KartParam ¶m); private: KartDynamics *m_dynamics; + CollisionGroup *m_hitboxGroup; EGG::Vector3f m_pos; EGG::Quatf m_decayingStuntRot; EGG::Quatf m_instantaneousStuntRot; @@ -39,6 +48,7 @@ private: EGG::Vector3f m_yAxis; EGG::Vector3f m_zAxis; EGG::Vector3f m_velocity; + f32 m_fc; // collisionLimit? }; } // namespace Kart diff --git a/source/game/kart/KartState.cc b/source/game/kart/KartState.cc index b4a5fee0..35ce81fb 100644 --- a/source/game/kart/KartState.cc +++ b/source/game/kart/KartState.cc @@ -1,5 +1,7 @@ #include "KartState.hh" +#include "game/kart/CollisionGroup.hh" + namespace Kart { KartState::KartState() { @@ -12,10 +14,45 @@ void KartState::init() { void KartState::reset() { m_ground = false; + m_top.setZero(); +} + +void KartState::calc() { + calcCollisions(); +} + +void KartState::calcCollisions() { + m_top.setZero(); + + u16 wheelCollisions = 0; + for (u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) { + if (hasFloorCollision(tirePhysics(tireIdx))) { + m_top += collisionData(tireIdx).floorNrm; + ++wheelCollisions; + } + } + + const CollisionData &colData = collisionData(); + if (colData.floor) { + m_top += colData.floorNrm; + } + + m_top.normalise(); + + if (wheelCollisions < 1 && !colData.floor) { + // Airtime + ; + } else { + m_ground = true; + } } bool KartState::isGround() const { return m_ground; } +const EGG::Vector3f &KartState::top() const { + return m_top; +} + } // namespace Kart diff --git a/source/game/kart/KartState.hh b/source/game/kart/KartState.hh index d0ae3b52..a05305ab 100644 --- a/source/game/kart/KartState.hh +++ b/source/game/kart/KartState.hh @@ -11,10 +11,16 @@ public: void init(); void reset(); + void calc(); + void calcCollisions(); + bool isGround() const; + const EGG::Vector3f &top() const; private: bool m_ground; + + EGG::Vector3f m_top; }; } // namespace Kart diff --git a/source/game/kart/KartSub.cc b/source/game/kart/KartSub.cc index 43c747db..c73c837e 100644 --- a/source/game/kart/KartSub.cc +++ b/source/game/kart/KartSub.cc @@ -1,8 +1,12 @@ #include "KartSub.hh" #include "game/kart/KartBody.hh" +#include "game/kart/KartCollide.hh" #include "game/kart/KartMove.hh" #include "game/kart/KartState.hh" +#include "game/kart/KartSuspensionPhysics.hh" + +#include "game/field/CollisionDirector.hh" namespace Kart { @@ -11,33 +15,51 @@ KartSub::KartSub() = default; void KartSub::createSubsystems(bool isBike) { m_move = isBike ? new KartMoveBike : new KartMove; m_state = new KartState; + m_collide = new KartCollide; } void KartSub::copyPointers(KartAccessor &pointers) { - pointers.m_move = m_move; - pointers.m_state = m_state; + pointers.collide = m_collide; + pointers.state = m_state; + pointers.move = m_move; } void KartSub::init() { resetPhysics(); body()->reset(); m_state->init(); + move()->setTurnParams(); + m_collide->init(); } void KartSub::initPhysicsValues() { physics()->updatePose(); + collide()->resetHitboxes(); } void KartSub::resetPhysics() { physics()->reset(); physics()->updatePose(); + collide()->resetHitboxes(); + + for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) { + suspensionPhysics(wheelIdx)->reset(); + } + for (u16 tireIdx = 0; tireIdx < tireCount(); ++tireIdx) { + tirePhysics(tireIdx)->reset(); + } m_move->setKartSpeedLimit(); + m_someScale = 1.0f; + m_maxSuspOvertravel.setZero(); + m_minSuspOvertravel.setZero(); } void KartSub::calcPass0() { + state()->calc(); physics()->setPos(dynamics()->pos()); physics()->setVelocity(dynamics()->velocity()); dynamics()->setGravity(-1.3f); + dynamics()->setAngVel0YFactor(0.9f); move()->calc(); @@ -54,6 +76,51 @@ void KartSub::calcPass0() { f32 maxSpeed = move()->hardSpeedLimit(); physics()->calc(DT, maxSpeed, scale(), !state()->isGround()); + + collide()->calcHitboxes(); + collisionGroup()->setHitboxScale(move()->totalScale()); +} + +void KartSub::calcPass1() { + m_floorCollisionCount = 0; + m_maxSuspOvertravel.setZero(); + m_minSuspOvertravel.setZero(); + + Field::CollisionDirector::Instance()->checkCourseColNarrScLocal(250.0f, pos(), + KCL_TYPE_VEHICLE_INTERACTABLE, 0); + collide()->findCollision(); + collide()->calcFloorEffect(); + + EGG::Vector3f forward = fullRot().rotateVector(EGG::Vector3f::ez); + m_someScale = scale().y; + + const EGG::Vector3f gravity(0.0f, -1.3f, 0.0f); + for (u16 i = 0; i < suspCount(); ++i) { + const EGG::Matrix34f wheelMatrix = body()->wheelMatrix(i); + suspensionPhysics(i)->calcCollision(DT, gravity, wheelMatrix); + } + + EGG::Vector3f vehicleCompensation = m_maxSuspOvertravel + m_minSuspOvertravel; + dynamics()->setPos(dynamics()->pos() + vehicleCompensation); + if (!physics()->hitboxGroup()->collisionData().floor) { + EGG::Vector3f relPos; + EGG::Vector3f vel; + EGG::Vector3f floorNrm; + + for (u16 wheelIdx = 0; wheelIdx < suspCount(); ++wheelIdx) { + suspensionPhysics(wheelIdx)->calcSuspension(forward, vehicleCompensation); + } + + move()->setFloorCollisionCount(m_floorCollisionCount); + + physics()->updatePose(); + + collide()->resetHitboxes(); + } +} + +f32 KartSub::someScale() { + return m_someScale; } } // namespace Kart diff --git a/source/game/kart/KartSub.hh b/source/game/kart/KartSub.hh index 9fa09d8c..337c6cf9 100644 --- a/source/game/kart/KartSub.hh +++ b/source/game/kart/KartSub.hh @@ -16,10 +16,18 @@ public: void resetPhysics(); void calcPass0(); + void calcPass1(); + + f32 someScale(); private: KartMove *m_move; + KartCollide *m_collide; KartState *m_state; + EGG::Vector3f m_maxSuspOvertravel; + EGG::Vector3f m_minSuspOvertravel; + u16 m_floorCollisionCount; + f32 m_someScale; static constexpr f32 DT = 1.0f; }; diff --git a/source/game/kart/KartSuspension.cc b/source/game/kart/KartSuspension.cc new file mode 100644 index 00000000..56ade10a --- /dev/null +++ b/source/game/kart/KartSuspension.cc @@ -0,0 +1,25 @@ +#include "KartSuspension.hh" + +namespace Kart { + +KartSuspension::KartSuspension() = default; + +KartSuspension::~KartSuspension() = default; + +void KartSuspension::init(u16 wheelIdx, u16 bspWheelIdx) { + m_physics = new KartSuspensionPhysics(wheelIdx, bspWheelIdx); +} + +void KartSuspension::initPhysics() { + m_physics->init(); +} + +void KartSuspension::setInitialState() { + m_physics->setInitialState(); +} + +KartSuspensionPhysics *KartSuspension::suspPhysics() { + return m_physics; +} + +} // namespace Kart diff --git a/source/game/kart/KartSuspension.hh b/source/game/kart/KartSuspension.hh new file mode 100644 index 00000000..dc3d1485 --- /dev/null +++ b/source/game/kart/KartSuspension.hh @@ -0,0 +1,27 @@ +#pragma once + +#include "game/kart/KartSuspensionPhysics.hh" + +namespace Kart { + +class KartSuspension : KartObjectProxy { +public: + KartSuspension(); + ~KartSuspension(); + + void init(u16 wheelIdx, u16 bspWheelIdx); + void initPhysics(); + + void setInitialState(); + + KartSuspensionPhysics *suspPhysics(); + +private: + KartSuspensionPhysics *m_physics; +}; + +class KartSuspensionFrontBike : public KartSuspension {}; + +class KartSuspensionRearBike : public KartSuspension {}; + +} // namespace Kart diff --git a/source/game/kart/KartSuspensionPhysics.cc b/source/game/kart/KartSuspensionPhysics.cc new file mode 100644 index 00000000..d772b16c --- /dev/null +++ b/source/game/kart/KartSuspensionPhysics.cc @@ -0,0 +1,232 @@ +#include "KartSuspensionPhysics.hh" + +#include "game/kart/KartCollide.hh" +#include "game/kart/KartDynamics.hh" +#include "game/kart/KartSub.hh" +#include "game/kart/KartTire.hh" + +#include + +namespace Kart { + +WheelPhysics::WheelPhysics(u16 wheelIdx, u16 bspWheelIdx) + : m_wheelIdx(wheelIdx), m_bspWheelIdx(bspWheelIdx), m_bspWheel(nullptr) {} + +WheelPhysics::~WheelPhysics() = default; + +void WheelPhysics::init() { + m_hitboxGroup = new CollisionGroup; + m_hitboxGroup->createSingleHitbox(10.0f, EGG::Vector3f::zero); +} + +void WheelPhysics::initBsp() { + m_bspWheel = &bsp().wheels[m_bspWheelIdx]; +} + +void WheelPhysics::reset() { + m_pos.setZero(); + m_lastPos.setZero(); + m_lastPosDiff.setZero(); + m_suspTravel = 0.0f; + m_colVel.setZero(); + m_speed.setZero(); + m_wheelEdgePos.setZero(); + m_effectiveRadius = 0.0f; + m_targetEffectiveRadius = 0.0f; + m_topmostPos.setZero(); + + if (m_bspWheel) { + m_suspTravel = m_bspWheel->maxTravel; + m_effectiveRadius = m_bspWheel->wheelRadius; + } +} + +void WheelPhysics::realign(const EGG::Vector3f &bottom, const EGG::Vector3f &vehicleMovement) { + const EGG::Vector3f topmostPos = m_topmostPos + vehicleMovement; + f32 scaledMaxTravel = m_bspWheel->maxTravel * sub()->someScale(); + f32 suspTravel = bottom.dot(m_pos - topmostPos); + m_suspTravel = std::max(0.0f, std::min(scaledMaxTravel, suspTravel)); + m_pos = topmostPos + m_suspTravel * bottom; + m_speed = m_pos - m_lastPos; + m_speed -= dynamics()->intVel(); + m_speed -= collisionData().movement; + m_hitboxGroup->collisionData().vel += m_speed; + m_lastPos = m_pos; + m_lastPosDiff = m_pos - topmostPos; +} + +void WheelPhysics::updateCollision(const EGG::Vector3f &bottom, const EGG::Vector3f &topmostPos) { + m_targetEffectiveRadius = m_bspWheel->wheelRadius; + f32 nextRadius = m_bspWheel->sphereRadius; + f32 scalar = m_effectiveRadius * scale().y - nextRadius * move()->totalScale(); + + EGG::Vector3f center = m_pos + scalar * bottom; + scalar = 0.3f * nextRadius * move()->leanRot() * move()->totalScale(); + center += scalar * bodyForward(); + m_hitboxGroup->setHitboxScale(move()->totalScale()); + collide()->calcWheelCollision(m_wheelIdx, m_hitboxGroup, m_colVel, center, nextRadius); + CollisionData &colData = m_hitboxGroup->collisionData(); + + if (colData.floor) { + m_pos += colData.tangentOff; + if (colData.intensity > -1) { + m_targetEffectiveRadius = m_bspWheel->wheelRadius - static_cast(colData.intensity); + } + } + + m_hitboxGroup->hitbox(0).setLastPos(center); + + m_topmostPos = topmostPos; + m_wheelEdgePos = m_pos + m_effectiveRadius * move()->totalScale() * bottom; + m_effectiveRadius += (m_targetEffectiveRadius - m_effectiveRadius) * 0.1f; + m_suspTravel = bottom.dot(m_pos - topmostPos); +} + +const EGG::Vector3f &WheelPhysics::pos() const { + return m_pos; +} + +const EGG::Vector3f &WheelPhysics::lastPosDiff() const { + return m_lastPosDiff; +} + +f32 WheelPhysics::suspTravel() { + return m_suspTravel; +} + +const EGG::Vector3f &WheelPhysics::topmostPos() const { + return m_topmostPos; +} + +CollisionGroup *WheelPhysics::hitboxGroup() { + return m_hitboxGroup; +} + +const CollisionGroup *WheelPhysics::hitboxGroup() const { + return m_hitboxGroup; +} + +const EGG::Vector3f &WheelPhysics::speed() const { + return m_speed; +} + +f32 WheelPhysics::effectiveRadius() const { + return m_effectiveRadius; +} + +void WheelPhysics::setSuspTravel(f32 suspTravel) { + m_suspTravel = suspTravel; +} + +void WheelPhysics::setPos(const EGG::Vector3f &pos) { + m_pos = pos; +} + +void WheelPhysics::setLastPos(const EGG::Vector3f &pos) { + m_lastPos = pos; +} + +void WheelPhysics::setLastPosDiff(const EGG::Vector3f &pos) { + m_lastPosDiff = pos; +} + +void WheelPhysics::setWheelEdgePos(const EGG::Vector3f &pos) { + m_wheelEdgePos = pos; +} + +void WheelPhysics::setColVel(const EGG::Vector3f &vec) { + m_colVel = vec; +} + +KartSuspensionPhysics::KartSuspensionPhysics(u16 wheelIdx, u16 bspWheelIdx) + : m_tirePhysics(nullptr), m_bspWheelIdx(bspWheelIdx), m_wheelIdx(wheelIdx) {} + +KartSuspensionPhysics::~KartSuspensionPhysics() = default; + +void KartSuspensionPhysics::init() { + m_tirePhysics = tire(m_wheelIdx)->wheelPhysics(); + m_bspWheel = &bsp().wheels[m_bspWheelIdx]; +} + +void KartSuspensionPhysics::reset() { + m_topmostPos.setZero(); + m_maxTravelScaled = 0.0f; + m_bottomDir.setZero(); +} + +void KartSuspensionPhysics::setInitialState() { + EGG::Vector3f rotatedRelPos = dynamics()->fullRot().rotateVector(m_bspWheel->relPosition); + rotatedRelPos += pos(); + + EGG::Vector3f unitRotated = dynamics()->fullRot().rotateVector(-EGG::Vector3f::ey); + + m_tirePhysics->setPos(rotatedRelPos + m_bspWheel->maxTravel * unitRotated); + m_tirePhysics->setLastPos(rotatedRelPos + m_bspWheel->maxTravel * unitRotated); + m_tirePhysics->setLastPosDiff(m_tirePhysics->pos() - rotatedRelPos); + m_tirePhysics->setWheelEdgePos(m_tirePhysics->pos() + + (m_tirePhysics->effectiveRadius() * move()->totalScale() * unitRotated)); + m_tirePhysics->hitboxGroup()->hitbox(0).setWorldPos(m_tirePhysics->pos()); + m_tirePhysics->hitboxGroup()->hitbox(0).setLastPos(pos() + 100 * EGG::Vector3f::ey); + m_topmostPos = rotatedRelPos; +} + +void KartSuspensionPhysics::calcCollision(f32 dt, const EGG::Vector3f &gravity, + const EGG::Matrix34f &mat) { + m_maxTravelScaled = m_bspWheel->maxTravel * sub()->someScale(); + EGG::Vector3f topmostPos = mat.ps_multVector(m_bspWheel->relPosition * scale()); + EGG::Matrix34f mStack_60; + EGG::Vector3f euler_angles(m_bspWheel->xRot * DEG2RAD, 0.0f, 0.0f); + mStack_60.makeR(euler_angles); + EGG::Vector3f local_ac = mStack_60.multVector33(EGG::Vector3f(0.0f, -1.0f, 0.0f)); + m_bottomDir = mat.multVector33(local_ac); + + f32 y_down = m_tirePhysics->suspTravel() + sub()->someScale() * 5.0f; + m_tirePhysics->setSuspTravel(std::max(0.0f, std::min(m_maxTravelScaled, y_down))); + m_tirePhysics->setColVel(dt * 10.0f * gravity); + m_tirePhysics->setPos(topmostPos + m_tirePhysics->suspTravel() * m_bottomDir); + m_tirePhysics->updateCollision(m_bottomDir, topmostPos); + + m_topmostPos = topmostPos; +} + +void KartSuspensionPhysics::calcSuspension(const EGG::Vector3f &forward, + const EGG::Vector3f &vehicleMovement) { + EGG::Vector3f lastPosDiff = m_tirePhysics->lastPosDiff(); + + m_tirePhysics->realign(m_bottomDir, vehicleMovement); + + CollisionGroup *hitboxGroup = m_tirePhysics->hitboxGroup(); + CollisionData &collisionData = hitboxGroup->collisionData(); + if (!collisionData.floor) { + return; + } + + EGG::Vector3f topDiff = m_tirePhysics->pos() - m_topmostPos; + f32 yDown = std::max(0.0f, m_bottomDir.dot(topDiff)); + EGG::Vector3f speed = lastPosDiff - topDiff; + f32 travel = m_maxTravelScaled - yDown; + f32 speedScalar = m_bottomDir.dot(speed); + + f32 springDamp = + -(m_bspWheel->springStiffness * travel + m_bspWheel->dampingFactor * speedScalar); + + EGG::Vector3f fRot = m_bottomDir * springDamp; + EGG::Vector3f fLinear = fRot; + EGG::Vector3f rotProj = fRot; + rotProj.y = 0.0f; + + rotProj = rotProj.proj(collisionData.floorNrm); + fLinear.y += rotProj.y; + fLinear.y = std::min(fLinear.y, param()->stats().maxNormalAcceleration); + + if (dynamics()->extVel().y > 5.0f) { + fLinear.y = 0.0f; + } + + dynamics()->applySuspensionWrench(m_topmostPos, fLinear, fRot, false); + + collide()->applySomeFloorMoment(0.1f, 0.8f, hitboxGroup, forward, move()->dir(), + m_tirePhysics->speed(), true, true, true); +} + +} // namespace Kart diff --git a/source/game/kart/KartSuspensionPhysics.hh b/source/game/kart/KartSuspensionPhysics.hh new file mode 100644 index 00000000..73c3a254 --- /dev/null +++ b/source/game/kart/KartSuspensionPhysics.hh @@ -0,0 +1,80 @@ +#pragma once + +#include "game/kart/CollisionGroup.hh" +#include "game/kart/KartObjectProxy.hh" +#include "game/kart/KartParam.hh" + +#include + +namespace Kart { + +class WheelPhysics : KartObjectProxy { +public: + WheelPhysics(u16 wheelIdx, u16 bspWheelIdx); + ~WheelPhysics(); + + void init(); + void initBsp(); + void reset(); + + void realign(const EGG::Vector3f &bottom, const EGG::Vector3f &vehicleMovement); + + void updateCollision(const EGG::Vector3f &bottom, const EGG::Vector3f &topmostPos); + + const EGG::Vector3f &pos() const; + const EGG::Vector3f &lastPosDiff() const; + f32 suspTravel(); + const EGG::Vector3f &topmostPos() const; + CollisionGroup *hitboxGroup(); + const CollisionGroup *hitboxGroup() const; + const EGG::Vector3f &speed() const; + f32 effectiveRadius() const; + + void setSuspTravel(f32 suspTravel); + void setPos(const EGG::Vector3f &pos); + void setLastPos(const EGG::Vector3f &pos); + void setLastPosDiff(const EGG::Vector3f &pos); + void setWheelEdgePos(const EGG::Vector3f &pos); + void setColVel(const EGG::Vector3f &vec); + +private: + u16 m_wheelIdx; + u16 m_bspWheelIdx; + const BSP::Wheel *m_bspWheel; + CollisionGroup *m_hitboxGroup; + EGG::Vector3f m_pos; + EGG::Vector3f m_lastPos; + EGG::Vector3f m_lastPosDiff; + f32 m_suspTravel; + EGG::Vector3f m_colVel; + EGG::Vector3f m_speed; + EGG::Vector3f m_wheelEdgePos; + f32 m_effectiveRadius; + f32 m_targetEffectiveRadius; + EGG::Vector3f m_topmostPos; +}; + +class KartSuspensionPhysics : KartObjectProxy { +public: + KartSuspensionPhysics(u16 wheelIdx, u16 bspWheelIdx); + ~KartSuspensionPhysics(); + + void init(); + void reset(); + + void setInitialState(); + + void calcCollision(f32 dt, const EGG::Vector3f &gravity, const EGG::Matrix34f &mat); + void calcSuspension(const EGG::Vector3f &forward, const EGG::Vector3f &vehicleMovement); + +private: + const BSP::Wheel *m_bspWheel; + WheelPhysics *m_tirePhysics; + u16 m_bspWheelIdx; + u16 m_wheelIdx; + EGG::Vector3f m_topmostPos; + f32 m_maxTravelScaled; + EGG::Vector3f m_bottomDir; +}; + +} // namespace Kart diff --git a/source/game/kart/KartTire.cc b/source/game/kart/KartTire.cc new file mode 100644 index 00000000..c8bba51e --- /dev/null +++ b/source/game/kart/KartTire.cc @@ -0,0 +1,42 @@ +#include "KartTire.hh" + +namespace Kart { + +KartTire::KartTire(u16 bspWheelIdx) : m_bspWheelIdx(bspWheelIdx) {} + +KartTire::~KartTire() = default; + +void KartTire::createPhysics(u16 tireIdx) { + m_wheelPhysics = new WheelPhysics(tireIdx, 1); +} + +void KartTire::init(u16 tireIdx) { + createPhysics(tireIdx); + m_wheelPhysics->init(); +} + +void KartTire::initBsp() { + m_wheelPhysics->initBsp(); +} + +WheelPhysics *KartTire::wheelPhysics() { + return m_wheelPhysics; +} + +KartTireFrontBike::KartTireFrontBike(u16 bspWheelIdx) : KartTire(bspWheelIdx) {} + +KartTireFrontBike::~KartTireFrontBike() = default; + +void KartTireFrontBike::createPhysics(u16 tireIdx) { + m_wheelPhysics = new WheelPhysics(tireIdx, 0); +} + +KartTireRearBike::KartTireRearBike(u16 bspWheelIdx) : KartTire(bspWheelIdx) {} + +KartTireRearBike::~KartTireRearBike() = default; + +void KartTireRearBike::createPhysics(u16 tireIdx) { + m_wheelPhysics = new WheelPhysics(tireIdx, 1); +} + +} // namespace Kart diff --git a/source/game/kart/KartTire.hh b/source/game/kart/KartTire.hh new file mode 100644 index 00000000..fa9f2b82 --- /dev/null +++ b/source/game/kart/KartTire.hh @@ -0,0 +1,40 @@ +#pragma once + +#include "game/kart/KartSuspensionPhysics.hh" + +namespace Kart { + +class KartTire { +public: + KartTire(u16 bspWheelIdx); + ~KartTire(); + + virtual void createPhysics(u16 tireIdx); + + void init(u16 tireIdx); + void initBsp(); + + WheelPhysics *wheelPhysics(); + +protected: + u16 m_bspWheelIdx; + WheelPhysics *m_wheelPhysics; +}; + +class KartTireFrontBike : public KartTire { +public: + KartTireFrontBike(u16 bspWheelIdx); + ~KartTireFrontBike(); + + void createPhysics(u16 tireIdx) override; +}; + +class KartTireRearBike : public KartTire { +public: + KartTireRearBike(u16 bspWheelIdx); + ~KartTireRearBike(); + + void createPhysics(u16 tireIdx) override; +}; + +} // namespace Kart diff --git a/source/game/scene/GameScene.hh b/source/game/scene/GameScene.hh index df861393..9b0813db 100644 --- a/source/game/scene/GameScene.hh +++ b/source/game/scene/GameScene.hh @@ -29,7 +29,7 @@ protected: private: struct Resource : private Abstract::Node { - s32 m_id; + s32 id; }; void initScene(); diff --git a/source/game/scene/RaceScene.cc b/source/game/scene/RaceScene.cc index 9373628a..07ae952d 100644 --- a/source/game/scene/RaceScene.cc +++ b/source/game/scene/RaceScene.cc @@ -54,7 +54,7 @@ void RaceScene::configure() { auto *commonArc = resMgr->load(0, nullptr); appendResource(commonArc, 0); - auto *courseArc = resMgr->load(raceCfg->raceScenario().m_course); + auto *courseArc = resMgr->load(raceCfg->raceScenario().course); appendResource(courseArc, 1); } diff --git a/source/game/system/DvdArchive.cc b/source/game/system/DvdArchive.cc index 8aaab955..be4c6d10 100644 --- a/source/game/system/DvdArchive.cc +++ b/source/game/system/DvdArchive.cc @@ -44,7 +44,7 @@ void *DvdArchive::getFile(const char *filename, size_t *size) const { void *file = m_archive->getFile(entryId, fileInfo); if (file && size) { - *size = fileInfo.m_length; + *size = fileInfo.length; } return file; diff --git a/source/game/system/RaceConfig.cc b/source/game/system/RaceConfig.cc index acb1a7e0..4e11721f 100644 --- a/source/game/system/RaceConfig.cc +++ b/source/game/system/RaceConfig.cc @@ -12,12 +12,12 @@ void RaceConfig::initRace() { // Normally we copy the menu scenario into the race scenario // There's no menu scenario in Kinoko, so instead we initialize values here // TODO: read from parameter file - m_raceScenario.m_playerCount = 1; - m_raceScenario.m_course = Course::SNES_Mario_Circuit_3; - Player &player = m_raceScenario.m_players[0]; - player.m_character = Character::Daisy; - player.m_vehicle = Vehicle::Mach_Bike; - player.m_type = Player::Type::Ghost; + m_raceScenario.playerCount = 1; + m_raceScenario.course = Course::SNES_Mario_Circuit_3; + Player &player = m_raceScenario.players[0]; + player.character = Character::Daisy; + player.vehicle = Vehicle::Mach_Bike; + player.type = Player::Type::Ghost; } RaceConfig *RaceConfig::CreateInstance() { @@ -41,14 +41,14 @@ RaceConfig::RaceConfig() = default; RaceConfig::~RaceConfig() = default; void RaceConfig::Scenario::init() { - m_playerCount = 0; - m_course = Course::GCN_Mario_Circuit; - - for (size_t i = 0; i < m_players.size(); ++i) { - Player &player = m_players[i]; - player.m_character = Character::Mario; - player.m_vehicle = Vehicle::Standard_Kart_M; - player.m_type = Player::Type::None; + playerCount = 0; + course = Course::GCN_Mario_Circuit; + + for (size_t i = 0; i < players.size(); ++i) { + Player &player = players[i]; + player.character = Character::Mario; + player.vehicle = Vehicle::Standard_Kart_M; + player.type = Player::Type::None; } } diff --git a/source/game/system/RaceConfig.hh b/source/game/system/RaceConfig.hh index a25be8bd..462892cd 100644 --- a/source/game/system/RaceConfig.hh +++ b/source/game/system/RaceConfig.hh @@ -15,9 +15,9 @@ public: None = 5, }; - Character m_character; - Vehicle m_vehicle; - Type m_type; + Character character; + Vehicle vehicle; + Type type; }; struct Scenario { @@ -29,9 +29,9 @@ public: void init(); - std::array m_players; - u8 m_playerCount; - Course m_course; + std::array players; + u8 playerCount; + Course course; }; void init(); diff --git a/source/game/system/RaceManager.cc b/source/game/system/RaceManager.cc index 8b0ce052..ae5184e3 100644 --- a/source/game/system/RaceManager.cc +++ b/source/game/system/RaceManager.cc @@ -23,8 +23,25 @@ void RaceManager::findKartStartPoint(EGG::Vector3f &pos, EGG::Vector3f &angles) } void RaceManager::calc() { - if (m_stage == Stage::Intro) { - ++m_introTimer; + constexpr u16 STAGE_INTRO_DURATION = 172; + constexpr u16 STAGE_COUNTDOWN_DURATION = 240; + + switch (m_stage) { + case Stage::Intro: + if (++m_introTimer >= STAGE_INTRO_DURATION) { + m_stage = Stage::Countdown; + } + break; + case Stage::Countdown: + if (++m_timer >= STAGE_COUNTDOWN_DURATION) { + m_stage = Stage::Race; + } + break; + case Stage::Race: + ++m_timer; + break; + default: + break; } } @@ -48,7 +65,7 @@ void RaceManager::DestroyInstance() { s_instance = nullptr; } -RaceManager::RaceManager() : m_stage(Stage::Intro), m_introTimer(0) {} +RaceManager::RaceManager() : m_stage(Stage::Intro), m_introTimer(0), m_timer(0) {} RaceManager::~RaceManager() = default; diff --git a/source/game/system/RaceManager.hh b/source/game/system/RaceManager.hh index 99110b34..e84fc4aa 100644 --- a/source/game/system/RaceManager.hh +++ b/source/game/system/RaceManager.hh @@ -29,7 +29,8 @@ private: ~RaceManager(); Stage m_stage; - u32 m_introTimer; + u16 m_introTimer; + u32 m_timer; static RaceManager *s_instance; }; diff --git a/source/test/TestDirector.cc b/source/test/TestDirector.cc index d6a06be2..4e5f0331 100644 --- a/source/test/TestDirector.cc +++ b/source/test/TestDirector.cc @@ -33,7 +33,7 @@ bool TestDirector::init() { bool TestDirector::calc() { // Check if we're out of frames - constexpr u16 TARGET_FRAME = 2; + constexpr u16 TARGET_FRAME = 172; assert(TARGET_FRAME <= m_frameCount); if (++m_currentFrame > TARGET_FRAME) { return false;