Nameless Engine
Loading...
Searching...
No Matches
MathHelpers.hpp
1#pragma once
2
3// Standard.
4#include <cmath>
5
6// Custom.
7#include "misc/Globals.h"
8#include "io/Logger.h"
9
10// External.
11#include "math/GLMath.hpp"
12#include "misc/Error.h"
13#include "misc/Profiler.hpp"
14
15namespace ne {
18 public:
19 MathHelpers() = delete;
20
30 static inline glm::vec3 convertNormalizedDirectionToRollPitchYaw(const glm::vec3& direction);
31
39 static inline glm::vec3 convertRollPitchYawToDirection(const glm::vec3& rotation);
40
50 static inline glm::vec3 convertSphericalToCartesianCoordinates(float radius, float theta, float phi);
51
61 const glm::vec3& location, float& radius, float& theta, float& phi);
62
70 static inline glm::vec3 calculateReciprocalVector(const glm::vec3& vector);
71
79 static inline glm::mat4x4 buildRotationMatrix(const glm::vec3& rotation);
80
97 static inline float normalizeToRange(float value, float min, float max);
98
107 static inline glm::vec3 normalizeSafely(const glm::vec3& vector);
108
109 private:
111 static inline const float smallFloatEpsilon = 0.0000001F; // NOLINT: not a very small number
112 };
113
114 glm::vec3 MathHelpers::convertNormalizedDirectionToRollPitchYaw(const glm::vec3& direction) {
115 PROFILE_FUNC;
116
117 // Ignore zero vectors.
118 if (glm::all(glm::epsilonEqual(direction, glm::vec3(0.0F, 0.0F, 0.0F), smallFloatEpsilon))) {
119 return glm::vec3(0.0F, 0.0F, 0.0F);
120 }
121
122#if defined(DEBUG)
123 // Make sure we are given a normalized vector.
124 constexpr float lengthDelta = 0.001F; // NOLINT: don't use too small value here
125 const auto length = glm::length(direction);
126 if (!glm::epsilonEqual(length, 1.0F, lengthDelta)) [[unlikely]] {
127 // show an error so that it will be instantly noticeable because we're in the debug build
128 Error error("the specified direction vector should have been normalized");
129 error.showError();
130 throw std::runtime_error(error.getFullErrorMessage());
131 }
132#endif
133
134 glm::vec3 worldRotation = glm::vec3(0.0F, 0.0F, 0.0F);
135
136 worldRotation.z = glm::degrees(std::atan2(direction.y, direction.x));
137 worldRotation.y = glm::degrees(-std::asin(direction.z));
138
139 // Check for NaNs.
140 if (glm::isnan(worldRotation.z)) {
142 "found NaN in the Z component of the calculated rotation, setting this component's value to "
143 "zero");
144 worldRotation.z = 0.0F;
145 }
146 if (glm::isnan(worldRotation.y)) {
148 "found NaN in the Y component of the calculated rotation, setting this component's value to "
149 "zero");
150 worldRotation.y = 0.0F;
151 }
152
153 // Use zero roll for now.
154
155 // Calculate roll:
156 // See if we can use world up direction to find the right direction.
157 // glm::vec3 vecToFindRight = worldUpDirection;
158 // if (std::abs(direction.z) > 0.999F) { // NOLINT: magic number
159 // // Use +X then.
160 // vecToFindRight = glm::vec3(1.0F, 0.0F, 0.0F);
161 //}
162 // const auto rightDirection = glm::normalize(glm::cross(direction, vecToFindRight));
163
164 // worldRotation.x =
165 // glm::degrees(-std::asinf(worldUpDirection.x * rightDirection.x + worldUpDirection.y *
166 // rightDirection.y));
167
168 // Check roll for NaN.
169 // if (glm::isnan(worldRotation.x)) {
170 // worldRotation.x = 0.0F;
171 // }
172
173 return worldRotation;
174 }
175
176 glm::vec3 MathHelpers::convertRollPitchYawToDirection(const glm::vec3& rotation) {
177 return buildRotationMatrix(rotation) * glm::vec4(Globals::WorldDirection::forward, 0.0F);
178 }
179
180 glm::vec3 MathHelpers::convertSphericalToCartesianCoordinates(float radius, float theta, float phi) {
181 phi = glm::radians(phi);
182 theta = glm::radians(theta);
183
184 const auto sinPhi = std::sin(phi);
185 const auto sinTheta = std::sin(theta);
186 const auto cosPhi = std::cos(phi);
187 const auto cosTheta = std::cos(theta);
188 return glm::vec3(radius * sinPhi * cosTheta, radius * sinPhi * sinTheta, radius * cosPhi);
189 }
190
192 const glm::vec3& location, float& radius, float& theta, float& phi) {
193 radius = glm::sqrt(location.x * location.x + location.y * location.y + location.z * location.z);
194 theta = glm::degrees(glm::atan2(location.y, location.x));
195 phi = glm::degrees(
196 glm::atan2(glm::sqrt(location.x * location.x + location.y * location.y), location.z));
197 }
198
199 glm::vec3 MathHelpers::calculateReciprocalVector(const glm::vec3& vector) {
200 glm::vec3 reciprocal;
201
202 if (std::abs(vector.x) < smallFloatEpsilon) [[unlikely]] {
203 reciprocal.x = 0.0F;
204 } else [[likely]] {
205 reciprocal.x = 1.0F / vector.x;
206 }
207
208 if (std::abs(vector.y) < smallFloatEpsilon) [[unlikely]] {
209 reciprocal.y = 0.0F;
210 } else [[likely]] {
211 reciprocal.y = 1.0F / vector.y;
212 }
213
214 if (std::abs(vector.z) < smallFloatEpsilon) [[unlikely]] {
215 reciprocal.z = 0.0F;
216 } else [[likely]] {
217 reciprocal.z = 1.0F / vector.z;
218 }
219
220 return reciprocal;
221 }
222
223 glm::mat4x4 MathHelpers::buildRotationMatrix(const glm::vec3& rotation) {
224 return glm::rotate(glm::radians(rotation.z), glm::vec3(0.0F, 0.0F, 1.0F)) *
225 glm::rotate(glm::radians(rotation.y), glm::vec3(0.0F, 1.0F, 0.0F)) *
226 glm::rotate(glm::radians(rotation.x), glm::vec3(1.0F, 0.0F, 0.0F));
227 }
228
229 float MathHelpers::normalizeToRange(float value, float min, float max) {
230 const auto width = max - min;
231 const auto offsetValue = value - min;
232
233 return (offsetValue - (floor(offsetValue / width) * width)) + min;
234 }
235
236 glm::vec3 MathHelpers::normalizeSafely(const glm::vec3& vector) {
237 const auto squareSum = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
238
239 if (squareSum < smallFloatEpsilon) {
240 return glm::vec3(0.0F, 0.0F, 0.0F);
241 }
242
243 return vector * glm::inversesqrt(squareSum);
244 }
245} // namespace ne
Definition: Error.h:27
std::string getFullErrorMessage() const
Definition: Error.cpp:84
void showError() const
Definition: Error.cpp:102
static Logger & get()
Definition: Logger.cpp:41
void warn(std::string_view sText, const std::source_location location=std::source_location::current()) const
Definition: Logger.cpp:62
Definition: MathHelpers.hpp:17
static float normalizeToRange(float value, float min, float max)
Definition: MathHelpers.hpp:229
static const float smallFloatEpsilon
Definition: MathHelpers.hpp:111
static glm::vec3 convertNormalizedDirectionToRollPitchYaw(const glm::vec3 &direction)
Definition: MathHelpers.hpp:114
static glm::mat4x4 buildRotationMatrix(const glm::vec3 &rotation)
Definition: MathHelpers.hpp:223
static glm::vec3 calculateReciprocalVector(const glm::vec3 &vector)
Definition: MathHelpers.hpp:199
static glm::vec3 normalizeSafely(const glm::vec3 &vector)
Definition: MathHelpers.hpp:236
static void convertCartesianCoordinatesToSpherical(const glm::vec3 &location, float &radius, float &theta, float &phi)
Definition: MathHelpers.hpp:191
static glm::vec3 convertRollPitchYawToDirection(const glm::vec3 &rotation)
Definition: MathHelpers.hpp:176
static glm::vec3 convertSphericalToCartesianCoordinates(float radius, float theta, float phi)
Definition: MathHelpers.hpp:180
static const glm::vec3 forward
Definition: Globals.h:25