SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
Math.h
Go to the documentation of this file.
1#pragma once
2
3#include <bit>
4#include <concepts>
5#include <cmath>
6#include <cstdint>
7#include <numbers>
8#include <type_traits>
9
10#include <half.hpp>
11
12// Numeric types are intentionally outside the sourcepp namespace
13using std::int8_t;
14using std::int16_t;
15using std::int32_t;
16using std::int64_t;
17using std::uint8_t;
18using std::uint16_t;
19using std::uint32_t;
20using std::uint64_t;
21using half_float::half;
22
23namespace sourcepp::math {
24
25template<std::floating_point F>
26constexpr F pi = std::numbers::pi_v<F>;
27constexpr auto pi_f32 = pi<float>;
28constexpr auto pi_f64 = pi<double>;
29
30template<typename T>
31concept Arithmetic = std::is_arithmetic_v<T> || std::same_as<T, half>;
32
33template<Arithmetic T>
34[[nodiscard]] constexpr T remap(T value, T l1, T h1, T l2, T h2) {
35 return l2 + (value - l1) * (h2 - l2) / (h1 - l1);
36}
37
38template<Arithmetic T>
39[[nodiscard]] constexpr T remap(T value, T h1, T h2) {
40 return value * h2 / h1;
41}
42
43[[nodiscard]] constexpr bool isPowerOf2(std::unsigned_integral auto n) {
44 return n && !(n & (n - 1));
45}
46
47template<std::unsigned_integral T>
48[[nodiscard]] constexpr T nearestPowerOf2(T n) {
49 if (isPowerOf2(n)) {
50 return n;
51 }
52 auto bigger = std::bit_ceil(n);
53 auto smaller = std::bit_floor(n);
54 return (n - smaller) < (bigger - n) ? smaller : bigger;
55}
56
57[[nodiscard]] constexpr uint32_t log2ceil(uint32_t value) {
58 return ((std::bit_cast<uint32_t>(static_cast<float>(value)) >> 23) & 0xff) - 127;
59}
60
61[[nodiscard]] constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n) {
62 if (const auto rest = n % alignment; rest > 0) {
63 return alignment - rest;
64 }
65 return 0;
66}
67
68template<uint8_t S_, Arithmetic P>
69struct Vec {
70 static constexpr uint8_t S = S_;
71 static_assert(S >= 2, "Vectors must have at least two values!");
72
73 using value_type = P;
74
76
77 constexpr Vec() = default;
78
79 // ReSharper disable once CppNonExplicitConvertingConstructor
80 template<std::convertible_to<P>... Vals>
81 requires (sizeof...(Vals) == S)
82 constexpr Vec(Vals... vals) // NOLINT(*-explicit-constructor)
83 : values{static_cast<P>(vals)...} {}
84
85 [[nodiscard]] constexpr const P* data() const {
86 return this->values;
87 }
88
89 [[nodiscard]] constexpr P* data() {
90 return this->values;
91 }
92
93 [[nodiscard]] constexpr uint8_t size() const {
94 return S;
95 }
96
97 [[nodiscard]] constexpr P& operator[](uint8_t index) {
98 if (index < S) {
99 return this->values[index];
100 }
101 return this->operator[](index % S);
102 }
103
104 [[nodiscard]] constexpr P operator[](uint8_t index) const {
105 if (index < S) {
106 return this->values[index];
107 }
108 return this->operator[](index % S);
109 }
110
111 [[nodiscard]] constexpr Vec operator+() const {
112 return *this;
113 }
114
115 template<uint8_t SO, Arithmetic PO>
116 [[nodiscard]] constexpr Vec operator+(const Vec<SO, PO>& other) const {
117 auto out = *this;
118 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
119 out[i] += static_cast<P>(other[i]);
120 }
121 return out;
122 }
123
124 template<uint8_t SO, Arithmetic PO>
125 constexpr void operator+=(const Vec<SO, PO>& other) {
126 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
127 (*this)[i] += static_cast<P>(other[i]);
128 }
129 }
130
131 [[nodiscard]] constexpr Vec operator-() const {
132 auto out = *this;
133 for (uint8_t i = 0; i < S; i++) {
134 out[i] *= -1;
135 }
136 return out;
137 }
138
139 template<uint8_t SO, Arithmetic PO>
140 [[nodiscard]] constexpr Vec operator-(const Vec<SO, PO>& other) const {
141 auto out = *this;
142 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
143 out[i] -= static_cast<P>(other[i]);
144 }
145 return out;
146 }
147
148 template<uint8_t SO, Arithmetic PO>
149 constexpr void operator-=(const Vec<SO, PO>& other) {
150 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
151 (*this)[i] -= static_cast<P>(other[i]);
152 }
153 }
154
155 [[nodiscard]] constexpr Vec operator*(Arithmetic auto scalar) const {
156 auto out = *this;
157 for (uint8_t i = 0; i < S; i++) {
158 out[i] *= static_cast<P>(scalar);
159 }
160 return out;
161 }
162
163 constexpr void operator*=(Arithmetic auto scalar) {
164 for (uint8_t i = 0; i < S; i++) {
165 (*this)[i] *= static_cast<P>(scalar);
166 }
167 }
168
169 [[nodiscard]] constexpr Vec operator/(Arithmetic auto scalar) const {
170 auto out = *this;
171 for (uint8_t i = 0; i < S; i++) {
172 out[i] /= static_cast<P>(scalar);
173 }
174 return out;
175 }
176
177 constexpr void operator/=(Arithmetic auto scalar) {
178 for (uint8_t i = 0; i < S; i++) {
179 (*this)[i] /= static_cast<P>(scalar);
180 }
181 }
182
183 [[nodiscard]] constexpr Vec operator%(Arithmetic auto scalar) const {
184 auto out = *this;
185 for (uint8_t i = 0; i < S; i++) {
186 out[i] %= static_cast<P>(scalar);
187 }
188 return out;
189 }
190
191 constexpr void operator%=(Arithmetic auto scalar) {
192 for (uint8_t i = 0; i < S; i++) {
193 (*this)[i] %= static_cast<P>(scalar);
194 }
195 }
196
197 template<uint8_t SO, Arithmetic PO>
198 [[nodiscard]] constexpr bool operator==(const Vec<SO, PO>& other) const {
199 if constexpr (S != SO) {
200 return false;
201 } else {
202 for (uint8_t i = 0; i < S; i++) {
203 if ((*this)[i] != static_cast<P>(other[i])) {
204 return false;
205 }
206 }
207 return true;
208 }
209 }
210
211 template<uint8_t SO, Arithmetic PO = P>
212 [[nodiscard]] constexpr Vec<SO, PO> to() const {
213 Vec<SO, PO> out{};
214 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
215 out[i] = static_cast<PO>((*this)[i]);
216 }
217 return out;
218 }
219
220 template<uint8_t SO, Arithmetic PO>
221 [[nodiscard]] constexpr Vec mul(const Vec<SO, PO>& other) const {
222 auto out = *this;
223 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
224 out[i] *= static_cast<P>(other[i]);
225 }
226 return out;
227 }
228
229 template<uint8_t SO, Arithmetic PO>
230 [[nodiscard]] constexpr Vec div(const Vec<SO, PO>& other) const {
231 auto out = *this;
232 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
233 out[i] /= static_cast<P>(other[i]);
234 }
235 return out;
236 }
237
238 template<uint8_t SO, Arithmetic PO>
239 [[nodiscard]] constexpr Vec mod(const Vec<SO, PO>& other) const {
240 auto out = *this;
241 for (uint8_t i = 0; i < (S > SO ? SO : S); i++) {
242 if constexpr ((std::floating_point<P> && std::floating_point<PO>) || std::floating_point<P>) {
243 out[i] = std::fmod(out[i], static_cast<P>(other[i]));
244 } else {
245 out[i] %= static_cast<P>(other[i]);
246 }
247 }
248 return out;
249 }
250
251 [[nodiscard]] constexpr float magf() const {
252 float out = 0.0;
253 for (uint8_t i = 0; i < S; i++) {
254 out += std::pow((*this)[i], 2);
255 }
256 return std::sqrt(out);
257 }
258
259 [[nodiscard]] constexpr double mag() const {
260 double out = 0.0;
261 for (uint8_t i = 0; i < S; i++) {
262 out += std::pow((*this)[i], 2);
263 }
264 return std::sqrt(out);
265 }
266
267 [[nodiscard]] constexpr P sum() const {
268 P out{};
269 for (uint8_t i = 0; i < S; i++) {
270 out += (*this)[i];
271 }
272 return out;
273 }
274
275 template<Arithmetic PO>
276 [[nodiscard]] constexpr Vec scale(const Vec<S, PO>& other) const {
277 Vec out;
278 for (uint8_t i = 0; i < S; i++) {
279 out[i] = (*this)[i] * static_cast<P>(other[i]);
280 }
281 return out;
282 }
283
284 template<Arithmetic PO>
285 [[nodiscard]] constexpr P dot(const Vec<S, PO>& other) const {
286 return this->scale(other).sum();
287 }
288
289 [[nodiscard]] constexpr Vec abs() const {
290 auto out = *this;
291 for (uint8_t i = 0; i < S; i++) {
292 out[i] = std::abs(out[i]);
293 }
294 return out;
295 }
296
297 [[nodiscard]] static constexpr Vec zero() {
298 return {};
299 }
300
301 [[nodiscard]] constexpr bool isZero() const {
302 return *this == zero();
303 }
304};
305static_assert(std::is_trivially_copyable_v<Vec<2, float>>);
306
307#define SOURCEPP_VEC_DEFINE(S) \
308 template<Arithmetic P> \
309 using Vec##S = Vec<S, P>; \
310 using Vec##S##i8 = Vec##S<int8_t>; \
311 using Vec##S##i16 = Vec##S<int16_t>; \
312 using Vec##S##i32 = Vec##S<int32_t>; \
313 using Vec##S##i64 = Vec##S<int64_t>; \
314 using Vec##S##i = Vec##S##i32; \
315 using Vec##S##ui8 = Vec##S<uint8_t>; \
316 using Vec##S##ui16 = Vec##S<uint16_t>; \
317 using Vec##S##ui32 = Vec##S<uint32_t>; \
318 using Vec##S##ui64 = Vec##S<uint64_t>; \
319 using Vec##S##ui = Vec##S##ui32; \
320 using Vec##S##f16 = Vec##S<half>; \
321 using Vec##S##f32 = Vec##S<float>; \
322 using Vec##S##f64 = Vec##S<double>; \
323 using Vec##S##f = Vec##S##f32
324
328
329#undef SOURCEPP_VEC_DEFINE
330
331using EulerAngles = Vec3f;
332
333using Quat = Vec4f;
334
337 uint16_t x : 16;
338 uint16_t y : 16;
339 uint16_t z : 15;
340 uint16_t wn : 1;
341
342 [[nodiscard]] Quat decompress() const {
343 // Convert from 16-bit (or 15-bit) integers to floating point values in the range [-1, 1]
344 const float fx = (static_cast<float>(this->x) / 32767.5f) - 1.f; // x / ((2^16 - 1) / 2) - 1
345 const float fy = (static_cast<float>(this->y) / 32767.5f) - 1.f; // y / ((2^16 - 1) / 2) - 1
346 const float fz = (static_cast<float>(this->z) / 16383.5f) - 1.f; // z / ((2^15 - 1) / 2) - 1
347
348 // Recalculate w from the constraint that x^2 + y^2 + z^2 + w^2 = 1
349 float fw = std::sqrt(1.f - fx * fx - fy * fy - fz * fz);
350
351 // Adjust w based on the stored sign bit
352 if (this->wn) {
353 fw = -fw;
354 }
355
356 return {fx, fy, fz, fw};
357 }
358};
359static_assert(std::is_trivially_copyable_v<QuatCompressed48>);
360
363 uint32_t x : 21;
364 uint32_t y : 21;
365 uint32_t z : 21;
366 uint32_t wn : 1;
367
368 [[nodiscard]] Quat decompress() const {
369 // Convert from 21-bit integers to floating point values in the range [-1, 1]
370 const double fx = (static_cast<double>(this->x) / 1048575.5) - 1.0f; // x / ((2^21 - 1) / 2) - 1
371 const double fy = (static_cast<double>(this->y) / 1048575.5) - 1.0f; // y / ((2^21 - 1) / 2) - 1
372 const double fz = (static_cast<double>(this->z) / 1048575.5) - 1.0f; // z / ((2^21 - 1) / 2) - 1
373
374 // Recalculate w from the constraint that x^2 + y^2 + z^2 + w^2 = 1
375 double fw = std::sqrt(1.0 - fx * fx - fy * fy - fz * fz);
376
377 // Adjust w based on the stored sign bit
378 if (this->wn) {
379 fw = -fw;
380 }
381
382 return {static_cast<float>(fx), static_cast<float>(fy), static_cast<float>(fz), static_cast<float>(fw)};
383 }
384};
385static_assert(std::is_trivially_copyable_v<QuatCompressed64>);
386
387template<uint8_t M_, uint8_t N_, Arithmetic P>
388struct Mat {
389 static constexpr uint8_t M = M_;
390 static_assert(M >= 2, "Matrices must have at least two rows!");
391 static constexpr uint8_t N = N_;
392 static_assert(N >= 2, "Matrices must have at least two columns!");
393
394 using value_type = P;
395
396 P values[M][N];
397
398 [[nodiscard]] constexpr const P* data() const {
399 return this->values;
400 }
401
402 [[nodiscard]] constexpr P* data() {
403 return this->values;
404 }
405
406 [[nodiscard]] constexpr uint8_t rows() const {
407 return M;
408 }
409
410 [[nodiscard]] constexpr uint8_t cols() const {
411 return N;
412 }
413
414 [[nodiscard]] P* operator[](uint8_t i) { return this->values[i]; }
415
416 [[nodiscard]] const P* operator[](uint8_t i) const { return this->values[i]; }
417};
418static_assert(std::is_trivially_copyable_v<Mat<2, 2, float>>);
419
420#define SOURCEPP_MAT_DEFINE(M, N) \
421 template<Arithmetic P> \
422 using Mat##M##x##N = Mat<M, N, P>; \
423 using Mat##M##x##N##i8 = Mat##M##x##N<int8_t>; \
424 using Mat##M##x##N##i16 = Mat##M##x##N<int16_t>; \
425 using Mat##M##x##N##i32 = Mat##M##x##N<int32_t>; \
426 using Mat##M##x##N##i64 = Mat##M##x##N<int64_t>; \
427 using Mat##M##x##N##i = Mat##M##x##N##i32; \
428 using Mat##M##x##N##ui8 = Mat##M##x##N<uint8_t>; \
429 using Mat##M##x##N##ui16 = Mat##M##x##N<uint16_t>; \
430 using Mat##M##x##N##ui32 = Mat##M##x##N<uint32_t>; \
431 using Mat##M##x##N##ui64 = Mat##M##x##N<uint64_t>; \
432 using Mat##M##x##N##ui = Mat##M##x##N##ui32; \
433 using Mat##M##x##N##f16 = Mat##M##x##N<half>; \
434 using Mat##M##x##N##f32 = Mat##M##x##N<float>; \
435 using Mat##M##x##N##f64 = Mat##M##x##N<double>; \
436 using Mat##M##x##N##f = Mat##M##x##N##f32
437
447
448#undef SOURCEPP_MAT_DEFINE
449
450} // namespace sourcepp::math
#define SOURCEPP_VEC_DEFINE(S)
Definition Math.h:307
#define SOURCEPP_MAT_DEFINE(M, N)
Definition Math.h:420
constexpr T nearestPowerOf2(T n)
Definition Math.h:48
constexpr bool isPowerOf2(std::unsigned_integral auto n)
Definition Math.h:43
constexpr auto pi_f32
Definition Math.h:27
constexpr auto pi_f64
Definition Math.h:28
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
Definition Math.h:61
constexpr T remap(T value, T l1, T h1, T l2, T h2)
Definition Math.h:34
constexpr uint32_t log2ceil(uint32_t value)
Definition Math.h:57
Vec4f Quat
Definition Math.h:333
constexpr F pi
Definition Math.h:26
Vec3f EulerAngles
Definition Math.h:331
constexpr uint8_t rows() const
Definition Math.h:406
constexpr const P * data() const
Definition Math.h:398
const P * operator[](uint8_t i) const
Definition Math.h:416
static constexpr uint8_t M
Definition Math.h:389
constexpr P * data()
Definition Math.h:402
static constexpr uint8_t N
Definition Math.h:391
P * operator[](uint8_t i)
Definition Math.h:414
P values[M][N]
Definition Math.h:396
constexpr uint8_t cols() const
Definition Math.h:410
Lower precision Quat compressed to 6 bytes.
Definition Math.h:336
Lower precision Quat compressed to 8 bytes.
Definition Math.h:362
constexpr Vec mod(const Vec< SO, PO > &other) const
Definition Math.h:239
constexpr Vec(Vals... vals)
Definition Math.h:82
constexpr P dot(const Vec< S, PO > &other) const
Definition Math.h:285
constexpr Vec div(const Vec< SO, PO > &other) const
Definition Math.h:230
static constexpr Vec zero()
Definition Math.h:297
constexpr Vec operator-(const Vec< SO, PO > &other) const
Definition Math.h:140
constexpr Vec operator-() const
Definition Math.h:131
constexpr void operator/=(Arithmetic auto scalar)
Definition Math.h:177
constexpr Vec operator+() const
Definition Math.h:111
constexpr float magf() const
Definition Math.h:251
constexpr Vec operator*(Arithmetic auto scalar) const
Definition Math.h:155
constexpr Vec operator/(Arithmetic auto scalar) const
Definition Math.h:169
constexpr Vec scale(const Vec< S, PO > &other) const
Definition Math.h:276
constexpr Vec operator%(Arithmetic auto scalar) const
Definition Math.h:183
constexpr double mag() const
Definition Math.h:259
constexpr bool isZero() const
Definition Math.h:301
constexpr Vec< SO, PO > to() const
Definition Math.h:212
constexpr uint8_t size() const
Definition Math.h:93
constexpr Vec operator+(const Vec< SO, PO > &other) const
Definition Math.h:116
constexpr bool operator==(const Vec< SO, PO > &other) const
Definition Math.h:198
constexpr Vec()=default
constexpr void operator-=(const Vec< SO, PO > &other)
Definition Math.h:149
constexpr void operator*=(Arithmetic auto scalar)
Definition Math.h:163
constexpr P operator[](uint8_t index) const
Definition Math.h:104
constexpr void operator%=(Arithmetic auto scalar)
Definition Math.h:191
constexpr P * data()
Definition Math.h:89
constexpr Vec mul(const Vec< SO, PO > &other) const
Definition Math.h:221
constexpr P & operator[](uint8_t index)
Definition Math.h:97
static constexpr uint8_t S
Definition Math.h:70
constexpr P sum() const
Definition Math.h:267
constexpr Vec abs() const
Definition Math.h:289
constexpr const P * data() const
Definition Math.h:85
constexpr void operator+=(const Vec< SO, PO > &other)
Definition Math.h:125