Aether3D Game Engine
Quaternion.hpp
1 #ifndef QUATERNION_H
2 #define QUATERNION_H
3 
4 #include <cmath>
5 #include <cassert>
6 #include "Vec3.hpp"
7 #include "Matrix.hpp"
8 
9 namespace ae3d
10 {
12  struct Quaternion
13  {
15  Quaternion() : x( 0 ), y( 0 ), z( 0 ), w( 1 ) {}
16 
21  Quaternion( const Vec3& vec, float aW ) : x( vec.x ), y( vec.y ), z( vec.z ), w( aW ) {}
22 
24  Quaternion( const Quaternion& v ) = default;
25 
27  Quaternion( Quaternion&& v ) noexcept = default;
28 
30  Quaternion& operator=( Quaternion&& ) noexcept = default;
31 
33  Quaternion& operator=( const Quaternion& ) = default;
34 
41  Vec3 operator*( const Vec3& vec ) const
42  {
43  // FIXME: vec was originally normalized here but I removed normalization because it caused
44  // too fast camera movement and errorneous movement at slow speeds. [2014-12-08]
45 
46  Quaternion vecQuat, resQuat;
47  vecQuat.x = vec.x;
48  vecQuat.y = vec.y;
49  vecQuat.z = vec.z;
50  vecQuat.w = 0.0f;
51 
52  resQuat = vecQuat * Conjugate();
53  resQuat = *this * resQuat;
54 
55  return Vec3( resQuat.x, resQuat.y, resQuat.z );
56  }
57 
63  Quaternion operator*( const Quaternion& aQ ) const
64  {
65  return Quaternion( Vec3( w * aQ.x + x * aQ.w + y * aQ.z - z * aQ.y,
66  w * aQ.y + y * aQ.w + z * aQ.x - x * aQ.z,
67  w * aQ.z + z * aQ.w + x * aQ.y - y * aQ.x ),
68  w * aQ.w - x * aQ.x - y * aQ.y - z * aQ.z );
69  }
70 
76  bool operator==( const Quaternion& q ) const
77  {
78  const float acceptableDelta = 0.00001f;
79 
80  return std::fabs(x - q.x) < acceptableDelta &&
81  std::fabs(y - q.y) < acceptableDelta &&
82  std::fabs(z - q.z) < acceptableDelta &&
83  std::fabs(w - q.w) < acceptableDelta;
84  }
85 
91  bool operator!=( const Quaternion& q ) const
92  {
93  return !(*this == q);
94  }
95 
103  {
104  return Quaternion( Vec3( -x, -y, -z ), w );
105  }
106 
113  float FindTwist( const Vec3& axis ) const
114  {
115  // Get the plane the axis is a normal of.
116  Vec3 orthonormal1, orthonormal2;
117  FindOrthonormals( axis, orthonormal1, orthonormal2 );
118 
119  Vec3 transformed = *this * orthonormal1;// = Vec3.Transform(orthonormal1, q);
120 
121  //project transformed vector onto plane
122  Vec3 flattened = transformed - axis * Vec3::Dot( transformed, axis );
123  flattened = flattened.Normalized();
124 
125  // get angle between original vector and projected transform to get angle around normal
126  return std::acos( Vec3::Dot( orthonormal1, flattened ) );
127  }
128 
133  void GetAxisAngle( Vec3& outAxis, float& outAngleRad ) const
134  {
135  const float scale = std::sqrt( x * x + y * y + z * z );
136 
137  if (std::fabs( scale ) < 0.00001f)
138  {
139  outAxis = Vec3( 0, 0, 0 );
140  }
141  else
142  {
143  outAxis.x = x / scale;
144  outAxis.y = y / scale;
145  outAxis.z = z / scale;
146  }
147 
148  outAngleRad = std::acos( w ) * 2;
149  }
150 
153  static Quaternion FromEuler( const Vec3& euler )
154  {
155  const Vec3 xAxis( 1, 0, 0 );
156  const Vec3 yAxis( 0, 1, 0 );
157  const Vec3 zAxis( 0, 0, 1 );
158 
159  Quaternion qx, qy, qz, qt;
160  qx.FromAxisAngle( xAxis, euler.x );
161  qy.FromAxisAngle( yAxis, euler.y );
162  qz.FromAxisAngle( zAxis, euler.z );
163  qt = qx * qy;
164  return qt * qz;
165  }
166 
171  void FromAxisAngle( const Vec3& axis, float angleDeg )
172  {
173  float angleRad = angleDeg * (3.1418693659f / 180.0f);
174 
175  angleRad *= 0.5f;
176 
177  const float sinAngle = std::sin( angleRad );
178 
179  x = axis.x * sinAngle;
180  y = axis.y * sinAngle;
181  z = axis.z * sinAngle;
182  w = std::cos( angleRad );
183  }
184 
189  static Quaternion CreateFromAxisAngle( const Vec3& axis, float angleDeg )
190  {
191  Quaternion q;
192  q.FromAxisAngle( axis, angleDeg );
193  return q;
194  }
195 
197  void FromMatrix( const Matrix44& mat )
198  {
199  const float trace = 1.0f + mat.m[0] + mat.m[5] + mat.m[10];
200 
201  if (trace > 0.0f)
202  {
203  assert( trace > 0 && "Quaternion from matrix: trying to get an sqrt of a negative value" );
204 
205  const float S = sqrtf( trace ) * 2.0f;
206  x = ( mat.m[9] - mat.m[6] ) / S;
207  y = ( mat.m[2] - mat.m[8] ) / S;
208  z = ( mat.m[4] - mat.m[1] ) / S;
209  w = 0.25f * S;
210  }
211  else if (mat.m[0] > mat.m[5] && mat.m[0] > mat.m[10])
212  {
213  const float val = 1.0f + mat.m[0] - mat.m[5] - mat.m[10];
214  assert( val > 0 && "Quaternion from matrix: trying to get an sqrt of a negative value" );
215 
216  const float S = std::sqrt( val ) * 2.0f;
217  x = 0.25f * S;
218  y = (mat.m[4] + mat.m[1] ) / S;
219  z = (mat.m[2] + mat.m[8] ) / S;
220  w = (mat.m[9] - mat.m[6] ) / S;
221  }
222  else if (mat.m[5] > mat.m[10])
223  {
224  const float val = 1.0f + mat.m[5] - mat.m[0] - mat.m[10];
225  assert( val > 0 && "Quaternion from matrix: trying to get an sqrt of a negative value" );
226 
227  const float S = std::sqrt(val ) * 2.0f;
228  x = (mat.m[4] + mat.m[1] ) / S;
229  y = 0.25f * S;
230  z = (mat.m[9] + mat.m[6] ) / S;
231  w = (mat.m[2] - mat.m[8] ) / S;
232  }
233  else
234  {
235  const float val = 1.0f + mat.m[10] - mat.m[0] - mat.m[5];
236  assert( val > 0 && "Quaternion from matrix: trying to get an sqrt of a negative value" );
237 
238  const float S = std::sqrt( val ) * 2.0f;
239  x = (mat.m[2] + mat.m[8] ) / S;
240  y = (mat.m[9] + mat.m[6] ) / S;
241  z = 0.25f * S;
242  w = (mat.m[4] - mat.m[1] ) / S;
243  }
244  }
245 
251  void GetMatrix( Matrix44& outMatrix ) const
252  {
253  const float x2 = x * x;
254  const float y2 = y * y;
255  const float z2 = z * z;
256  const float xy = x * y;
257  const float xz = x * z;
258  const float yz = y * z;
259  const float wx = w * x;
260  const float wy = w * y;
261  const float wz = w * z;
262 
263  outMatrix.m[ 0] = 1 - 2 * (y2 + z2);
264  outMatrix.m[ 1] = 2 * (xy - wz);
265  outMatrix.m[ 2] = 2 * (xz + wy);
266  outMatrix.m[ 3] = 0;
267  outMatrix.m[ 4] = 2 * (xy + wz);
268  outMatrix.m[ 5] = 1 - 2 * (x2 + z2);
269  outMatrix.m[ 6] = 2 * (yz - wx);
270  outMatrix.m[ 7] = 0;
271  outMatrix.m[ 8] = 2 * (xz - wy);
272  outMatrix.m[ 9] = 2 * (yz + wx);
273  outMatrix.m[10] = 1 - 2 * (x2 + y2);
274  outMatrix.m[11] = 0;
275  outMatrix.m[12] = 0;
276  outMatrix.m[13] = 0;
277  outMatrix.m[14] = 0;
278  outMatrix.m[15] = 1;
279  }
280 
285  Vec3 GetEuler() const
286  {
287  Vec3 out;
288  out.z = std::atan2( 2 * y * w - 2 * x * z, 1 - 2 * y * y - 2 * z * z );
289  out.y = std::asin( 2 * x * y + 2 * z * w );
290  out.x = std::atan2( 2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z );
291  return out / (3.14159265358979f / 180.0f);
292  }
293 
295  void Normalize()
296  {
297  const float mag2 = w * w + x * x + y * y + z * z;
298  const float acceptableDelta = 0.00001f;
299 
300  if (std::fabs( mag2 ) > acceptableDelta && std::fabs( mag2 - 1.0f ) > acceptableDelta)
301  {
302  const float oneOverMag = 1.0f / std::sqrt( mag2 );
303 
304  x *= oneOverMag;
305  y *= oneOverMag;
306  z *= oneOverMag;
307  w *= oneOverMag;
308  }
309  }
310 
312  float x;
314  float y;
316  float z;
318  float w;
319 
320  private:
321  void FindOrthonormals( const Vec3& normal, Vec3& orthonormal1, Vec3& orthonormal2 ) const
322  {
323  Matrix44 orthoX( 90, 0, 0 );
324  Matrix44 orthoY( 0, 90, 0 );
325 
326  Vec3 ww;
327  Matrix44::TransformDirection( normal, orthoX, &ww );
328  const float dot = Vec3::Dot( normal, ww );
329 
330  if (std::fabs( dot ) > 0.6f)
331  {
332  Matrix44::TransformDirection( normal, orthoY, &ww );
333  }
334 
335  ww = ww.Normalized();
336 
337  orthonormal1 = Vec3::Cross( normal, ww );
338  orthonormal1 = orthonormal1.Normalized();
339  orthonormal2 = Vec3::Cross( normal, orthonormal1 );
340  orthonormal2 = orthonormal2.Normalized();
341  }
342  };
343 }
344 #endif
Quaternion()
Constructor.
Definition: Quaternion.hpp:15
Quaternion & operator=(Quaternion &&) noexcept=default
Move assignment.
void FromMatrix(const Matrix44 &mat)
Definition: Quaternion.hpp:197
float x
X coordinate.
Definition: Vec3.hpp:15
Definition: AudioClip.hpp:4
static Quaternion FromEuler(const Vec3 &euler)
Definition: Quaternion.hpp:153
Quaternion operator*(const Quaternion &aQ) const
Applies rotation aQ to this quaternion and returns the result.
Definition: Quaternion.hpp:63
Vec3 GetEuler() const
Definition: Quaternion.hpp:285
float z
Definition: Quaternion.hpp:316
3-component vector.
Definition: Vec3.hpp:12
void GetMatrix(Matrix44 &outMatrix) const
Definition: Quaternion.hpp:251
float y
Definition: Quaternion.hpp:314
bool operator==(const Quaternion &q) const
Definition: Quaternion.hpp:76
static Quaternion CreateFromAxisAngle(const Vec3 &axis, float angleDeg)
Definition: Quaternion.hpp:189
float w
Definition: Quaternion.hpp:318
Quaternion Conjugate() const
Definition: Quaternion.hpp:102
float x
Definition: Quaternion.hpp:312
float z
Z coordinate.
Definition: Vec3.hpp:19
static Vec3 Cross(const Vec3 &v1, const Vec3 &v2)
Cross product.
Definition: Vec3.hpp:28
void Normalize()
Definition: Quaternion.hpp:295
float FindTwist(const Vec3 &axis) const
Finds twist angle around axis.
Definition: Quaternion.hpp:113
static float Dot(const Vec3 &v1, const Vec3 &v2)
Definition: Vec3.hpp:100
void FromAxisAngle(const Vec3 &axis, float angleDeg)
Definition: Quaternion.hpp:171
void GetAxisAngle(Vec3 &outAxis, float &outAngleRad) const
Definition: Quaternion.hpp:133
Vec3 Normalized() const
Definition: Vec3.hpp:311
float y
Y coordinate.
Definition: Vec3.hpp:17
Quaternion(const Vec3 &vec, float aW)
Definition: Quaternion.hpp:21
bool operator!=(const Quaternion &q) const
Definition: Quaternion.hpp:91
Stores an orientation.
Definition: Quaternion.hpp:12
Vec3 operator*(const Vec3 &vec) const
Multiplying a quaternion q with a vector v applies the q-rotation to vec.
Definition: Quaternion.hpp:41