TurtleBrains  0.3.5
High quality, portable, C++ framework for rapid 2D game development.
tb_quaternion.hpp
1 
11 #ifndef TurtleBrains_Quaternion_hpp
12 #define TurtleBrains_Quaternion_hpp
13 
14 #include <turtle_brains/math/tb_math.hpp>
15 #include <turtle_brains/core/tb_configuration.hpp>
16 #include <turtle_brains/core/tb_error.hpp>
17 #include <turtle_brains/core/tb_defines.hpp> //For tb_unsused
18 #include <turtle_brains/math/tb_vector.hpp> //For SkipInitialization
19 
20 namespace TurtleBrains
21 {
22  namespace Math
23  {
24  class Matrix3;
25  class Matrix4;
26 
27  namespace Unstable {
28 
29  class Quaternion;
30 
31  Vector3 MultiplyVector3Quaternion(const Vector3& left, const Quaternion& right);
32  Quaternion* QuaternionMultiply(Quaternion* result, const Quaternion* left, const Quaternion* right);
33 
38  class Quaternion
39  {
40  public:
44  static Quaternion Identity(void) { return Quaternion(0.0f, 0.0f, 0.0f, 1.0f); }
45 
46  union
47  {
48  float mComponents[4];
49 
50 #if defined(tb_visual_cpp)
51 #pragma warning(push)
52 #pragma warning(disable: 4201)
53  struct { float x, y, z, w; };
54 #pragma warning(pop)
55 #else
56  struct { float x, y, z, w; };
57 #endif
58  };
59 
66  inline explicit Quaternion(const SkipInitialization& fastAndStupid)
67  {
68  tb_unused(fastAndStupid);
69  }
70 
74  inline Quaternion(void) :
75  x(0.0f),
76  y(0.0f),
77  z(0.0f),
78  w(1.0f)
79  {
80  }
81 
90  inline Quaternion(const float valueX, const float valueY, const float valueZ, const float valueW) :
91  x(valueX),
92  y(valueY),
93  z(valueZ),
94  w(valueW)
95  {
96  }
97 
104  inline explicit Quaternion(const Vector3& other, const float valueW) :
105  x(other.x),
106  y(other.y),
107  z(other.z),
108  w(valueW)
109  {
110  tb_error("Probably useful to explicitly convert (not copy value) see HAXE: from_angle_axis function");
111  }
112 
118  inline explicit Quaternion(const float* componentArray) :
119  x(componentArray[0]),
120  y(componentArray[1]),
121  z(componentArray[2]),
122  w(componentArray[3])
123  {
124  }
125 
132  inline Quaternion(const Quaternion& other) :
133  x(other.x),
134  y(other.y),
135  z(other.z),
136  w(other.w)
137  {
138  }
139 
144  {
145  }
146 
152  inline Quaternion& operator=(const Quaternion& other)
153  {
154  x = other.x;
155  y = other.y;
156  z = other.z;
157  w = other.w;
158  return *this;
159  }
160 
170  inline bool operator==(const Quaternion& other) const
171  {
172  return (true == IsEqual(x, other.x) && true == IsEqual(y, other.y) &&
173  true == IsEqual(z, other.z) && true == IsEqual(w, other.w)) ? true : false;
174  }
175 
180  inline bool operator!=(const Quaternion& other) const
181  {
182  return (true == operator==(other)) ? false : true;
183  }
184 
188  inline operator const float*(void) const { return mComponents; }
189 
194  inline operator float*(void) { return mComponents; }
195 
199  inline const float& operator[](const int index) const { return mComponents[index]; }
200 
205  inline float& operator[](const int index) { return mComponents[index]; }
206 
207 #if defined(tb_with_math_operators)
208 
212  inline Quaternion operator+(const Quaternion& rightSide) const { return Quaternion(x + rightSide.x, y + rightSide.y, z + rightSide.z, w + rightSide.w); }
213 
218  inline Quaternion& operator+=(const Quaternion& rightSide) { x += rightSide.x; y += rightSide.y; z += rightSide.z; w += rightSide.w; return *this; }
219 
223  inline Quaternion operator-(const Quaternion& rightSide) const { return Quaternion(x - rightSide.x, y - rightSide.y, z - rightSide.z, w - rightSide.w); }
224 
228  inline Quaternion& operator-=(const Quaternion& rightSide) { x -= rightSide.x; y -= rightSide.y; z -= rightSide.z; w -= rightSide.w; return *this; }
229 
233  inline Quaternion operator*(const Quaternion& rightSide) const
234  {
235  Quaternion result(SkipInitialization::kSkipInitialization);
236  QuaternionMultiply(&result, this, &rightSide);
237  return result;
238  }
239 
244  inline Quaternion operator*=(const Quaternion& rightSide)
245  {
246  Quaternion result(SkipInitialization::kSkipInitialization);
247  QuaternionMultiply(&result, this, &rightSide);
248  *this = result;
249  return *this;
250  }
251 
255  inline Quaternion operator*(float scalar) const { return Quaternion(x * scalar, y * scalar, z * scalar, w * scalar); }
256 
261  friend Quaternion operator*(float scalar, const Quaternion& rightSide) { return Quaternion(scalar * rightSide.x, scalar * rightSide.y, scalar * rightSide.z, scalar * rightSide.w); }
262 
267  inline Quaternion& operator*=(float scalar) { x *= scalar; y *= scalar; z *= scalar; w *= scalar; return *this; }
268 
272  inline Quaternion operator/(float scalar) const { return Quaternion(x / scalar, y / scalar, z / scalar, w / scalar); }
273 
278  inline Quaternion& operator/=(float scalar) { x /= scalar; y /= scalar; z /= scalar; w /= scalar; return *this; }
279 
283  inline Quaternion operator-(void) const { return Quaternion(-x, -y, -z, -w); }
284 #endif /* tb_math_with_operators */
285 
290  inline float Magnitude(void) const { return sqrt((x * x) + (y * y) + (z * z) + (w * w)); }
291 
296  inline float MagnitudeSquared(void) const { return (x * x) + (y * y) + (z * z) + (w * w); }
297 
302  inline Quaternion GetNormalized(void) const
303  {
304  const float magnitude(Magnitude());
305  if (true == IsZero(magnitude)) { return Identity(); }
306  return Quaternion(x / magnitude, y / magnitude, z / magnitude, w / magnitude);
307  }
308 
314  inline float Normalize(void)
315  {
316  const float magnitude(Magnitude());
317  if (true == IsZero(magnitude))
318  {
319  x = 0.0f;
320  y = 0.0f;
321  z = 0.0f;
322  w = 1.0f;
323  }
324  else
325  {
326  x /= magnitude;
327  y /= magnitude;
328  z /= magnitude;
329  w /= magnitude;
330  }
331  return magnitude;
332  }
333 
337  inline Vector3 ApplyForward(const Vector3& forward) const
338  {
339  return MultiplyVector3Quaternion(forward, *this);
340  }
341 
345  inline Vector3 ToEuler(void) const
346  {
347  return Vector3(
348  atan2f(2 * mComponents[1] * mComponents[3] - 2 * mComponents[0] * mComponents[2], 1 - 2 * powf(mComponents[1], 2) - 2 * powf(mComponents[2], 2)), // heading
349  asinf(2 * mComponents[0] * mComponents[1] + 2 * mComponents[2] * mComponents[3]), // attitude
350  atan2f(2 * mComponents[0] * mComponents[3] - 2 * mComponents[1] * mComponents[2], 1 - 2 * powf(mComponents[0], 2) - 2 * powf(mComponents[2], 2)) // bank
351  );
352  }
353 
357  inline void ToAngleAxis(Angle& angle, Vector3& axis) const
358  {
359  const Quaternion normalizedSelf((w > 1.0f || w < -1.0f) ? GetNormalized() : *this);
360 
361  const float s = sqrtf(1.0f - normalizedSelf.w * normalizedSelf.w); //var s = Math.sqrt(1 - a[3] * a[3]);
362  angle = Angle(2.0f * acosf(normalizedSelf.w), AngleUnit::Radians);
363  axis = (s < 0.995f) ? Vector3(normalizedSelf.x, normalizedSelf.y, normalizedSelf.z) :
364  Vector3(normalizedSelf.x / s, normalizedSelf.y / s, normalizedSelf.z / s);
365  }
366 
370  inline static Quaternion FromAngleAxis(const Angle angle, const Vector3& axis)
371  {
372  const float s = sinf(angle.AsRadians() * 0.5f);
373  const float c = cosf(angle.AsRadians() * 0.5f);
374  return Quaternion(axis[0] * s, axis[1] * s, axis[2] * s, c);
375  }
376 
380  inline static Quaternion FromForward(const Vector3& forward, const Vector3* up = nullptr)
381  {
382  const tbMath::Vector3 unitForward = forward.GetNormalized();
383  const tbMath::Vector3 unitUp = ((nullptr == up) ? Vector3(0.0f, 1.0f, 0.0f) : up->GetNormalized());
384 
385  const float dot = Vector3DotProduct(&unitUp, &unitForward);
386  Vector3 axis(SkipInitialization::kSkipInitialization);
387  Vector3CrossProduct(&axis, &unitUp, &unitForward);
388  return Quaternion(axis, dot + 1.0f);
389  }
390 
394  inline static Quaternion FromEuler(const Vector3& euler)
395  {
396  const float halfHeading = euler.x * 0.5f;
397  const float halfPitch = euler.y * 0.5f;
398  const float halfRoll = euler.z * 0.5f;
399 
400  const float c1 = cosf(halfHeading);
401  const float s1 = sinf(halfHeading);
402  const float c2 = cosf(halfPitch);
403  const float s2 = sinf(halfPitch);
404  const float c3 = cosf(halfRoll);
405  const float s3 = sinf(halfRoll);
406  const float c1c2 = c1*c2;
407  const float s1s2 = s1*s2;
408  return Quaternion(
409  c1c2*s3 + s1s2*c3,
410  s1*c2*c3 + c1*s2*s3,
411  c1*s2*c3 - s1*c2*s3,
412  c1c2*c3 - s1s2*s3
413  );
414  }
415 
416  //These are defined in tb_matrix_quaternion.h
417  static Quaternion FromMatrix(const Matrix3& matrix);
418  static Quaternion FromMatrix(const Matrix4& matrix);
419 
420  };
421 
422  //-------------------------------------------------------------------------------------------------------------------//
423  //-------------------------------------------------------------------------------------------------------------------//
424  //-------------------------------------------------------------------------------------------------------------------//
425 
436  inline Quaternion* QuaternionAdd(Quaternion* result, const Quaternion* left, const Quaternion* right)
437  {
438  tb_error_if(nullptr == left, "tbExternalError: Invalid parameter for left, expected valid pointer.");
439  tb_error_if(nullptr == right, "tbExternalError: Invalid parameter for right, expected valid pointer.");
440  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
441 
442  result->x = left->x + right->x;
443  result->y = left->y + right->y;
444  result->z = left->z + right->z;
445  result->w = left->w + right->w;
446  return result;
447  }
448 
459  inline Quaternion* QuaternionSubtract(Quaternion* result, const Quaternion* left, const Quaternion* right)
460  {
461  tb_error_if(nullptr == left, "tbExternalError: Invalid parameter for left, expected valid pointer.");
462  tb_error_if(nullptr == right, "tbExternalError: Invalid parameter for right, expected valid pointer.");
463  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
464 
465  result->x = left->x - right->x;
466  result->y = left->y - right->y;
467  result->z = left->z - right->z;
468  result->w = left->w - right->w;
469  return result;
470  }
471 
481  inline Quaternion* QuaternionMultiply(Quaternion* result, const Quaternion* left, const Quaternion* right)
482  {
483  tb_error_if(nullptr == left, "tbExternalError: Invalid parameter for left, expected valid pointer.");
484  tb_error_if(nullptr == right, "tbExternalError: Invalid parameter for right, expected valid pointer.");
485  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
486  tb_error_if(result == left || result == right, "tbExternalError: Invalid parameter for result, expected different location than left or right.")
487 
488  const Quaternion& a(*left);
489  const Quaternion& b(*right);
490  //(*result)[0] = a[0] * b[3] + a[3] * b[0] + a[1] * b[2] - a[2] * b[1];
491  //(*result)[1] = a[1] * b[3] + a[3] * b[1] + a[2] * b[0] - a[0] * b[2];
492  //(*result)[2] = a[2] * b[3] + a[3] * b[2] + a[0] * b[1] - a[1] * b[0];
493  //(*result)[3] = a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2];
494 
495  result->x = a.x * b.w + a.w * b.x + a.y * b.z - a.z * b.y;
496  result->y = a.y * b.w + a.w * b.y + a.z * b.x - a.x * b.z;
497  result->z = a.z * b.w + a.w * b.z + a.x * b.y - a.y * b.x;
498  result->w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z;
499  return result;
500  }
501 
511  inline Quaternion* QuaternionScale(Quaternion* result, const Quaternion* input, const float scalar)
512  {
513  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
514  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
515 
516  result->x = input->x * scalar;
517  result->y = input->y * scalar;
518  result->z = input->z * scalar;
519  result->w = input->w * scalar;
520  return result;
521  }
522 
532  inline Quaternion* QuaternionScaleDivide(Quaternion* result, const Quaternion* input, const float scalar)
533  {
534  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
535  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
536 
537  result->x = input->x / scalar;
538  result->y = input->y / scalar;
539  result->z = input->z / scalar;
540  result->w = input->w / scalar;
541  return result;
542  }
543 
553  inline Quaternion* QuaternionNegate(Quaternion* result, const Quaternion* input)
554  {
555  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
556  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
557 
558  result->x = -input->x;
559  result->y = -input->y;
560  result->z = -input->z;
561  result->w = -input->w;
562  return result;
563  }
564 
568  inline float QuaternionMagnitude(const Quaternion* input)
569  {
570  return sqrtf((input->x * input->x) + (input->y * input->y) + (input->z * input->z) + (input->w * input->w));
571  }
572 
577  inline float QuaternionMagnitudeSquared(const Quaternion* input)
578  {
579  return (input->x * input->x) + (input->y * input->y) + (input->z * input->z) + (input->w * input->w);
580  }
581 
590  inline Quaternion* QuaternionNormalize(Quaternion* result, const Quaternion* input)
591  {
592  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
593  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
594 
595  const float magnitude = QuaternionMagnitude(input);
596  if (true == IsZero(magnitude))
597  {
598  result->x = 0.0f;
599  result->y = 0.0f;
600  result->z = 0.0f;
601  result->w = 1.0f;
602  }
603  else
604  {
605  result->x = input->x / magnitude;
606  result->y = input->y / magnitude;
607  result->z = input->z / magnitude;
608  result->w = input->w / magnitude;
609  }
610  return result;
611  }
612 
624  inline Quaternion* QuaternionNormalizeMagnitude(Quaternion* result, const Quaternion* input, float &magnitude)
625  {
626  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
627  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
628 
629  magnitude = QuaternionMagnitude(input);
630  if (true == IsZero(magnitude))
631  {
632  result->x = 0.0f;
633  result->y = 0.0f;
634  result->z = 0.0f;
635  result->w = 1.0f;
636  }
637  else
638  {
639  result->x = input->x / magnitude;
640  result->y = input->y / magnitude;
641  result->z = input->z / magnitude;
642  result->w = input->w / magnitude;
643  }
644  return result;
645  }
646 
656  inline Quaternion* QuaternionInverse(Quaternion* result, const Quaternion* input)
657  {
658  tb_error_if(nullptr == input, "tbExternalError: Invalid parameter for input, expected valid pointer.");
659  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
660 
661  result->x = -input->x;
662  result->y = -input->y;
663  result->z = -input->z;
664  result->w = input->w;
665  return result;
666  }
667 
668  inline float QuaternionDotProduct(const Quaternion* leftSide, const Quaternion* rightSide)
669  {
670  tb_error_if(nullptr == leftSide, "tbExternalError: Invalid parameter for leftSide, expected valid pointer.");
671  tb_error_if(nullptr == rightSide, "tbExternalError: Invalid parameter for rightSide, expected valid pointer.");
672  return (leftSide->x * rightSide->x) + (leftSide->y * rightSide->y) + (leftSide->z * rightSide->z) + (leftSide->w * rightSide->w);
673  }
674 
675  //Linear Interpolation
676  inline Quaternion* QuaternionLerp(Quaternion* result, const Quaternion* start, const Quaternion* final, const float time)
677  {
678  tb_error_if(nullptr == start, "tbExternalError: Invalid parameter for start, expected valid pointer.");
679  tb_error_if(nullptr == final, "tbExternalError: Invalid parameter for final, expected valid pointer.");
680  tb_error_if(nullptr == result, "tbExternalError: Invalid parameter for result, expected valid pointer.");
681  tb_error_if(result == start || result == final, "tbExternalError: Invalid parameter for result, expected different location than start or final.")
682 
683  //result = a + (b - a) * t;
684  QuaternionSubtract(result, final, start); // b - a
685  QuaternionScale(result, result, time); //(b - a) * t
686  QuaternionAdd(result, start, result); //result = a + (b - a) * t;
687  result->Normalize();
688  return result;
689  }
690 
691  //Spherical Interpolation
692  inline Quaternion QuaternionSlerp(const Quaternion& start, const Quaternion& final, const float time)
693  {
694  //a = start, b = final
695 
696  float dot = QuaternionDotProduct(&start, &final);
697 
698  Quaternion a(start);
699  if (dot < 0.0f)
700  {
701  QuaternionNegate(&a, &start);
702  dot = -dot;
703  }
704 
705  if (dot > 0.995f)
706  { //Too close to bother with.
707  Quaternion result;
708  QuaternionLerp(&result, &start, &final, time);
709  return result;
710  }
711 
712  //If inputs are already normalized, this winds up doing nothing.
713  dot = tbMath::Clamp(dot, -1.0f, 1.0f);
714 
715  Quaternion temporary; //= b - a * d;
716  QuaternionScale(&temporary, &a, dot); //a * dot
717  QuaternionSubtract(&temporary, &final, &temporary); //b - (a * dot)
718  temporary.Normalize();
719 
720  const float theta = acosf(dot) * time;
721  QuaternionScale(&temporary, &temporary, sinf(theta));
722  QuaternionScale(&a, &a, cosf(theta));
723 
724  //return a * Math.cos(theta) + c * Math.sin(theta);
725  Quaternion result(SkipInitialization::kSkipInitialization);
726  QuaternionAdd(&result, &a, &temporary);
727  return result;
728  }
729 
730  inline Vector3 MultiplyVector3Quaternion(const Vector3& left, const Quaternion& right)
731  {
732  Vector3 uv(SkipInitialization::kSkipInitialization);
733  Vector3 uuv(SkipInitialization::kSkipInitialization);
734  Vector3CrossProduct(&uv, reinterpret_cast<const Vector3*>(&right[0]), &left);
735  Vector3CrossProduct(&uuv, reinterpret_cast<const Vector3*>(&right[0]), &uv);
736  return left + ((uv * right.w) + uuv) * 2.0f;
737  }
738 
739  }; /* namespace Unstable */
740  }; /* namespace Math */
741 }; /* namespace TurtleBrains */
742 
743 namespace tbMath = TurtleBrains::Math;
744 
745 #endif /* TurtleBrains_Quaternion_hpp */
static Quaternion FromEuler(const Vector3 &euler)
Definition: tb_quaternion.hpp:394
AngleType< float > Angle
Definition: tb_angle.hpp:154
bool operator!=(const Quaternion &other) const
Definition: tb_quaternion.hpp:180
Quaternion(const float *componentArray)
Definition: tb_quaternion.hpp:118
Type AsRadians(void) const
Definition: tb_angle.hpp:79
Contains objects and functions for dealing with Vector and Matrix math.
static Quaternion Identity(void)
Definition: tb_quaternion.hpp:44
bool operator==(const Quaternion &other) const
Definition: tb_quaternion.hpp:170
#define tb_error(message,...)
Definition: tb_error.hpp:23
static Quaternion FromForward(const Vector3 &forward, const Vector3 *up=nullptr)
Definition: tb_quaternion.hpp:380
Quaternion(const SkipInitialization &fastAndStupid)
Definition: tb_quaternion.hpp:66
Definition: tb_vector.hpp:317
Vector3 ToEuler(void) const
Definition: tb_quaternion.hpp:345
Definition: tb_matrix.hpp:111
Vector3 ApplyForward(const Vector3 &forward) const
Definition: tb_quaternion.hpp:337
float Magnitude(void) const
Definition: tb_quaternion.hpp:290
bool IsZero(const Type &value)
Definition: tb_math.hpp:53
Quaternion & operator=(const Quaternion &other)
Definition: tb_quaternion.hpp:152
Definition: tb_quaternion.hpp:38
Quaternion(const Quaternion &other)
Definition: tb_quaternion.hpp:132
Here is some information about the primary namespace.
Definition: tb_application_dialog.hpp:21
#define tb_unused(parameter)
Definition: tb_defines.hpp:25
constexpr const T & Clamp(const T &value, const T &minimumValue, const T &maximumValue) noexcept
Definition: tb_math.hpp:106
Quaternion GetNormalized(void) const
Definition: tb_quaternion.hpp:302
Definition: tb_matrix.hpp:527
SkipInitialization
Definition: tb_vector.hpp:30
static Quaternion FromAngleAxis(const Angle angle, const Vector3 &axis)
Definition: tb_quaternion.hpp:370
bool IsEqual(const Type &leftValue, const Type &rightValue)
Definition: tb_math.hpp:30
Specifies the angle input value is in units of Radians.
float MagnitudeSquared(void) const
Definition: tb_quaternion.hpp:296
float & operator[](const int index)
Definition: tb_quaternion.hpp:205
const float & operator[](const int index) const
Definition: tb_quaternion.hpp:199
Quaternion(const float valueX, const float valueY, const float valueZ, const float valueW)
Definition: tb_quaternion.hpp:90
float Vector3DotProduct(const Vector3 *leftSide, const Vector3 *rightSide)
Definition: tb_vector.hpp:1204
~Quaternion(void)
Definition: tb_quaternion.hpp:143
Vector3 * Vector3CrossProduct(Vector3 *result, const Vector3 *leftSide, const Vector3 *rightSide)
Definition: tb_vector.hpp:1234
Quaternion(const Vector3 &other, const float valueW)
Definition: tb_quaternion.hpp:104
Definition: tb_angle.hpp:34
float Normalize(void)
Definition: tb_quaternion.hpp:314
void ToAngleAxis(Angle &angle, Vector3 &axis) const
Definition: tb_quaternion.hpp:357
#define tb_error_if(errorTest, message,...)
Definition: tb_error.hpp:42
Quaternion(void)
Definition: tb_quaternion.hpp:74