Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Quaternion.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2009 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "Quaternion.h"
21 #include "MathUtil.h"
22 
23 const float EPSILON=0.0001f;
24 
25 
27  m_W(1)
28 {
29 }
30 
31 CQuaternion::CQuaternion(float x, float y, float z, float w) :
32  m_V(x, y, z), m_W(w)
33 {
34 }
35 
37 {
38  CQuaternion Temp;
39  Temp.m_W = m_W + quat.m_W;
40  Temp.m_V = m_V + quat.m_V;
41  return Temp;
42 }
43 
45 {
46  *this = *this + quat;
47  return *this;
48 }
49 
51 {
52  CQuaternion Temp;
53  Temp.m_W = m_W - quat.m_W;
54  Temp.m_V = m_V - quat.m_V;
55  return Temp;
56 }
57 
59 {
60  *this = *this - quat;
61  return *this;
62 }
63 
65 {
66  CQuaternion Temp;
67  Temp.m_W = (m_W * quat.m_W) - (m_V.Dot(quat.m_V));
68  Temp.m_V = (m_V.Cross(quat.m_V)) + (quat.m_V * m_W) + (m_V * quat.m_W);
69  return Temp;
70 }
71 
73 {
74  *this = *this * quat;
75  return *this;
76 }
77 
79 {
80  CQuaternion Temp;
81  Temp.m_W = m_W * factor;
82  Temp.m_V = m_V * factor;
83  return Temp;
84 }
85 
86 
87 float CQuaternion::Dot(const CQuaternion& quat) const
88 {
89  return
90  m_V.X * quat.m_V.X +
91  m_V.Y * quat.m_V.Y +
92  m_V.Z * quat.m_V.Z +
93  m_W * quat.m_W;
94 }
95 
96 void CQuaternion::FromEulerAngles (float x, float y, float z)
97 {
98  float cr, cp, cy;
99  float sr, sp, sy;
100 
101  CQuaternion QRoll, QPitch, QYaw;
102 
103  cr = cosf(x * 0.5f);
104  cp = cosf(y * 0.5f);
105  cy = cosf(z * 0.5f);
106 
107  sr = sinf(x * 0.5f);
108  sp = sinf(y * 0.5f);
109  sy = sinf(z * 0.5f);
110 
111  QRoll.m_V = CVector3D(sr, 0, 0);
112  QRoll.m_W = cr;
113 
114  QPitch.m_V = CVector3D(0, sp, 0);
115  QPitch.m_W = cp;
116 
117  QYaw.m_V = CVector3D(0, 0, sy);
118  QYaw.m_W = cy;
119 
120  (*this) = QYaw * QPitch * QRoll;
121 }
123 {
124  float heading, attitude, bank;
125  float sqw = m_W * m_W;
126  float sqx = m_V.X*m_V.X;
127  float sqy = m_V.Y*m_V.Y;
128  float sqz = m_V.Z*m_V.Z;
129  float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
130  float test = m_V.X*m_V.Y + m_V.Z*m_W;
131  if (test > (.5f-EPSILON)*unit)
132  { // singularity at north pole
133  heading = 2 * atan2( m_V.X, m_W);
134  attitude = (float)M_PI/2;
135  bank = 0;
136  }
137  else if (test < (-.5f+EPSILON)*unit)
138  { // singularity at south pole
139  heading = -2 * atan2(m_V.X, m_W);
140  attitude = -(float)M_PI/2;
141  bank = 0;
142  }
143  else
144  {
145  heading = atan2(2.f * (m_V.X*m_V.Y + m_V.Z*m_W),(sqx - sqy - sqz + sqw));
146  bank = atan2(2.f * (m_V.Y*m_V.Z + m_V.X*m_W),(-sqx - sqy + sqz + sqw));
147  attitude = asin(-2.f * (m_V.X*m_V.Z - m_V.Y*m_W));
148  }
149  return CVector3D(bank, attitude, heading);
150 }
151 
153 {
154  CMatrix3D result;
155  ToMatrix(result);
156  return result;
157 }
158 
159 void CQuaternion::ToMatrix(CMatrix3D& result) const
160 {
161  float wx, wy, wz, xx, xy, xz, yy, yz, zz;
162 
163  // calculate coefficients
164  xx = m_V.X * m_V.X * 2.f;
165  xy = m_V.X * m_V.Y * 2.f;
166  xz = m_V.X * m_V.Z * 2.f;
167 
168  yy = m_V.Y * m_V.Y * 2.f;
169  yz = m_V.Y * m_V.Z * 2.f;
170 
171  zz = m_V.Z * m_V.Z * 2.f;
172 
173  wx = m_W * m_V.X * 2.f;
174  wy = m_W * m_V.Y * 2.f;
175  wz = m_W * m_V.Z * 2.f;
176 
177  result._11 = 1.0f - (yy + zz);
178  result._12 = xy - wz;
179  result._13 = xz + wy;
180  result._14 = 0;
181 
182  result._21 = xy + wz;
183  result._22 = 1.0f - (xx + zz);
184  result._23 = yz - wx;
185  result._24 = 0;
186 
187  result._31 = xz - wy;
188  result._32 = yz + wx;
189  result._33 = 1.0f - (xx + yy);
190  result._34 = 0;
191 
192  result._41 = 0;
193  result._42 = 0;
194  result._43 = 0;
195  result._44 = 1;
196 }
197 
198 void CQuaternion::Slerp(const CQuaternion& from, const CQuaternion& to, float ratio)
199 {
200  float to1[4];
201  float omega, cosom, sinom, scale0, scale1;
202 
203  // calc cosine
204  cosom = from.Dot(to);
205 
206 
207  // adjust signs (if necessary)
208  if (cosom < 0.0)
209  {
210  cosom = -cosom;
211  to1[0] = -to.m_V.X;
212  to1[1] = -to.m_V.Y;
213  to1[2] = -to.m_V.Z;
214  to1[3] = -to.m_W;
215  }
216  else
217  {
218  to1[0] = to.m_V.X;
219  to1[1] = to.m_V.Y;
220  to1[2] = to.m_V.Z;
221  to1[3] = to.m_W;
222  }
223 
224  // calculate coefficients
225  if ((1.0f - cosom) > EPSILON)
226  {
227  // standard case (slerp)
228  omega = acosf(cosom);
229  sinom = sinf(omega);
230  scale0 = sinf((1.0f - ratio) * omega) / sinom;
231  scale1 = sinf(ratio * omega) / sinom;
232  }
233  else
234  {
235  // "from" and "to" quaternions are very close
236  // ... so we can do a linear interpolation
237  scale0 = 1.0f - ratio;
238  scale1 = ratio;
239  }
240 
241  // calculate final values
242  m_V.X = scale0 * from.m_V.X + scale1 * to1[0];
243  m_V.Y = scale0 * from.m_V.Y + scale1 * to1[1];
244  m_V.Z = scale0 * from.m_V.Z + scale1 * to1[2];
245  m_W = scale0 * from.m_W + scale1 * to1[3];
246 }
247 
248 void CQuaternion::Nlerp(const CQuaternion& from, const CQuaternion& to, float ratio)
249 {
250  float c = from.Dot(to);
251  if (c < 0.f)
252  *this = from - (to + from) * ratio;
253  else
254  *this = from + (to - from) * ratio;
255  Normalize();
256 }
257 
258 ///////////////////////////////////////////////////////////////////////////////////////////////
259 // FromAxisAngle: create a quaternion from axis/angle representation of a rotation
260 void CQuaternion::FromAxisAngle(const CVector3D& axis, float angle)
261 {
262  float sinHalfTheta=(float) sin(angle/2);
263  float cosHalfTheta=(float) cos(angle/2);
264 
265  m_V.X=axis.X*sinHalfTheta;
266  m_V.Y=axis.Y*sinHalfTheta;
267  m_V.Z=axis.Z*sinHalfTheta;
268  m_W=cosHalfTheta;
269 }
270 
271 ///////////////////////////////////////////////////////////////////////////////////////////////
272 // ToAxisAngle: convert the quaternion to axis/angle representation of a rotation
273 void CQuaternion::ToAxisAngle(CVector3D& axis, float& angle)
274 {
275  CQuaternion q = *this;
276  q.Normalize();
277  angle = acosf(q.m_W) * 2.f;
278  float sin_a = sqrtf(1.f - q.m_W * q.m_W);
279  if (fabsf(sin_a) < 0.0005f) sin_a = 1.f;
280  axis.X = q.m_V.X / sin_a;
281  axis.Y = q.m_V.Y / sin_a;
282  axis.Z = q.m_V.Z / sin_a;
283 }
284 
285 
286 ///////////////////////////////////////////////////////////////////////////////////////////////
287 // Normalize: normalize this quaternion
289 {
290  float lensqrd=SQR(m_V.X)+SQR(m_V.Y)+SQR(m_V.Z)+SQR(m_W);
291  if (lensqrd>0) {
292  float invlen=1.0f/sqrtf(lensqrd);
293  m_V*=invlen;
294  m_W*=invlen;
295  }
296 }
297 
298 ///////////////////////////////////////////////////////////////////////////////////////////////
299 
301 {
302  // v' = q * v * q^-1
303  // (where v is the quat. with w=0, xyz=vec)
304 
305  return (*this * CQuaternion(vec.X, vec.Y, vec.Z, 0.f) * GetInverse()).m_V;
306 }
307 
309 {
310  // (x,y,z,w)^-1 = (-x/l^2, -y/l^2, -z/l^2, w/l^2) where l^2=x^2+y^2+z^2+w^2
311  // Since we're only using quaternions for rotation, they should always have unit
312  // length, so assume l=1
313  return CQuaternion(-m_V.X, -m_V.Y, -m_V.Z, m_W);
314 }
#define M_PI
Definition: wposix.h:64
float _21
Definition: Matrix3D.h:42
void FromEulerAngles(float x, float y, float z)
Definition: Quaternion.cpp:96
CQuaternion GetInverse() const
Definition: Quaternion.cpp:308
float _22
Definition: Matrix3D.h:43
float _12
Definition: Matrix3D.h:43
float _44
Definition: Matrix3D.h:45
CQuaternion operator*(const CQuaternion &quat) const
Definition: Quaternion.cpp:64
CQuaternion & operator*=(const CQuaternion &quat)
Definition: Quaternion.cpp:72
float Dot(const CVector3D &vector) const
Definition: Vector3D.cpp:48
float _43
Definition: Matrix3D.h:44
CVector3D Cross(const CVector3D &vector) const
Definition: Vector3D.cpp:55
float m_W
Definition: Quaternion.h:28
CVector3D Rotate(const CVector3D &vec) const
Definition: Quaternion.cpp:300
#define SQR(x)
Definition: MathUtil.h:23
float _32
Definition: Matrix3D.h:43
float _11
Definition: Matrix3D.h:42
float _33
Definition: Matrix3D.h:44
CQuaternion & operator-=(const CQuaternion &quat)
Definition: Quaternion.cpp:58
CVector3D ToEulerAngles()
Definition: Quaternion.cpp:122
CQuaternion operator+(const CQuaternion &quat) const
Definition: Quaternion.cpp:36
void ToAxisAngle(CVector3D &axis, float &angle)
Definition: Quaternion.cpp:273
float X
Definition: Vector3D.h:31
CMatrix3D ToMatrix() const
Definition: Quaternion.cpp:152
float Y
Definition: Vector3D.h:31
float Dot(const CQuaternion &quat) const
Definition: Quaternion.cpp:87
float _13
Definition: Matrix3D.h:44
float _31
Definition: Matrix3D.h:42
float _42
Definition: Matrix3D.h:43
float _24
Definition: Matrix3D.h:45
float _34
Definition: Matrix3D.h:45
CQuaternion & operator+=(const CQuaternion &quat)
Definition: Quaternion.cpp:44
CQuaternion operator-(const CQuaternion &quat) const
Definition: Quaternion.cpp:50
CVector3D m_V
Definition: Quaternion.h:27
float _23
Definition: Matrix3D.h:44
void Normalize()
Definition: Quaternion.cpp:288
float Z
Definition: Vector3D.h:31
void FromAxisAngle(const CVector3D &axis, float angle)
Definition: Quaternion.cpp:260
void Nlerp(const CQuaternion &from, const CQuaternion &to, float ratio)
Definition: Quaternion.cpp:248
void Slerp(const CQuaternion &from, const CQuaternion &to, float ratio)
Definition: Quaternion.cpp:198
float _41
Definition: Matrix3D.h:42
const float EPSILON
Definition: Quaternion.cpp:23
float _14
Definition: Matrix3D.h:45