Skip to content

Fix incorrect vector normalisation with dot products smaller than epsilon #247

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions source/egg/math/Quat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void Quatf::setRPY(const Vector3f &rpy) {
/// @addr{0x8023A168}
/// @brief Scales the quaternion to a unit length.
void Quatf::normalise() {
f32 len = dot() > std::numeric_limits<f32>::epsilon() ? Mathf::sqrt(dot()) : 0.0f;
f32 len = squaredNorm() > std::numeric_limits<f32>::epsilon() ? norm() : 0.0f;

if (len != 0.0f) {
f32 inv = 1.0f / len;
Expand Down Expand Up @@ -112,8 +112,12 @@ Quatf Quatf::slerpTo(const Quatf &q1, f32 t) const {

/// @addr{0x8023A138}
/// @brief Computes \f$this \cdot this = w^2 + x^2 + y^2 + z^2\f$
f32 Quatf::dot() const {
return w * w + v.dot();
f32 Quatf::squaredNorm() const {
return w * w + v.squaredLength();
}

f32 Quatf::norm() const {
return Mathf::sqrt(squaredNorm());
}

/// @brief Computes \f$this \cdot rhs = w \times rhs.w + x \times rhs.x + y \times rhs.y + z \times
Expand Down
3 changes: 2 additions & 1 deletion source/egg/math/Quat.hh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ struct Quatf {
Vector3f rotateVector(const Vector3f &vec) const;
Vector3f rotateVectorInv(const Vector3f &vec) const;
Quatf slerpTo(const Quatf &q2, f32 t) const;
f32 dot() const;
f32 squaredNorm() const;
f32 norm() const;
f32 dot(const Quatf &q) const;
void setAxisRotation(f32 angle, const Vector3f &axis);
Quatf multSwap(const Vector3f &v) const;
Expand Down
14 changes: 8 additions & 6 deletions source/egg/math/Vector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void Vector2f::read(Stream &stream) {
}

/// @brief The dot product between the vector and itself.
f32 Vector3f::dot() const {
f32 Vector3f::squaredLength() const {
return x * x + y * y + z * z;
}

Expand Down Expand Up @@ -75,16 +75,18 @@ Vector3f Vector3f::cross(const Vector3f &rhs) const {

/// @brief The square root of the vector's dot product.
f32 Vector3f::length() const {
return Mathf::sqrt(dot());
return Mathf::sqrt(squaredLength());
}

/// @addr{0x80243ADC}
/// @brief Normalizes the vector and returns the original length.
/// @return (optional) The length of the vector before normalisation.
f32 Vector3f::normalise() {
f32 len = length();
if (std::numeric_limits<f32>::epsilon() < dot()) {
*this = *this * (1.0f / len);
f32 len = 0.0f;

if (squaredLength() > std::numeric_limits<f32>::epsilon()) {
len = length();
*this *= (1.0f / len);
}

return len;
Expand Down Expand Up @@ -134,7 +136,7 @@ std::pair<Vector3f, Vector3f> Vector3f::projAndRej(const Vector3f &rhs) const {
/// @brief The square of the distance between two vectors.
f32 Vector3f::sqDistance(const Vector3f &rhs) const {
const EGG::Vector3f diff = *this - rhs;
return diff.dot();
return diff.squaredLength();
}

/// @addr{0x8019ADE0}
Expand Down
2 changes: 1 addition & 1 deletion source/egg/math/Vector.hh
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct Vector3f {
}

[[nodiscard]] Vector3f cross(const EGG::Vector3f &rhs) const;
[[nodiscard]] f32 dot() const;
[[nodiscard]] f32 squaredLength() const;
[[nodiscard]] f32 dot(const EGG::Vector3f &rhs) const;
[[nodiscard]] f32 ps_dot() const;
[[nodiscard]] f32 ps_dot(const EGG::Vector3f &rhs) const;
Expand Down
8 changes: 4 additions & 4 deletions source/game/field/ObjectCollisionBase.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ bool ObjectCollisionBase::check(ObjectCollisionBase &rhs, EGG::Vector3f &distanc
return true;
}

max = EGG::Mathf::sqrt(D.dot());
max = D.length();

if (max2 - max * max <= std::numeric_limits<f32>::epsilon() * max2) {
FUN_808350e4(state, D);
getNearestPoint(state, state.m_flags, v0, v1);
f32 len = EGG::Mathf::sqrt(D.dot());
f32 len = D.length();
v0 -= D * (getBoundingRadius() / len);
v1 += D * (rhs.getBoundingRadius() / len);

Expand Down Expand Up @@ -123,7 +123,7 @@ void ObjectCollisionBase::FUN_808350e4(GJKState &state, EGG::Vector3f &v) const
EGG::Vector3f tmp = EGG::Vector3f::zero;
getNearestPoint(state, mask, tmp);

sqLen = tmp.dot();
sqLen = tmp.squaredLength();
if (sqLen < min) {
state.m_flags = mask;
v = tmp;
Expand Down Expand Up @@ -240,7 +240,7 @@ void ObjectCollisionBase::calcSimplex(GJKState &state) const {
}

state.m_scales[state.m_mask][idx] = 1.0f;
s_dotProductCache[idx][idx] = state.m_s[idx].dot();
s_dotProductCache[idx][idx] = state.m_s[idx].squaredLength();

for (u32 i = 0, iMask = 1; i < 4; ++i, iMask *= 2) {
if ((state.m_flags & iMask) == 0) {
Expand Down
4 changes: 2 additions & 2 deletions source/game/kart/KartCollide.cc
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ void KartCollide::calcSideCollision(CollisionData &collisionData, Hitbox &hitbox
if (Field::CollisionDirector::Instance()->checkSphereCachedPartial(effectivePos,
hitbox.lastPos(), KCL_TYPE_DRIVER_WALL, &tempColInfo, nullptr,
hitbox.radius())) {
tangents[i] = colInfo->tangentOff.dot();
tangents[i] = colInfo->tangentOff.squaredLength();
}
}

Expand Down Expand Up @@ -727,7 +727,7 @@ void KartCollide::applySomeFloorMoment(f32 down, f32 rate, CollisionGroup *hitbo
crossVec = colData.floorNrm.cross(negSpeed);
crossVec = crossVec.cross(colData.floorNrm);

if (std::numeric_limits<f32>::epsilon() >= crossVec.dot()) {
if (std::numeric_limits<f32>::epsilon() >= crossVec.squaredLength()) {
return;
}

Expand Down
10 changes: 5 additions & 5 deletions source/game/kart/KartDynamics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ void KartDynamics::calc(f32 dt, f32 maxSpeed, bool /*air*/) {
EGG::Vector3f playerBackHoriz = playerBack;
playerBackHoriz.y = 0.0f;

if (std::numeric_limits<f32>::epsilon() < playerBackHoriz.dot()) {
if (std::numeric_limits<f32>::epsilon() < playerBackHoriz.squaredLength()) {
playerBackHoriz.normalise();
const auto [proj, rej] = m_extVel.projAndRej(playerBackHoriz);
const EGG::Vector3f &speedBack = proj;
m_extVel = rej;

f32 norm = speedBack.dot();
f32 norm = speedBack.squaredLength();
if (std::numeric_limits<f32>::epsilon() < norm) {
norm = EGG::Mathf::sqrt(norm);
} else {
Expand Down Expand Up @@ -147,10 +147,10 @@ void KartDynamics::calc(f32 dt, f32 maxSpeed, bool /*air*/) {

EGG::Vector3f angVelSum = m_angVel2 + m_angVel1 + m_angVel0Factor * m_angVel0;

if (std::numeric_limits<f32>::epsilon() < angVelSum.dot()) {
if (std::numeric_limits<f32>::epsilon() < angVelSum.squaredLength()) {
m_mainRot += m_mainRot.multSwap(angVelSum) * (dt * 0.5f);

if (EGG::Mathf::abs(m_mainRot.dot()) < std::numeric_limits<f32>::epsilon()) {
if (EGG::Mathf::abs(m_mainRot.norm()) < std::numeric_limits<f32>::epsilon()) {
m_mainRot = EGG::Quatf::ident;
} else {
m_mainRot.normalise();
Expand All @@ -161,7 +161,7 @@ void KartDynamics::calc(f32 dt, f32 maxSpeed, bool /*air*/) {
stabilize();
}

if (EGG::Mathf::abs(m_mainRot.dot()) < std::numeric_limits<f32>::epsilon()) {
if (EGG::Mathf::abs(m_mainRot.norm()) < std::numeric_limits<f32>::epsilon()) {
m_mainRot = EGG::Quatf::ident;
} else {
m_mainRot.normalise();
Expand Down
2 changes: 1 addition & 1 deletion source/game/kart/KartJump.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ void KartJump::setAngle(const EGG::Vector3f &left) {

f32 vel1YDot = m_move->vel1Dir().dot(EGG::Vector3f::ey);
EGG::Vector3f vel1YCross = m_move->vel1Dir().cross(EGG::Vector3f::ey);
f32 vel1YCrossMag = EGG::Mathf::sqrt(vel1YCross.dot());
f32 vel1YCrossMag = vel1YCross.length();
f32 pitch = EGG::Mathf::abs(EGG::Mathf::atan2(vel1YCrossMag, vel1YDot));
f32 angle = 90.0f - (pitch * RAD2DEG);
u32 weightClass = static_cast<u32>(param()->stats().weightClass);
Expand Down
10 changes: 5 additions & 5 deletions source/game/kart/KartMove.cc
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ void KartMove::calcDirs() {

EGG::Vector3f dirDiff = local_b8 - m_dir;

if (dirDiff.dot() <= std::numeric_limits<f32>::epsilon()) {
if (dirDiff.squaredLength() <= std::numeric_limits<f32>::epsilon()) {
m_dir = local_b8;
m_dirDiff.setZero();
} else {
Expand Down Expand Up @@ -546,7 +546,7 @@ void KartMove::calcDirs() {
if (m_hasLandingDir) {
f32 dot = m_dir.dot(m_landingDir);
EGG::Vector3f cross = m_dir.cross(m_landingDir);
f32 crossDot = EGG::Mathf::sqrt(cross.dot());
f32 crossDot = cross.length();
f32 angle = EGG::Mathf::atan2(crossDot, dot);
angle = EGG::Mathf::abs(angle);

Expand Down Expand Up @@ -1577,7 +1577,7 @@ void KartMove::calcDive() {
EGG::Vector3f forwardRotated = dynamics()->mainRot().rotateVector(EGG::Vector3f::ez);
f32 upDotTop = m_up.dot(topRotated);
EGG::Vector3f upCrossTop = m_up.cross(topRotated);
f32 crossNorm = EGG::Mathf::sqrt(upCrossTop.dot());
f32 crossNorm = upCrossTop.length();
f32 angle = EGG::Mathf::abs(EGG::Mathf::atan2(crossNorm, upDotTop));

f32 fVar1 = angle * RAD2DEG - 20.0f;
Expand Down Expand Up @@ -1654,7 +1654,7 @@ void KartMove::calcVehicleRotation(f32 turn) {
EGG::Vector3f frontSpeed = velocity().rej(front).perpInPlane(m_up, false);
f32 magnitude = tiltMagnitude;

if (frontSpeed.dot() > std::numeric_limits<f32>::epsilon()) {
if (frontSpeed.squaredLength() > std::numeric_limits<f32>::epsilon()) {
magnitude = frontSpeed.length();

if (front.z * frontSpeed.x - front.x * frontSpeed.z > 0.0f) {
Expand Down Expand Up @@ -2418,7 +2418,7 @@ void KartMoveBike::calcVehicleRotation(f32 turn) {
scalar = std::min(1.0f, scalar);
top = scalar * m_up + (1.0f - scalar) * EGG::Vector3f::ey;

if (std::numeric_limits<f32>::epsilon() < top.dot()) {
if (std::numeric_limits<f32>::epsilon() < top.squaredLength()) {
top.normalise();
}
}
Expand Down