mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-04-29 03:54:10 +00:00
488 lines
12 KiB
C++
488 lines
12 KiB
C++
#include "Library/Math/MathUtil.h"
|
|
|
|
#include <math/seadMathCalcCommon.h>
|
|
#include <prim/seadBitUtil.h>
|
|
#include <random/seadGlobalRandom.h>
|
|
|
|
namespace al {
|
|
f32 calcAngleRadian(const sead::Vector3f& a, const sead::Vector3f& b) {
|
|
f32 dot = a.dot(b);
|
|
sead::Vector3f cross;
|
|
cross.setCross(a, b);
|
|
return sead::Mathf::atan2(cross.length(), dot);
|
|
}
|
|
|
|
f32 calcAngleDegree(const sead::Vector3f& a, const sead::Vector3f& b) {
|
|
return sead::Mathf::rad2deg(calcAngleRadian(a, b));
|
|
}
|
|
|
|
f32 calcAngleDegree(const sead::Vector2f& a, const sead::Vector2f& b) {
|
|
if (isNearZero(a, 0.001) || isNearZero(b, 0.001))
|
|
return 0.0f;
|
|
|
|
return sead::Mathf::rad2deg(sead::Mathf::atan2(a.cross(b), a.dot(b)));
|
|
}
|
|
|
|
bool isNearZero(const sead::Vector2f& vec, f32 tolerance) {
|
|
return vec.squaredLength() < tolerance * tolerance;
|
|
}
|
|
|
|
bool tryCalcAngleDegree(f32* out, const sead::Vector3f& a, const sead::Vector3f& b) {
|
|
if (isNearZero(a, 0.001) || isNearZero(b, 0.001))
|
|
return false;
|
|
|
|
*out = calcAngleDegree(a, b);
|
|
return true;
|
|
}
|
|
|
|
bool isNearZero(const sead::Vector3f& vec, f32 tolerance) {
|
|
return vec.squaredLength() < tolerance * tolerance;
|
|
}
|
|
|
|
f32 calcAngleOnPlaneRadian(const sead::Vector3f& a, const sead::Vector3f& b,
|
|
const sead::Vector3f& vertical) {
|
|
sead::Vector3f planeA;
|
|
verticalizeVec(&planeA, vertical, a);
|
|
sead::Vector3f planeB;
|
|
verticalizeVec(&planeB, vertical, b);
|
|
|
|
f32 dot = planeA.dot(planeB);
|
|
sead::Vector3f cross;
|
|
cross.setCross(planeA, planeB);
|
|
f32 angle = sead::Mathf::atan2(cross.length(), dot);
|
|
|
|
return vertical.dot(cross) < 0.0f ? -angle : angle;
|
|
}
|
|
|
|
/**
|
|
* Takes the plane perpendicular to unit vector `vertical`, projects `vec` onto it, and
|
|
* stores the result in `out`. The effect is that `vec` and `out` will look equal
|
|
* if looking in the direction of `vertical`.
|
|
*/
|
|
void verticalizeVec(sead::Vector3f* out, const sead::Vector3f& vertical,
|
|
const sead::Vector3f& vec) {
|
|
out->setScaleAdd(-vertical.dot(vec), vertical, vec);
|
|
}
|
|
|
|
f32 calcAngleOnPlaneDegree(const sead::Vector3f& a, const sead::Vector3f& b,
|
|
const sead::Vector3f& vertical) {
|
|
return sead::Mathf::rad2deg(calcAngleOnPlaneRadian(a, b, vertical));
|
|
}
|
|
|
|
f32 calcAngleOnPlaneDegreeOrZero(const sead::Vector3f& a, const sead::Vector3f& b,
|
|
const sead::Vector3f& vertical) {
|
|
f32 angle = 0.0f;
|
|
if (!tryCalcAngleOnPlaneDegree(&angle, a, b, vertical))
|
|
return 0.0f;
|
|
|
|
return angle;
|
|
}
|
|
|
|
s32 calcAngleSignOnPlane(const sead::Vector3f& a, const sead::Vector3f& b,
|
|
const sead::Vector3f& vertical) {
|
|
sead::Vector3f planeA;
|
|
verticalizeVec(&planeA, vertical, a);
|
|
sead::Vector3f planeB;
|
|
verticalizeVec(&planeB, vertical, b);
|
|
|
|
sead::Vector3f cross;
|
|
cross.setCross(planeA, planeB);
|
|
const f32 angle = vertical.dot(cross);
|
|
|
|
if (angle > 0.0f)
|
|
return 1;
|
|
if (angle < 0.0f)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void normalize(sead::Vector2f* out, const sead::Vector2f& vec) {
|
|
*out = vec;
|
|
normalize(out);
|
|
}
|
|
|
|
void normalize(sead::Vector3f* out, const sead::Vector3f& vec) {
|
|
*out = vec;
|
|
normalize(out);
|
|
}
|
|
|
|
bool tryNormalizeOrZero(sead::Vector3f* vec) {
|
|
if (isNearZero(*vec, 0.001f)) {
|
|
*vec = {0.0f, 0.0f, 0.0f};
|
|
return false;
|
|
}
|
|
|
|
normalize(vec);
|
|
return true;
|
|
}
|
|
|
|
bool tryNormalizeOrZero(sead::Vector3f* out, const sead::Vector3f& vec) {
|
|
*out = vec;
|
|
return tryNormalizeOrZero(out);
|
|
}
|
|
|
|
// TODO: Rename parameters here and in header
|
|
bool isNearAngleDegreeHV(const sead::Vector3f& a, const sead::Vector3f& b, const sead::Vector3f& c,
|
|
f32 d, f32 e) {
|
|
return isNearAngleRadianHV(a, b, c, sead::Mathf::deg2rad(d), sead::Mathf::deg2rad(e));
|
|
}
|
|
|
|
bool isNear(f32 value, f32 target, f32 tolerance) {
|
|
return sead::Mathf::abs(value - target) < sead::Mathf::abs(tolerance);
|
|
}
|
|
|
|
bool isNear(const sead::Vector2f& value, const sead::Vector2f& target, f32 tolerance) {
|
|
return (value - target).length() <= tolerance;
|
|
}
|
|
|
|
bool isNear(const sead::Vector3f& value, const sead::Vector3f& target, f32 tolerance) {
|
|
return (value - target).length() <= tolerance;
|
|
}
|
|
|
|
bool isNear(const sead::Color4f& value, const sead::Color4f& target, f32 tolerance) {
|
|
return sead::Mathf::abs(value.r - target.r) < tolerance &&
|
|
sead::Mathf::abs(value.g - target.g) < tolerance &&
|
|
sead::Mathf::abs(value.b - target.b) < tolerance &&
|
|
sead::Mathf::abs(value.a - target.a) < tolerance;
|
|
}
|
|
|
|
bool isNearZero(f32 value, f32 tolerance) {
|
|
return sead::Mathf::abs(value) < tolerance;
|
|
}
|
|
|
|
bool isNearZero(const sead::Matrix34f& value, f32 tolerance) {
|
|
sead::Vector3f vec;
|
|
|
|
value.getBase(vec, 0);
|
|
if (isNearZero(vec, tolerance))
|
|
return true;
|
|
value.getBase(vec, 1);
|
|
if (isNearZero(vec, tolerance))
|
|
return true;
|
|
value.getBase(vec, 2);
|
|
if (isNearZero(vec, tolerance))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isNearZeroOrGreater(f32 value, f32 tolerance) {
|
|
return value >= 0.0f || isNearZero(value, tolerance);
|
|
}
|
|
|
|
bool isNearZeroOrLess(f32 value, f32 tolerance) {
|
|
return value <= 0.0f || isNearZero(value, tolerance);
|
|
}
|
|
|
|
bool isExistNearZeroVal(const sead::Vector3f& vec, f32 tolerance) {
|
|
return isNearZero(vec.x, tolerance) || isNearZero(vec.y, tolerance) ||
|
|
isNearZero(vec.z, tolerance);
|
|
}
|
|
|
|
bool isNormalize(const sead::Vector3f& vec, f32 tolerance) {
|
|
return sead::Mathf::abs(1.0f - vec.length()) <= tolerance;
|
|
}
|
|
|
|
bool isParallelDirection(const sead::Vector2f& a, const sead::Vector2f& b, f32 tolerance) {
|
|
return !(sead::Mathf::abs(a.cross(b)) > tolerance);
|
|
}
|
|
|
|
bool isNearDirection(const sead::Vector2f& a, const sead::Vector2f& b, f32 tolerance) {
|
|
if (a.dot(b) < 0.0f)
|
|
return false;
|
|
|
|
return isParallelDirection(a, b, tolerance);
|
|
}
|
|
|
|
bool isInRange(s32 x, s32 a, s32 b) {
|
|
return (b < a) ? (a >= x && x >= b) : (b >= x && x >= a);
|
|
}
|
|
|
|
bool isInRange(f32 x, f32 a, f32 b) {
|
|
if (b < a) {
|
|
if (x < b || a < x)
|
|
return false;
|
|
return true;
|
|
} else {
|
|
if (x < a || b < x)
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void normalize(sead::Vector2f* vec) {
|
|
vec->normalize();
|
|
}
|
|
|
|
void normalize(sead::Vector3f* vec) {
|
|
vec->normalize();
|
|
}
|
|
|
|
bool tryNormalizeOrZero(sead::Vector2f* vec) {
|
|
if (isNearZero(*vec, 0.001f)) {
|
|
*vec = {0.0f, 0.0f};
|
|
return false;
|
|
}
|
|
|
|
normalize(vec);
|
|
return true;
|
|
}
|
|
|
|
bool tryNormalizeOrZero(sead::Vector2f* out, const sead::Vector2f& vec) {
|
|
*out = vec;
|
|
return tryNormalizeOrZero(out);
|
|
}
|
|
|
|
bool tryNormalizeOrDirZ(sead::Vector3f* vec) {
|
|
if (isNearZero(*vec, 0.001f)) {
|
|
// mismatches if this isn't set twice
|
|
vec->set(0.0f, 0.0f, 0.0f);
|
|
vec->set(sead::Vector3f::ez);
|
|
return false;
|
|
}
|
|
|
|
normalize(vec);
|
|
return true;
|
|
}
|
|
|
|
bool tryNormalizeOrDirZ(sead::Vector3f* out, const sead::Vector3f& vec) {
|
|
*out = vec;
|
|
return tryNormalizeOrDirZ(out);
|
|
}
|
|
|
|
void setLength(sead::Vector3f* vec, f32 length) {
|
|
f32 curLen = vec->length();
|
|
if (curLen > 0.0f) {
|
|
f32 scale = length / curLen;
|
|
*vec *= scale;
|
|
}
|
|
}
|
|
|
|
void setProjectionLength(sead::Vector3f* out, const sead::Vector3f& vec, f32 length) {
|
|
f32 scale = length / sead::Mathf::abs(vec.dot(*out));
|
|
*out *= scale;
|
|
}
|
|
|
|
bool limitLength(sead::Vector2f* out, const sead::Vector2f& vec, f32 limit) {
|
|
f32 len = vec.length();
|
|
if (len > limit) {
|
|
f32 invLen = limit / len;
|
|
out->setScale(vec, invLen);
|
|
return true;
|
|
} else {
|
|
out->set(vec);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool limitLength(sead::Vector3f* out, const sead::Vector3f& vec, f32 limit) {
|
|
f32 len = vec.length();
|
|
if (len > limit) {
|
|
f32 invLen = limit / len;
|
|
out->setScale(vec, invLen);
|
|
return true;
|
|
} else {
|
|
out->set(vec);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
f32 normalizeAbs(f32 x, f32 min, f32 max) {
|
|
if (x >= 0)
|
|
return normalize(x, min, max);
|
|
else
|
|
return -normalize(-x, min, max);
|
|
}
|
|
|
|
f32 normalize(f32 x, f32 min, f32 max) {
|
|
if (sead::Mathf::abs(max - min) < 0.001f) {
|
|
if (x < min)
|
|
return 0.0f;
|
|
else
|
|
return 1.0f;
|
|
}
|
|
|
|
f32 clamped = sead::Mathf::clamp(x, min, max);
|
|
return (clamped - min) / (max - min);
|
|
}
|
|
|
|
f32 normalize(s32 x, s32 min, s32 max) {
|
|
if (x <= min)
|
|
return 0.0f;
|
|
if (max - min <= 0) {
|
|
if (x < min)
|
|
return 0.0f;
|
|
else
|
|
return 1.0f;
|
|
}
|
|
|
|
return (static_cast<f32>(clamp(x, min, max) - min) / static_cast<f32>(max - min));
|
|
}
|
|
|
|
f32 sign(f32 x) {
|
|
if (x < 0.0f)
|
|
return -1.0f;
|
|
if (x > 0.0f)
|
|
return 1.0f;
|
|
return x;
|
|
}
|
|
|
|
s32 sign(s32 x) {
|
|
if (x < 0)
|
|
return -1;
|
|
if (x > 0)
|
|
return 1;
|
|
return x;
|
|
}
|
|
|
|
f32 cubeRoot(f32 x) {
|
|
f32 onethird = 1.0f / 3.0f;
|
|
|
|
u32 i = 0x54a0fc86 - sead::BitUtil::bitCast<u32>(x) / 3;
|
|
f32 y = sead::BitUtil::bitCast<f32>(i);
|
|
|
|
y = y * onethird * (4.0f - x * y * y * y);
|
|
y = y * onethird * (4.0f - x * y * y * y);
|
|
y = y * onethird * (4.0f - x * y * y * y);
|
|
return x * y * y;
|
|
}
|
|
|
|
f32 easeIn(f32 t) {
|
|
return (((t * -0.5f) + 1.5f) * t) * t;
|
|
}
|
|
|
|
f32 easeOut(f32 t) {
|
|
return (((t * -0.5f) * t) + 1.5f) * t;
|
|
}
|
|
|
|
f32 easeInOut(f32 t) {
|
|
return (((t * -2.0f) + 3.0f) * t) * t;
|
|
}
|
|
|
|
f32 squareIn(f32 t) {
|
|
return t * t;
|
|
}
|
|
|
|
f32 squareOut(f32 t) {
|
|
return (2.0f - t) * t;
|
|
}
|
|
|
|
f32 powerIn(f32 t, f32 exp) {
|
|
return sead::Mathf::pow(t, exp);
|
|
}
|
|
|
|
f32 powerOut(f32 t, f32 exp) {
|
|
return sead::Mathf::pow(t, 1.0 / exp);
|
|
}
|
|
|
|
f32 logarithmIn(f32 t, f32 base) {
|
|
f32 base1 = base + sead::Mathf::epsilon();
|
|
f32 a = powf(base1 + 0.0f, 1.0 - t);
|
|
f32 b = powf(base1 + 1.0f, t);
|
|
return a * b - base1;
|
|
}
|
|
|
|
f32 logarithmOut(f32 t, f32 base) {
|
|
return 1.0f - logarithmIn(1.0f - t, base);
|
|
}
|
|
|
|
f32 exponentIn(f32 t, f32 exp) {
|
|
return t * exp2f(exp * (t - 1.0f));
|
|
}
|
|
|
|
f32 exponentOut(f32 t, f32 exp) {
|
|
return 1.0f - exponentIn(1.0f - t, exp);
|
|
}
|
|
|
|
f32 hermiteRate(f32 t, f32 m0, f32 m1) {
|
|
return hermite(0.0f, m0, 1.0f, m1, t);
|
|
}
|
|
|
|
f32 lerpValue(f32 a, f32 b, f32 t) {
|
|
t = sead::Mathf::clamp(t, 0.0f, 1.0f);
|
|
return (a * (1.0f - t)) + (t * b);
|
|
}
|
|
|
|
/**
|
|
* Interpolates between `y0` and `y1` as `t` goes from 0.0 to 1.0. This interpolation is defined by
|
|
* `m0` and `m1`, which are the rates of change of `t` at the points `y0` and `y1` respectively.
|
|
*/
|
|
f32 hermite(f32 y0, f32 m0, f32 y1, f32 m1, f32 t) {
|
|
f32 coef_m1 = t * (t * t - t);
|
|
f32 coef_y1 = t * t + -2.0f * coef_m1;
|
|
f32 coef_m0 = coef_m1 - (t * t - t);
|
|
return y0 - coef_y1 * y0 + coef_y1 * y1 + coef_m0 * m0 + coef_m1 * m1;
|
|
}
|
|
|
|
f32 hermite(f32 y0, f32 m0, f32 y1, f32 m1, f32 t, f32 width) {
|
|
t *= 1.0f / width;
|
|
f32 a1 = y0 - y1;
|
|
f32 a2 = t - 1.0f;
|
|
f32 a3 = t + t - 3.0f;
|
|
return y0 + (a1 * a3) * t * t + (t * a2) * (t * m1 + a2 * m0);
|
|
}
|
|
|
|
f32 calcFourthOrderRate(f32 t, f32 scale) {
|
|
return ((scale + -3.0f) * t * t + (scale * -2.0f + 4.0f) * t + scale) * t * t;
|
|
}
|
|
|
|
f32 getRandom() {
|
|
u32 random = (sead::GlobalRandom::instance()->getU32() >> 9) | 0x3F800000;
|
|
return (*reinterpret_cast<f32*>(&random)) - 1;
|
|
}
|
|
|
|
f32 getRandom(f32 factor) {
|
|
return getRandom(0.0f, factor);
|
|
}
|
|
|
|
f32 getRandom(f32 min, f32 max) {
|
|
return (getRandom() * (max - min)) + min;
|
|
}
|
|
|
|
s32 getRandom(s32 factor) {
|
|
return getRandom(0, factor);
|
|
}
|
|
|
|
s32 getRandom(s32 min, s32 max) {
|
|
return (s32)getRandom((f32)min, (f32)max);
|
|
}
|
|
|
|
f32 getRandomDegree() {
|
|
return getRandom(360.0f);
|
|
}
|
|
|
|
f32 getRandomRadian() {
|
|
return getRandom(6.2832f);
|
|
}
|
|
|
|
void getRandomVector(sead::Vector3f* vec, f32 factor) {
|
|
f32 x = (getRandom() * (factor + factor)) - factor;
|
|
f32 y = (getRandom() * (factor + factor)) - factor;
|
|
f32 z = (getRandom() * (factor + factor)) - factor;
|
|
vec->x = x;
|
|
vec->y = y;
|
|
vec->z = z;
|
|
}
|
|
|
|
void getRandomDir(sead::Vector3f* vec) {
|
|
getRandomVector(vec, 10.0f);
|
|
while (vec->dot(*vec) < 0.000001f) {
|
|
*vec = {0.0f, 0.0f, 0.0f};
|
|
getRandomVector(vec, 10.0f);
|
|
}
|
|
vec->normalize();
|
|
}
|
|
|
|
void calcParabolicFunctionParam(f32* gravity, f32* initialVelY, f32 maxHeight,
|
|
f32 verticalDistance) {
|
|
f32 maxHeightSign = sign(maxHeight);
|
|
|
|
f32 maxHeightAdjusted =
|
|
sead::Mathf::sqrt(sead::Mathf::clampMin((maxHeight - verticalDistance) * maxHeight, 0.0));
|
|
*initialVelY = 2 * ((maxHeightSign * maxHeightAdjusted) + maxHeight);
|
|
*gravity = verticalDistance - *initialVelY;
|
|
}
|
|
|
|
} // namespace al
|