Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
MathHelper.cpp
Go to the documentation of this file.
1
2
3#include <glm/glm.hpp>
4#include <glm/gtc/packing.hpp>
5#include <glm/gtc/type_ptr.hpp>
6#include <glm/gtc/epsilon.hpp>
7
8//ref for decomposeMatrix:
9#include <glm/gtx/matrix_decompose.hpp>
10#include <glm/gtx/transform.hpp>
11
12namespace Divide::Util {
13
14bool decomposeMatrix(const mat4<F32>& transform,
15 vec3<F32>& translationOut,
16 vec3<F32>& scaleOut,
17 vec3<Angle::RADIANS<F32>>& rotationOut,
18 bool& isUniformScaleOut)
19{
20 using T = F32;
21
22 glm::mat4 LocalMatrix = glm::make_mat4(transform.mat);
23
24 // Normalize the matrix.
25 if (glm::epsilonEqual(LocalMatrix[3][3], static_cast<T>(0), glm::epsilon<T>()))
26 return false;
27
28 // First, isolate perspective. This is the messiest.
29 if (glm::epsilonNotEqual(LocalMatrix[0][3], static_cast<T>(0), glm::epsilon<T>()) ||
30 glm::epsilonNotEqual(LocalMatrix[1][3], static_cast<T>(0), glm::epsilon<T>()) ||
31 glm::epsilonNotEqual(LocalMatrix[2][3], static_cast<T>(0), glm::epsilon<T>()))
32 {
33 LocalMatrix[0][3] = LocalMatrix[1][3] = LocalMatrix[2][3] = static_cast<T>(0);
34 LocalMatrix[3][3] = static_cast<T>(1);
35 }
36
37 // Next take care of translation (easy).
38 glm::vec3 translation(LocalMatrix[3]);
39 translationOut.set(translation.x, translation.y, translation.z);
40 LocalMatrix[3] = glm::vec4(0, 0, 0, LocalMatrix[3].w);
41
42 glm::vec3 Row[3];
43
44 // Now get scale and shear.
45 for (glm::length_t i = 0; i < 3; ++i)
46 for (glm::length_t j = 0; j < 3; ++j)
47 Row[i][j] = LocalMatrix[i][j];
48
49 // Compute X scale factor and normalize first row.
50 scaleOut.x = length(Row[0]);// v3Length(Row[0]);
51 Row[0] = glm::detail::scale(Row[0], static_cast<T>(1));
52 // Now, compute Y scale and normalize 2nd row.
53 scaleOut.y = length(Row[1]);
54 Row[1] = glm::detail::scale(Row[1], static_cast<T>(1));
55 scaleOut.z = length(Row[2]);
56 Row[2] = glm::detail::scale(Row[2], static_cast<T>(1));
57 isUniformScaleOut = scaleOut.isUniform();
58
59 rotationOut.y = asin(-Row[0][2]);
60 if (!IS_ZERO(cos(rotationOut.y)))
61 {
62 rotationOut.x = atan2(Row[1][2], Row[2][2]);
63 rotationOut.z = atan2(Row[0][1], Row[0][0]);
64 }
65 else
66 {
67 rotationOut.x = atan2(-Row[2][0], Row[1][1]);
68 rotationOut.z = 0;
69 }
70
71 return true;
72}
73bool decomposeMatrix(const mat4<F32>& transform,
74 vec3<F32>& translationOut,
75 vec3<F32>& scaleOut,
76 vec3<Angle::RADIANS<F32>>& rotationOut) {
77 bool uniformScaleTemp = false;
78 return decomposeMatrix(transform, translationOut, scaleOut, rotationOut, uniformScaleTemp);
79}
80
81bool decomposeMatrix(const mat4<F32>& transform,
82 vec3<F32>& translationOut,
83 vec3<F32>& scaleOut) {
84 using T = F32;
85
86 glm::mat4 LocalMatrix = glm::make_mat4(transform.mat);
87
88 // Normalize the matrix.
89 if (glm::epsilonEqual(LocalMatrix[3][3], static_cast<T>(0), glm::epsilon<T>()))
90 return false;
91
92 // First, isolate perspective. This is the messiest.
93 if (glm::epsilonNotEqual(LocalMatrix[0][3], static_cast<T>(0), glm::epsilon<T>()) ||
94 glm::epsilonNotEqual(LocalMatrix[1][3], static_cast<T>(0), glm::epsilon<T>()) ||
95 glm::epsilonNotEqual(LocalMatrix[2][3], static_cast<T>(0), glm::epsilon<T>()))
96 {
97 LocalMatrix[0][3] = LocalMatrix[1][3] = LocalMatrix[2][3] = static_cast<T>(0);
98 LocalMatrix[3][3] = static_cast<T>(1);
99 }
100
101 // Next take care of translation (easy).
102 glm::vec3 translation(LocalMatrix[3]);
103 translationOut.set(translation.x, translation.y, translation.z);
104 LocalMatrix[3] = glm::vec4(0, 0, 0, LocalMatrix[3].w);
105
106 glm::vec3 Row[3];
107
108 // Now get scale and shear.
109 for (glm::length_t i = 0; i < 3; ++i)
110 for (glm::length_t j = 0; j < 3; ++j)
111 Row[i][j] = LocalMatrix[i][j];
112
113 // Compute X scale factor and normalize first row.
114 scaleOut.x = length(Row[0]);// v3Length(Row[0]);
115 Row[0] = glm::detail::scale(Row[0], static_cast<T>(1));
116 // Now, compute Y scale and normalize 2nd row.
117 scaleOut.y = length(Row[1]);
118 Row[1] = glm::detail::scale(Row[1], static_cast<T>(1));
119 scaleOut.z = length(Row[2]);
120 Row[2] = glm::detail::scale(Row[2], static_cast<T>(1));
121
122 return true;
123}
124
125bool decomposeMatrix(const mat4<F32>& transform,
126 vec3<F32>& translationOut) {
127 using T = F32;
128
129 glm::mat4 LocalMatrix = glm::make_mat4(transform.mat);
130
131 // Normalize the matrix.
132 if (glm::epsilonEqual(LocalMatrix[3][3], static_cast<T>(0), glm::epsilon<T>()))
133 return false;
134
135 // First, isolate perspective. This is the messiest.
136 if (glm::epsilonNotEqual(LocalMatrix[0][3], static_cast<T>(0), glm::epsilon<T>()) ||
137 glm::epsilonNotEqual(LocalMatrix[1][3], static_cast<T>(0), glm::epsilon<T>()) ||
138 glm::epsilonNotEqual(LocalMatrix[2][3], static_cast<T>(0), glm::epsilon<T>()))
139 {
140 LocalMatrix[0][3] = LocalMatrix[1][3] = LocalMatrix[2][3] = static_cast<T>(0);
141 LocalMatrix[3][3] = static_cast<T>(1);
142 }
143
144 // Next take care of translation (easy).
145 glm::vec3 translation(LocalMatrix[3]);
146 translationOut.set(translation.x, translation.y, translation.z);
147 return true;
148}
149
150bool IntersectCircles(const Circle& cA, const Circle& cB, vec2<F32>* pointsOut) noexcept {
151 assert(pointsOut != nullptr);
152
153 const F32 x0 = cA.center[0];
154 const F32 y0 = cA.center[1];
155 const F32 r0 = cA.radius;
156
157 const F32 x1 = cB.center[0];
158 const F32 y1 = cB.center[1];
159 const F32 r1 = cB.radius;
160
161 /* dx and dy are the vertical and horizontal distances between
162 * the circle centers.
163 */
164 const F32 dx = x1 - x0;
165 const F32 dy = y1 - y0;
166
167 /* Determine the straight-line distance between the centers. */
168 //d = sqrt((dy*dy) + (dx*dx));
169 const F32 d = hypot(dx, dy); // Suggested by Keith Briggs
170
171 /* Check for solvability. */
172 if (d > r0 + r1)
173 {
174 /* no solution. circles do not intersect. */
175 return false;
176 }
177 if (d < fabs(r0 - r1))
178 {
179 /* no solution. one circle is contained in the other */
180 return false;
181 }
182
183 /* 'point 2' is the point where the line through the circle
184 * intersection points crosses the line between the circle
185 * centers.
186 */
187
188 /* Determine the distance from point 0 to point 2. */
189 const F32 a = (r0*r0 - r1*r1 + d*d) / (2.0f * d);
190
191 /* Determine the coordinates of point 2. */
192 const F32 x2 = x0 + dx * a / d;
193 const F32 y2 = y0 + dy * a / d;
194
195 /* Determine the distance from point 2 to either of the
196 * intersection points.
197 */
198 const F32 h = sqrt(r0*r0 - a*a);
199
200 /* Now determine the offsets of the intersection points from
201 * point 2.
202 */
203 const F32 rx = -dy * (h / d);
204 const F32 ry = dx * (h / d);
205
206 /* Determine the absolute intersection points. */
207
208 pointsOut[0].x = x2 + rx;
209 pointsOut[1].x = x2 - rx;
210
211 pointsOut[0].y = y2 + ry;
212 pointsOut[1].y = y2 - ry;
213
214 return true;
215}
216
217void ToByteColour(const FColour4& floatColour, UColour4& colourOut) noexcept {
218 colourOut.set(FLOAT_TO_CHAR_UNORM(floatColour.r),
219 FLOAT_TO_CHAR_UNORM(floatColour.g),
220 FLOAT_TO_CHAR_UNORM(floatColour.b),
221 FLOAT_TO_CHAR_UNORM(floatColour.a));
222}
223
224void ToByteColour(const FColour3& floatColour, UColour3& colourOut) noexcept {
225 colourOut.set(FLOAT_TO_CHAR_UNORM(floatColour.r),
226 FLOAT_TO_CHAR_UNORM(floatColour.g),
227 FLOAT_TO_CHAR_UNORM(floatColour.b));
228}
229
230void ToFloatColour(const UColour4& byteColour, FColour4& colourOut) noexcept {
231 colourOut.set(UNORM_CHAR_TO_FLOAT(byteColour.r),
232 UNORM_CHAR_TO_FLOAT(byteColour.g),
233 UNORM_CHAR_TO_FLOAT(byteColour.b),
234 UNORM_CHAR_TO_FLOAT(byteColour.a));
235}
236
237void ToFloatColour(const UColour3& byteColour, FColour3& colourOut) noexcept {
238 colourOut.set(UNORM_CHAR_TO_FLOAT(byteColour.r),
239 UNORM_CHAR_TO_FLOAT(byteColour.g),
240 UNORM_CHAR_TO_FLOAT(byteColour.b));
241}
242
243void ToFloatColour(const vec4<U32>& uintColour, FColour4& colourOut) noexcept {
244 colourOut.set(uintColour.r / 255.0f,
245 uintColour.g / 255.0f,
246 uintColour.b / 255.0f,
247 uintColour.a / 255.0f);
248}
249
250void ToFloatColour(const vec3<U32>& uintColour, FColour3& colourOut) noexcept {
251 colourOut.set(uintColour.r / 255.0f,
252 uintColour.g / 255.0f,
253 uintColour.b / 255.0f);
254}
255
256UColour4 ToByteColour(const FColour4& floatColour) noexcept {
257 UColour4 tempColour;
258 ToByteColour(floatColour, tempColour);
259 return tempColour;
260}
261
262UColour3 ToByteColour(const FColour3& floatColour) noexcept {
263 UColour3 tempColour;
264 ToByteColour(floatColour, tempColour);
265 return tempColour;
266}
267
268FColour4 ToFloatColour(const UColour4& byteColour) noexcept {
269 FColour4 tempColour;
270 ToFloatColour(byteColour, tempColour);
271 return tempColour;
272}
273
274FColour3 ToFloatColour(const UColour3& byteColour) noexcept {
275 FColour3 tempColour;
276 ToFloatColour(byteColour, tempColour);
277 return tempColour;
278}
279
280FColour4 ToFloatColour(const vec4<U32>& uintColour) noexcept {
281 FColour4 tempColour;
282 ToFloatColour(uintColour, tempColour);
283 return tempColour;
284}
285
286FColour3 ToFloatColour(const vec3<U32>& uintColour) noexcept {
287 FColour3 tempColour;
288 ToFloatColour(uintColour, tempColour);
289 return tempColour;
290}
291
292F32 PACK_VEC3(const vec3<F32_SNORM>& value) noexcept {
293 return PACK_VEC3(value.x, value.y, value.z);
294}
295
296void UNPACK_VEC3(const F32 src, vec3<F32_SNORM>& res)noexcept {
297 UNPACK_VEC3(src, res.x, res.y, res.z);
298}
299
300vec3<F32_SNORM> UNPACK_VEC3(const F32 src) noexcept {
301 vec3<F32_SNORM> res;
302 UNPACK_VEC3(src, res);
303 return res;
304}
305
306[[nodiscard]] vec3<F32_NORM> UNPACK_11_11_10(const U32 src) {
307 vec3<F32_NORM> res;
308 UNPACK_11_11_10(src, res);
309 return res;
310}
311
313 return to_U32(glm::packHalf2x16(glm::mediump_vec2(value.x, value.y)));
314}
315
316void UNPACK_HALF2x16(const U32 src, vec2<F32>& value) {
317 const glm::vec2 ret = glm::unpackHalf2x16(src);
318 value.set(ret.x, ret.y);
319}
320
322 vec2<F32> ret;
323 UNPACK_HALF2x16(src, ret);
324 return ret;
325}
326
327U16 PACK_HALF1x16(const F32 value) {
328 return to_U16(glm::packHalf1x16(value));
329}
330
331void UNPACK_HALF1x16(const U16 src, F32& value) {
332 value = glm::unpackHalf1x16(src);
333}
334
336 F32 ret = 0.f;
337 UNPACK_HALF1x16(src, ret);
338 return ret;
339}
340
342 return glm::uintBitsToFloat(src);
343}
344
346 return glm::floatBitsToUint(src);
347}
348
349F32 INT_TO_FLOAT(const I32 src) {
350 return glm::intBitsToFloat(src);
351}
352
353I32 FLOAT_TO_INT(const F32 src) {
354 return glm::floatBitsToInt(src);
355}
356
357U32 PACK_HALF2x16(const F32 x, const F32 y) {
358 return to_U32(glm::packHalf2x16(glm::mediump_vec2(x, y)));
359}
360
361void UNPACK_HALF2x16(const U32 src, F32& x, F32& y) {
362
363 const glm::vec2 ret = glm::unpackHalf2x16(src);
364 x = ret.x;
365 y = ret.y;
366}
367
368// Converts each component of the normalized floating point value "value" into 8 bit integer values.
370 return PACK_UNORM4x8(value.x, value.y, value.z, value.w);
371}
372
374 return PACK_UNORM4x8(value.x, value.y, value.z, value.w);
375}
376
377void UNPACK_UNORM4x8(const U32 src, vec4<F32_NORM>& value) {
378 UNPACK_UNORM4x8(src, value.x, value.y, value.z, value.w);
379}
380
381U32 PACK_UNORM4x8(const U8 x, const U8 y, const U8 z, const U8 w) {
382 return to_U32(glm::packUnorm4x8({ UNORM_CHAR_TO_FLOAT(x),
386}
387
388U32 PACK_UNORM4x8(const F32_NORM x, const F32_NORM y, const F32_NORM z, const F32_NORM w) {
389 assert(IS_IN_RANGE_INCLUSIVE(x, 0.f, 1.f));
390 assert(IS_IN_RANGE_INCLUSIVE(y, 0.f, 1.f));
391 assert(IS_IN_RANGE_INCLUSIVE(z, 0.f, 1.f));
392 assert(IS_IN_RANGE_INCLUSIVE(w, 0.f, 1.f));
393
394 return to_U32(glm::packUnorm4x8({ x, y, z, w }));
395}
396
397void UNPACK_UNORM4x8(const U32 src, U8& x, U8& y, U8& z, U8& w) {
398 const glm::vec4 ret = glm::unpackUnorm4x8(src);
399 x = FLOAT_TO_CHAR_UNORM(ret.x);
400 y = FLOAT_TO_CHAR_UNORM(ret.y);
401 z = FLOAT_TO_CHAR_UNORM(ret.z);
402 w = FLOAT_TO_CHAR_UNORM(ret.w);
403}
404
405void UNPACK_UNORM4x8(const U32 src, F32_NORM& x, F32_NORM& y, F32_NORM& z, F32_NORM& w) {
406 const glm::vec4 ret = glm::unpackUnorm4x8(src);
407 x = ret.x; y = ret.y; z = ret.z; w = ret.w;
408 assert(IS_IN_RANGE_INCLUSIVE(x, 0.f, 1.f));
409 assert(IS_IN_RANGE_INCLUSIVE(y, 0.f, 1.f));
410 assert(IS_IN_RANGE_INCLUSIVE(z, 0.f, 1.f));
411 assert(IS_IN_RANGE_INCLUSIVE(w, 0.f, 1.f));
412}
413
415 vec4<U8> ret;
416 UNPACK_UNORM4x8(src, ret.x, ret.y, ret.z, ret.w);
417 return ret;
418}
419
421 vec4<F32_NORM> ret;
422 UNPACK_UNORM4x8(src, ret.x, ret.y, ret.z, ret.w);
423 return ret;
424}
425
427 return PACK_11_11_10(value.x, value.y, value.z);
428}
429
430void UNPACK_11_11_10(const U32 src, vec3<F32_NORM>& res) {
431 UNPACK_11_11_10(src, res.x, res.y, res.z);
432}
433
434U32 PACK_11_11_10(const F32_NORM x, const F32_NORM y, const F32_NORM z) {
435 assert(x >= 0.f && x <= 1.0f);
436 assert(y >= 0.f && y <= 1.0f);
437 assert(z >= 0.f && z <= 1.0f);
438
439 return glm::packF2x11_1x10(glm::vec3(x, y, z));
440}
441
442void UNPACK_11_11_10(const U32 src, F32_NORM& x, F32_NORM& y, F32_NORM& z) {
443 const glm::vec3 ret = glm::unpackF2x11_1x10(src);
444 x = ret.x;
445 y = ret.y;
446 z = ret.z;
447}
448
449void Normalize(vec3<F32>& inputRotation, const bool degrees, const bool normYaw, const bool normPitch, const bool normRoll) noexcept {
450 if (normYaw) {
451 F32 yaw = degrees ? Angle::to_RADIANS(inputRotation.yaw)
452 : inputRotation.yaw;
453 if (yaw < -M_PI_f) {
454 yaw = fmod(yaw, M_PI_f * 2.0f);
455 if (yaw < -M_PI_f) {
456 yaw += M_PI_f * 2.0f;
457 }
458 inputRotation.yaw = Angle::to_DEGREES(yaw);
459 } else if (yaw > M_PI_f) {
460 yaw = fmod(yaw, M_PI_f * 2.0f);
461 if (yaw > M_PI_f) {
462 yaw -= M_PI_f * 2.0f;
463 }
464 inputRotation.yaw = degrees ? Angle::to_DEGREES(yaw) : yaw;
465 }
466 }
467 if (normPitch) {
468 F32 pitch = degrees ? Angle::to_RADIANS(inputRotation.pitch)
469 : inputRotation.pitch;
470 if (pitch < -M_PI_f) {
471 pitch = fmod(pitch, M_PI_f * 2.0f);
472 if (pitch < -M_PI_f) {
473 pitch += M_PI_f * 2.0f;
474 }
475 inputRotation.pitch = Angle::to_DEGREES(pitch);
476 } else if (pitch > M_PI_f) {
477 pitch = fmod(pitch, M_PI_f * 2.0f);
478 if (pitch > M_PI_f) {
479 pitch -= M_PI_f * 2.0f;
480 }
481 inputRotation.pitch =
482 degrees ? Angle::to_DEGREES(pitch) : pitch;
483 }
484 }
485 if (normRoll) {
486 F32 roll = degrees ? Angle::to_RADIANS(inputRotation.roll)
487 : inputRotation.roll;
488 if (roll < -M_PI_f) {
489 roll = fmod(roll, M_PI_f * 2.0f);
490 if (roll < -M_PI_f) {
491 roll += M_PI_f * 2.0f;
492 }
493 inputRotation.roll = Angle::to_DEGREES(roll);
494 } else if (roll > M_PI_f) {
495 roll = fmod(roll, M_PI_f * 2.0f);
496 if (roll > M_PI_f) {
497 roll -= M_PI_f * 2.0f;
498 }
499 inputRotation.roll = degrees ? Angle::to_DEGREES(roll) : roll;
500 }
501 }
502}
503
504} // namespace Divide::Util
void set(const T *v) noexcept
set the 2 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:335
bool isUniform(F32 tolerance=0.0001f) const noexcept
uniform vector: x = y = z
void set(const T *v) noexcept
set the 3 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:707
void set(const T *v) noexcept
set the 4 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:1241
constexpr DEGREES< T > to_DEGREES(RADIANS< T > angle) noexcept
Definition: MathHelper.inl:380
constexpr RADIANS< T > to_RADIANS(DEGREES< T > angle) noexcept
Definition: MathHelper.inl:368
F32 INT_TO_FLOAT(I32 src)
Definition: MathHelper.cpp:349
void UNPACK_11_11_10(U32 src, vec3< F32_NORM > &res)
Definition: MathHelper.cpp:430
U32 PACK_11_11_10(const vec3< F32_NORM > &value)
Definition: MathHelper.cpp:426
bool decomposeMatrix(const mat4< F32 > &transform, vec3< F32 > &translationOut, vec3< F32 > &scaleOut, vec3< Angle::RADIANS< F32 > > &rotationOut, bool &isUniformScaleOut)
Definition: MathHelper.cpp:14
F32 PACK_VEC3(F32_SNORM x, F32_SNORM y, F32_SNORM z) noexcept
Definition: MathHelper.inl:316
void Normalize(vec3< F32 > &inputRotation, bool degrees=false, bool normYaw=true, bool normPitch=true, bool normRoll=true) noexcept
Normalise the selected rotations to be within the +/-180 degree range.
Definition: MathHelper.cpp:449
UColour4 ToByteColour(const FColour4 &floatColour) noexcept
Definition: MathHelper.cpp:256
FColour4 ToFloatColour(const UColour4 &byteColour) noexcept
Definition: MathHelper.cpp:268
bool IntersectCircles(const Circle &cA, const Circle &cB, vec2< F32 > *pointsOut) noexcept
Definition: MathHelper.cpp:150
void UNPACK_VEC3(F32 src, F32_SNORM &x, F32_SNORM &y, F32_SNORM &z) noexcept
Definition: MathHelper.inl:335
void UNPACK_HALF1x16(U16 src, F32 &value)
Only convert the range [-1024., 1024.] for accurate results.
Definition: MathHelper.cpp:331
U32 PACK_UNORM4x8(const vec4< F32_NORM > &value)
Definition: MathHelper.cpp:369
F32 UINT_TO_FLOAT(U32 src)
Definition: MathHelper.cpp:341
vec4< U8 > UNPACK_UNORM4x8_U8(U32 src)
Definition: MathHelper.cpp:414
U32 FLOAT_TO_UINT(F32 src)
Definition: MathHelper.cpp:345
U16 PACK_HALF1x16(F32 value)
Only convert the range [-1024., 1024.] for accurate results.
Definition: MathHelper.cpp:327
I32 FLOAT_TO_INT(F32 src)
Definition: MathHelper.cpp:353
vec4< F32_NORM > UNPACK_UNORM4x8_F32(U32 src)
Definition: MathHelper.cpp:420
U32 PACK_HALF2x16(vec2< F32 > value)
Definition: MathHelper.cpp:312
void UNPACK_HALF2x16(U32 src, vec2< F32 > &value)
Definition: MathHelper.cpp:316
void UNPACK_UNORM4x8(U32 src, vec4< F32_NORM > &value)
Definition: MathHelper.cpp:377
constexpr F32 M_PI_f
Definition: MathHelper.h:86
constexpr U8 FLOAT_TO_CHAR_UNORM(F32_NORM value) noexcept
Returns round(value * 255)
Definition: MathHelper.inl:307
bool IS_ZERO(const T X) noexcept
constexpr U32 to_U32(const T value)
constexpr F32_NORM UNORM_CHAR_TO_FLOAT(U8 value) noexcept
Returns value / 255.f.
Definition: MathHelper.inl:302
bool IS_IN_RANGE_INCLUSIVE(const T x, const U min, const U max) noexcept
constexpr U16 to_U16(const T value)
int32_t I32
uint8_t U8
uint16_t U16
uint32_t U32