Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/orxonox/trunk/src/lib/math/vector.cc @ 4477

Last change on this file since 4477 was 4477, checked in by bensch, 19 years ago

orxonox/trunk: vector is completely documented again :)

File size: 15.0 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Christian Meyer
13   co-programmer: Patrick Boenzli : Vector::scale()
14                                    Vector::abs()
15   
16   Quaternion code borrowed from an Gamasutra article by Nick Bobick and Ken Shoemake
17*/
18
19#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_MATH
20
21#include "vector.h"
22#include "debug.h"
23
24using namespace std;
25
26/////////////
27/* VECTORS */
28/////////////
29/**
30   \brief returns the this-vector normalized to length 1.0
31*/
32Vector Vector::getNormalized() const
33{
34  float l = len();
35  if(unlikely(l != 1.0))
36    {
37      return *this;
38    }
39  else if(unlikely(l == 0.0))
40    {
41      return *this;
42    }
43
44  return *this / l;
45}
46
47/**
48   \brief Vector is looking in the positive direction on all axes after this
49*/
50Vector Vector::abs() 
51{
52  Vector v(fabs(x), fabs(y), fabs(z));
53  return v;
54}
55
56
57
58/**
59   \brief Outputs the values of the Vector
60*/
61void Vector::debug(void) const
62{
63  PRINT(0)("Vector Debug information\n");
64  PRINT(0)("x: %f; y: %f; z: %f", x, y, z);
65  PRINT(3)(" lenght: %f", len());
66  PRINT(0)("\n");
67}
68
69/////////////////
70/* QUATERNIONS */
71/////////////////
72/**
73   \brief calculates a lookAt rotation
74   \param dir: the direction you want to look
75   \param up: specify what direction up should be
76   
77   Mathematically this determines the rotation a (0,0,1)-Vector has to undergo to point
78   the same way as dir. If you want to use this with cameras, you'll have to reverse the
79   dir Vector (Vector(0,0,0) - your viewing direction) or you'll point the wrong way. You
80   can use this for meshes as well (then you do not have to reverse the vector), but keep
81   in mind that if you do that, the model's front has to point in +z direction, and left
82   and right should be -x or +x respectively or the mesh wont rotate correctly.
83*/
84Quaternion::Quaternion (const Vector& dir, const Vector& up)
85{
86  Vector z = dir;
87  z.normalize(); 
88  Vector x = up.cross(z);
89  x.normalize(); 
90  Vector y = z.cross(x);
91 
92  float m[4][4];
93  m[0][0] = x.x;
94  m[0][1] = x.y;
95  m[0][2] = x.z;
96  m[0][3] = 0;
97  m[1][0] = y.x;
98  m[1][1] = y.y;
99  m[1][2] = y.z;
100  m[1][3] = 0;
101  m[2][0] = z.x;
102  m[2][1] = z.y;
103  m[2][2] = z.z;
104  m[2][3] = 0;
105  m[3][0] = 0;
106  m[3][1] = 0;
107  m[3][2] = 0;
108  m[3][3] = 1;
109 
110  *this = Quaternion (m);
111}
112
113/**
114   \brief calculates a rotation from euler angles
115   \param roll: the roll in radians
116   \param pitch: the pitch in radians
117   \param yaw: the yaw in radians
118*/
119Quaternion::Quaternion (float roll, float pitch, float yaw)
120{
121  float cr, cp, cy, sr, sp, sy, cpcy, spsy;
122 
123  // calculate trig identities
124  cr = cos(roll/2);
125  cp = cos(pitch/2);
126  cy = cos(yaw/2);
127 
128  sr = sin(roll/2);
129  sp = sin(pitch/2);
130  sy = sin(yaw/2);
131 
132  cpcy = cp * cy;
133  spsy = sp * sy;
134 
135  w = cr * cpcy + sr * spsy;
136  v.x = sr * cpcy - cr * spsy;
137  v.y = cr * sp * cy + sr * cp * sy;
138  v.z = cr * cp * sy - sr * sp * cy;
139}
140
141/**
142   \brief rotates one Quaternion by another
143   \param q: another Quaternion to rotate this by
144   \return a quaternion that represents the first one rotated by the second one (WARUNING: this operation is not commutative! e.g. (A*B) != (B*A))
145*/
146Quaternion Quaternion::operator*(const Quaternion& q) const
147{
148  float A, B, C, D, E, F, G, H;
149 
150  A = (w   + v.x)*(q.w   + q.v.x);
151  B = (v.z - v.y)*(q.v.y - q.v.z);
152  C = (w   - v.x)*(q.v.y + q.v.z); 
153  D = (v.y + v.z)*(q.w   - q.v.x);
154  E = (v.x + v.z)*(q.v.x + q.v.y);
155  F = (v.x - v.z)*(q.v.x - q.v.y);
156  G = (w   + v.y)*(q.w   - q.v.z);
157  H = (w   - v.y)*(q.w   + q.v.z);
158 
159  Quaternion r;
160  r.v.x = A - (E + F + G + H)/2;
161  r.v.y = C + (E - F + G - H)/2;
162  r.v.z = D + (E - F - G + H)/2;
163  r.w = B +  (-E - F + G + H)/2;
164
165  return r;
166}
167
168/**
169   \brief rotate a Vector by a Quaternion
170   \param v: the Vector
171   \return a new Vector representing v rotated by the Quaternion
172*/
173
174Vector Quaternion::apply (const Vector& v) const
175{
176  Quaternion q;
177  q.v = v;
178  q.w = 0;
179  q = *this * q * conjugate();
180  return q.v;
181}
182
183
184/**
185   \brief multiply a Quaternion with a real value
186   \param f: a real value
187   \return a new Quaternion containing the product
188*/
189Quaternion Quaternion::operator*(const float& f) const
190{
191  Quaternion r(*this);
192  r.w = r.w*f;
193  r.v = r.v*f;
194  return r;
195}
196
197/**
198   \brief divide a Quaternion by a real value
199   \param f: a real value
200   \return a new Quaternion containing the quotient
201*/
202Quaternion Quaternion::operator/(const float& f) const
203{
204  if( f == 0) return Quaternion();
205  Quaternion r(*this);
206  r.w = r.w/f;
207  r.v = r.v/f;
208  return r;
209}
210
211/**
212   \brief calculate the conjugate value of the Quaternion
213   \return the conjugate Quaternion
214*/
215/*
216Quaternion Quaternion::conjugate() const
217{
218  Quaternion r(*this);
219  r.v = Vector() - r.v;
220  return r;
221}
222*/
223
224/**
225   \brief calculate the norm of the Quaternion
226   \return the norm of The Quaternion
227*/
228float Quaternion::norm() const
229{
230  return w*w + v.x*v.x + v.y*v.y + v.z*v.z;
231}
232
233/**
234   \brief calculate the inverse value of the Quaternion
235   \return the inverse Quaternion
236   
237        Note that this is equal to conjugate() if the Quaternion's norm is 1
238*/
239Quaternion Quaternion::inverse() const
240{
241  float n = norm();
242  if (n != 0)
243    {
244      return conjugate() / norm();
245    }
246  else return Quaternion();
247}
248
249/**
250   \brief convert the Quaternion to a 4x4 rotational glMatrix
251   \param m: a buffer to store the Matrix in
252*/
253void Quaternion::matrix (float m[4][4]) const
254{
255  float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; 
256 
257  // calculate coefficients
258  x2 = v.x + v.x;
259  y2 = v.y + v.y; 
260  z2 = v.z + v.z;
261  xx = v.x * x2; xy = v.x * y2; xz = v.x * z2;
262  yy = v.y * y2; yz = v.y * z2; zz = v.z * z2;
263  wx = w * x2; wy = w * y2; wz = w * z2;
264 
265  m[0][0] = 1.0 - (yy + zz); m[1][0] = xy - wz;
266  m[2][0] = xz + wy; m[3][0] = 0.0;
267       
268  m[0][1] = xy + wz; m[1][1] = 1.0 - (xx + zz);
269  m[2][1] = yz - wx; m[3][1] = 0.0;
270 
271  m[0][2] = xz - wy; m[1][2] = yz + wx;
272  m[2][2] = 1.0 - (xx + yy); m[3][2] = 0.0;
273 
274  m[0][3] = 0; m[1][3] = 0;
275  m[2][3] = 0; m[3][3] = 1;
276}
277
278/**
279   \brief performs a smooth move.
280   \param from  where
281   \param to where
282   \param t the time this transformation should take value [0..1]
283
284   \returns the Result of the smooth move
285*/
286Quaternion quatSlerp(const Quaternion& from, const Quaternion& to, float t)
287{
288  float tol[4];
289  double omega, cosom, sinom, scale0, scale1;
290  //  float DELTA = 0.2;
291
292  cosom = from.v.x * to.v.x + from.v.y * to.v.y + from.v.z * to.v.z + from.w * to.w;
293
294  if( cosom < 0.0 ) 
295    { 
296      cosom = -cosom; 
297      tol[0] = -to.v.x;
298      tol[1] = -to.v.y;
299      tol[2] = -to.v.z;
300      tol[3] = -to.w;
301    }
302  else
303    {
304      tol[0] = to.v.x;
305      tol[1] = to.v.y;
306      tol[2] = to.v.z;
307      tol[3] = to.w;
308    }
309 
310  //if( (1.0 - cosom) > DELTA )
311  //{
312  omega = acos(cosom);
313  sinom = sin(omega);
314  scale0 = sin((1.0 - t) * omega) / sinom;
315  scale1 = sin(t * omega) / sinom;
316  //}
317  /*
318    else
319    {
320    scale0 = 1.0 - t;
321    scale1 = t;
322    }
323  */
324
325
326  /*
327    Quaternion res;
328    res.v.x = scale0 * from.v.x + scale1 * tol[0];
329    res.v.y = scale0 * from.v.y + scale1 * tol[1];
330    res.v.z = scale0 * from.v.z + scale1 * tol[2];
331    res.w = scale0 * from.w + scale1 * tol[3];
332  */
333  return Quaternion(Vector(scale0 * from.v.x + scale1 * tol[0],
334                           scale0 * from.v.y + scale1 * tol[1],
335                           scale0 * from.v.z + scale1 * tol[2]),
336                    scale0 * from.w + scale1 * tol[3]);
337}
338
339
340/**
341   \brief convert a rotational 4x4 glMatrix into a Quaternion
342   \param m: a 4x4 matrix in glMatrix order
343*/
344Quaternion::Quaternion (float m[4][4])
345{
346       
347  float  tr, s, q[4];
348  int    i, j, k;
349
350  int nxt[3] = {1, 2, 0};
351
352  tr = m[0][0] + m[1][1] + m[2][2];
353
354        // check the diagonal
355  if (tr > 0.0) 
356  {
357    s = sqrt (tr + 1.0);
358    w = s / 2.0;
359    s = 0.5 / s;
360    v.x = (m[1][2] - m[2][1]) * s;
361    v.y = (m[2][0] - m[0][2]) * s;
362    v.z = (m[0][1] - m[1][0]) * s;
363        } 
364        else 
365        {
366                // diagonal is negative
367        i = 0;
368        if (m[1][1] > m[0][0]) i = 1;
369    if (m[2][2] > m[i][i]) i = 2;
370    j = nxt[i];
371    k = nxt[j];
372
373    s = sqrt ((m[i][i] - (m[j][j] + m[k][k])) + 1.0);
374     
375    q[i] = s * 0.5;
376           
377    if (s != 0.0) s = 0.5 / s;
378       
379          q[3] = (m[j][k] - m[k][j]) * s;
380    q[j] = (m[i][j] + m[j][i]) * s;
381    q[k] = (m[i][k] + m[k][i]) * s;
382
383        v.x = q[0];
384        v.y = q[1];
385        v.z = q[2];
386        w = q[3];
387  }
388}
389
390/**
391   \brief outputs some nice formated debug information about this quaternion
392*/
393void Quaternion::debug(void)
394{
395  PRINT(0)("Quaternion Debug Information\n");
396  PRINT(0)("real a=%f; imag: x=%f y=%f z=%f\n", w, v.x, v.y, v.z);
397}
398
399/**
400   \brief create a rotation from a vector
401   \param v: a vector
402*/
403Rotation::Rotation (const Vector& v)
404{
405  Vector x = Vector( 1, 0, 0);
406  Vector axis = x.cross( v);
407  axis.normalize();
408  float angle = angleRad( x, v);
409  float ca = cos(angle);
410  float sa = sin(angle);
411  m[0] = 1.0f+(1.0f-ca)*(axis.x*axis.x-1.0f);
412  m[1] = -axis.z*sa+(1.0f-ca)*axis.x*axis.y;
413  m[2] = axis.y*sa+(1.0f-ca)*axis.x*axis.z;
414  m[3] = axis.z*sa+(1.0f-ca)*axis.x*axis.y;
415  m[4] = 1.0f+(1.0f-ca)*(axis.y*axis.y-1.0f);
416  m[5] = -axis.x*sa+(1.0f-ca)*axis.y*axis.z;
417  m[6] = -axis.y*sa+(1.0f-ca)*axis.x*axis.z;
418  m[7] = axis.x*sa+(1.0f-ca)*axis.y*axis.z;
419  m[8] = 1.0f+(1.0f-ca)*(axis.z*axis.z-1.0f);
420}
421
422/**
423   \brief creates a rotation from an axis and an angle (radians!)
424   \param axis: the rotational axis
425   \param angle: the angle in radians
426*/
427Rotation::Rotation (const Vector& axis, float angle)
428{
429  float ca, sa;
430  ca = cos(angle);
431  sa = sin(angle);
432  m[0] = 1.0f+(1.0f-ca)*(axis.x*axis.x-1.0f);
433  m[1] = -axis.z*sa+(1.0f-ca)*axis.x*axis.y;
434  m[2] = axis.y*sa+(1.0f-ca)*axis.x*axis.z;
435  m[3] = axis.z*sa+(1.0f-ca)*axis.x*axis.y;
436  m[4] = 1.0f+(1.0f-ca)*(axis.y*axis.y-1.0f);
437  m[5] = -axis.x*sa+(1.0f-ca)*axis.y*axis.z;
438  m[6] = -axis.y*sa+(1.0f-ca)*axis.x*axis.z;
439  m[7] = axis.x*sa+(1.0f-ca)*axis.y*axis.z;
440  m[8] = 1.0f+(1.0f-ca)*(axis.z*axis.z-1.0f);
441}
442
443/**
444   \brief creates a rotation from euler angles (pitch/yaw/roll)
445   \param pitch: rotation around z (in radians)
446   \param yaw: rotation around y (in radians)
447   \param roll: rotation around x (in radians)
448*/
449Rotation::Rotation ( float pitch, float yaw, float roll)
450{
451  float cy, sy, cr, sr, cp, sp;
452  cy = cos(yaw);
453  sy = sin(yaw);
454  cr = cos(roll);
455  sr = sin(roll);
456  cp = cos(pitch);
457  sp = sin(pitch);
458  m[0] = cy*cr;
459  m[1] = -cy*sr;
460  m[2] = sy;
461  m[3] = cp*sr+sp*sy*cr;
462  m[4] = cp*cr-sp*sr*sy;
463  m[5] = -sp*cy;
464  m[6] = sp*sr-cp*sy*cr;
465  m[7] = sp*cr+cp*sy*sr;
466  m[8] = cp*cy;
467}
468
469/**
470   \brief creates a nullrotation (an identity rotation)
471*/
472Rotation::Rotation ()
473{
474  m[0] = 1.0f;
475  m[1] = 0.0f;
476  m[2] = 0.0f;
477  m[3] = 0.0f;
478  m[4] = 1.0f;
479  m[5] = 0.0f;
480  m[6] = 0.0f;
481  m[7] = 0.0f;
482  m[8] = 1.0f;
483}
484
485/**
486   \brief fills the specified buffer with a 4x4 glmatrix
487   \param buffer: Pointer to an array of 16 floats
488   
489   Use this to get the rotation in a gl-compatible format
490*/
491void Rotation::glmatrix (float* buffer)
492{
493        buffer[0] = m[0];
494        buffer[1] = m[3];
495        buffer[2] = m[6];
496        buffer[3] = m[0];
497        buffer[4] = m[1];
498        buffer[5] = m[4];
499        buffer[6] = m[7];
500        buffer[7] = m[0];
501        buffer[8] = m[2];
502        buffer[9] = m[5];
503        buffer[10] = m[8];
504        buffer[11] = m[0];
505        buffer[12] = m[0];
506        buffer[13] = m[0];
507        buffer[14] = m[0];
508        buffer[15] = m[1];
509}
510
511/**
512   \brief multiplies two rotational matrices
513   \param r: another Rotation
514   \return the matrix product of the Rotations
515   
516   Use this to rotate one rotation by another
517*/
518Rotation Rotation::operator* (const Rotation& r)
519{
520        Rotation p;
521       
522        p.m[0] = m[0]*r.m[0] + m[1]*r.m[3] + m[2]*r.m[6];
523        p.m[1] = m[0]*r.m[1] + m[1]*r.m[4] + m[2]*r.m[7];
524        p.m[2] = m[0]*r.m[2] + m[1]*r.m[5] + m[2]*r.m[8];
525       
526        p.m[3] = m[3]*r.m[0] + m[4]*r.m[3] + m[5]*r.m[6];
527        p.m[4] = m[3]*r.m[1] + m[4]*r.m[4] + m[5]*r.m[7];
528        p.m[5] = m[3]*r.m[2] + m[4]*r.m[5] + m[5]*r.m[8];
529
530        p.m[6] = m[6]*r.m[0] + m[7]*r.m[3] + m[8]*r.m[6];
531        p.m[7] = m[6]*r.m[1] + m[7]*r.m[4] + m[8]*r.m[7];
532        p.m[8] = m[6]*r.m[2] + m[7]*r.m[5] + m[8]*r.m[8];
533       
534        return p;
535}
536
537
538/**
539   \brief rotates the vector by the given rotation
540   \param v: a vector
541   \param r: a rotation
542   \return the rotated vector
543*/
544Vector rotateVector( const Vector& v, const Rotation& r)
545{
546  Vector t;
547 
548  t.x = v.x * r.m[0] + v.y * r.m[1] + v.z * r.m[2];
549  t.y = v.x * r.m[3] + v.y * r.m[4] + v.z * r.m[5];
550  t.z = v.x * r.m[6] + v.y * r.m[7] + v.z * r.m[8];
551
552  return t;
553}
554
555/**
556   \brief calculate the distance between two lines
557   \param l: the other line
558   \return the distance between the lines
559*/
560float Line::distance (const Line& l) const
561{
562  float q, d;
563  Vector n = a.cross(l.a);
564  q = n.dot(r-l.r);
565  d = n.len();
566  if( d == 0.0) return 0.0;
567  return q/d;
568}
569
570/**
571   \brief calculate the distance between a line and a point
572   \param v: the point
573   \return the distance between the Line and the point
574*/
575float Line::distancePoint (const Vector& v) const
576{
577  Vector d = v-r;
578  Vector u = a * d.dot( a);
579  return (d - u).len();
580}
581
582/**
583   \brief calculate the two points of minimal distance of two lines
584   \param l: the other line
585   \return a Vector[2] (!has to be deleted after use!) containing the two points of minimal distance
586*/
587Vector* Line::footpoints (const Line& l) const
588{
589  Vector* fp = new Vector[2];
590  Plane p = Plane (r + a.cross(l.a), r, r + a);
591  fp[1] = p.intersectLine (l);
592  p = Plane (fp[1], l.a);
593  fp[0] = p.intersectLine (*this);
594  return fp;
595}
596
597/**
598  \brief calculate the length of a line
599  \return the lenght of the line
600*/
601float Line::len() const
602{
603  return a.len();
604}
605
606/**
607   \brief rotate the line by given rotation
608   \param rot: a rotation
609*/
610void Line::rotate (const Rotation& rot)
611{
612  Vector t = a + r;
613  t = rotateVector( t, rot);
614  r = rotateVector( r, rot),
615  a = t - r;
616}
617
618/**
619   \brief create a plane from three points
620   \param a: first point
621   \param b: second point
622   \param c: third point
623*/
624Plane::Plane (Vector a, Vector b, Vector c)
625{
626  n = (a-b).cross(c-b);
627  k = -(n.x*b.x+n.y*b.y+n.z*b.z);
628}
629
630/**
631   \brief create a plane from anchor point and normal
632   \param norm: normal vector
633   \param p: anchor point
634*/
635Plane::Plane (Vector norm, Vector p)
636{
637  n = norm;
638  k = -(n.x*p.x+n.y*p.y+n.z*p.z);
639}
640
641/**
642   \brief returns the intersection point between the plane and a line
643   \param l: a line
644*/
645Vector Plane::intersectLine (const Line& l) const
646{
647  if (n.x*l.a.x+n.y*l.a.y+n.z*l.a.z == 0.0) return Vector(0,0,0);
648  float t = (n.x*l.r.x+n.y*l.r.y+n.z*l.r.z+k) / (n.x*l.a.x+n.y*l.a.y+n.z*l.a.z);
649  return l.r + (l.a * t);
650}
651
652/**
653   \brief returns the distance between the plane and a point
654   \param p: a Point
655   \return the distance between the plane and the point (can be negative)
656*/
657float Plane::distancePoint (const Vector& p) const
658{
659  float l = n.len();
660  if( l == 0.0) return 0.0;
661  return (n.dot(p) + k) / n.len();
662}
663
664/**
665   \brief returns the side a point is located relative to a Plane
666   \param p: a Point
667   \return 0 if the point is contained within the Plane, positive(negative) if the point is in the positive(negative) semi-space of the Plane
668*/
669float Plane::locatePoint (const Vector& p) const
670{
671  return (n.dot(p) + k);
672}
673
Note: See TracBrowser for help on using the repository browser.