A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
Math.hh
1#pragma once
2
3#include <Common.hh>
4
5#include <cmath>
6
7static constexpr f32 F_PI = 3.1415927f;
8static constexpr f32 F_TAU = 6.283185f;
9static constexpr f32 DEG2RAD = 0.017453292f;
10static constexpr f32 DEG2RAD360 = 0.034906585f;
11static constexpr f32 RAD2DEG = 57.2957795f;
12static constexpr f32 DEG2FIDX = 256.0f / 360.0f;
13static constexpr f32 RAD2FIDX = 128.0f / F_PI;
14static constexpr f32 FIDX2RAD = F_PI / 128.0f;
15
17namespace EGG::Mathf {
18
19[[nodiscard]] f32 frsqrt(f32 x);
20
22[[nodiscard]] static inline f32 sqrt(f32 x) {
23 return x > 0.0f ? frsqrt(x) * x : 0.0f;
24}
25
26[[nodiscard]] f32 SinFIdx(f32 fidx);
27[[nodiscard]] f32 CosFIdx(f32 fidx);
28[[nodiscard]] f32 AtanFIdx_(f32 fidx);
29[[nodiscard]] f32 Atan2FIdx(f32 x, f32 y);
30
33[[nodiscard]] static inline f32 sin(f32 x) {
34 return SinFIdx(x * RAD2FIDX);
35}
36
39[[nodiscard]] static inline f32 cos(f32 x) {
40 return CosFIdx(x * RAD2FIDX);
41}
42
44[[nodiscard]] static inline f32 acos(f32 x) {
45 return ::acosl(x);
46}
47
49[[nodiscard]] static inline f32 atan2(f32 y, f32 x) {
50 return Atan2FIdx(y, x) * FIDX2RAD;
51}
52
53[[nodiscard]] static inline f32 abs(f32 x) {
54 return std::abs(x);
55}
56
60[[nodiscard]] static inline f64 force25Bit(f64 x) {
61 u64 bits = std::bit_cast<u64>(x);
62 bits = (bits & 0xfffffffff8000000ULL) + (bits & 0x8000000);
63 return std::bit_cast<f64>(bits);
64}
65
68[[nodiscard]] static inline f32 fma(f32 x, f32 y, f32 z) {
69 return static_cast<f32>(
70 static_cast<f64>(x) * force25Bit(static_cast<f64>(y)) + static_cast<f64>(z));
71}
72
75[[nodiscard]] static inline f32 fms(f32 x, f32 y, f32 z) {
76 return static_cast<f32>(
77 static_cast<f64>(x) * force25Bit(static_cast<f64>(y)) - static_cast<f64>(z));
78}
79
80// frsqrte matching courtesy of Geotale, with reference to https://achurch.org/cpu-tests/ppc750cl.s
81struct BaseAndDec {
82 u64 base;
83 s64 dec;
84};
85
86union c64 {
87 constexpr c64(const f64 p) {
88 f = p;
89 }
90
91 constexpr c64(const u64 p) {
92 u = p;
93 }
94
95 u64 u;
96 f64 f;
97};
98
99static constexpr u64 EXPONENT_SHIFT_F64 = 52;
100static constexpr u64 MANTISSA_MASK_F64 = 0x000fffffffffffffULL;
101static constexpr u64 EXPONENT_MASK_F64 = 0x7ff0000000000000ULL;
102static constexpr u64 SIGN_MASK_F64 = 0x8000000000000000ULL;
103
104static constexpr std::array<BaseAndDec, 32> RSQRTE_TABLE = {{
105 {0x69fa000000000ULL, -0x15a0000000LL},
106 {0x5f2e000000000ULL, -0x13cc000000LL},
107 {0x554a000000000ULL, -0x1234000000LL},
108 {0x4c30000000000ULL, -0x10d4000000LL},
109 {0x43c8000000000ULL, -0x0f9c000000LL},
110 {0x3bfc000000000ULL, -0x0e88000000LL},
111 {0x34b8000000000ULL, -0x0d94000000LL},
112 {0x2df0000000000ULL, -0x0cb8000000LL},
113 {0x2794000000000ULL, -0x0bf0000000LL},
114 {0x219c000000000ULL, -0x0b40000000LL},
115 {0x1bfc000000000ULL, -0x0aa0000000LL},
116 {0x16ae000000000ULL, -0x0a0c000000LL},
117 {0x11a8000000000ULL, -0x0984000000LL},
118 {0x0ce6000000000ULL, -0x090c000000LL},
119 {0x0862000000000ULL, -0x0898000000LL},
120 {0x0416000000000ULL, -0x082c000000LL},
121 {0xffe8000000000ULL, -0x1e90000000LL},
122 {0xf0a4000000000ULL, -0x1c00000000LL},
123 {0xe2a8000000000ULL, -0x19c0000000LL},
124 {0xd5c8000000000ULL, -0x17c8000000LL},
125 {0xc9e4000000000ULL, -0x1610000000LL},
126 {0xbedc000000000ULL, -0x1490000000LL},
127 {0xb498000000000ULL, -0x1330000000LL},
128 {0xab00000000000ULL, -0x11f8000000LL},
129 {0xa204000000000ULL, -0x10e8000000LL},
130 {0x9994000000000ULL, -0x0fe8000000LL},
131 {0x91a0000000000ULL, -0x0f08000000LL},
132 {0x8a1c000000000ULL, -0x0e38000000LL},
133 {0x8304000000000ULL, -0x0d78000000LL},
134 {0x7c48000000000ULL, -0x0cc8000000LL},
135 {0x75e4000000000ULL, -0x0c28000000LL},
136 {0x6fd0000000000ULL, -0x0b98000000LL},
137}};
138
139[[nodiscard]] static inline f64 frsqrte(const f64 val) {
140 c64 bits(val);
141
142 u64 mantissa = bits.u & MANTISSA_MASK_F64;
143 s64 exponent = bits.u & EXPONENT_MASK_F64;
144 bool sign = (bits.u & SIGN_MASK_F64) != 0;
145
146 // Handle 0 case
147 if (mantissa == 0 && exponent == 0) {
148 return std::copysign(std::numeric_limits<f64>::infinity(), bits.f);
149 }
150
151 // Handle NaN-like
152 if (exponent == EXPONENT_MASK_F64) {
153 if (mantissa == 0) {
154 return sign ? std::numeric_limits<f64>::quiet_NaN() : 0.0;
155 }
156
157 return val;
158 }
159
160 // Handle negative inputs
161 if (sign) {
162 return std::numeric_limits<f64>::quiet_NaN();
163 }
164
165 if (exponent == 0) {
166 // Shift so one bit goes to where the exponent would be,
167 // then clear that bit to mimick a not-subnormal number!
168 // Aka, if there are 12 leading zeroes, shift left once
169 u32 shift = std::countl_zero(mantissa) - static_cast<u32>(63 - EXPONENT_SHIFT_F64);
170
171 mantissa <<= shift;
172 mantissa &= MANTISSA_MASK_F64;
173 // The shift is subtracted by 1 because denormals by default
174 // are offset by 1 (exponent 0 doesn't have implied 1 bit)
175 exponent -= static_cast<s64>(shift - 1) << EXPONENT_SHIFT_F64;
176 }
177
178 // In reality this doesn't get the full exponent -- Only the least significant bit
179 // Only that's needed because square roots of higher exponent bits simply multiply the
180 // result by 2!!
181 u32 key = static_cast<u32>((static_cast<u64>(exponent) | mantissa) >> 37);
182 u64 new_exp =
183 (static_cast<u64>((0xbfcLL << EXPONENT_SHIFT_F64) - exponent) >> 1) & EXPONENT_MASK_F64;
184
185 // Remove the bits relating to anything higher than the LSB of the exponent
186 const auto &entry = RSQRTE_TABLE[0x1f & (key >> 11)];
187
188 // The result is given by an estimate then an adjustment based on the original
189 // key that was computed
190 u64 new_mantissa = static_cast<u64>(entry.base + entry.dec * static_cast<s64>(key & 0x7ff));
191
192 return c64(new_exp | new_mantissa).f;
193}
194
195} // namespace EGG::Mathf
This header houses common data types such as our integral types and enums.
Math functions and constants used in the base game.
Definition Math.cc:3
static f32 fma(f32 x, f32 y, f32 z)
Fused multiply-add operation.
Definition Math.hh:68
f32 frsqrt(f32 x)
Definition Math.cc:315
static f32 sin(f32 x)
Definition Math.hh:33
static f64 force25Bit(f64 x)
This is used to mimic the Wii's floating-point unit.
Definition Math.hh:60
static f32 cos(f32 x)
Definition Math.hh:39
static f32 fms(f32 x, f32 y, f32 z)
Fused multiply-subtract operation.
Definition Math.hh:75