1 /* Matrix and vector arithmetic.
2 * By Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: matvec.c,v 1.5 2002/05/04 11:54:23 rich Exp $
33 float identity_matrix[16] = {
40 float zero_vec[4] = { 0, 0, 0, 1 };
43 new_identity_matrix (pool pool)
45 float *m = new_matrix (pool);
46 make_identity_matrix (m);
51 new_zero_vec (pool pool)
53 float *v = new_vec (pool);
58 /* This code is taken from Mesa 3.0. I have exchanged degrees for radians. */
60 make_rotation_matrix (float angle,
61 float x, float y, float z,
64 /* This function contributed by Erich Boleyn (erich@uruk.org) */
66 float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
71 mag = sqrt ( x*x + y*y + z*z );
74 /* generate an identity matrix and return */
75 make_identity_matrix (m);
83 #define M(row,col) m[col*4+row]
86 * Arbitrary axis rotation matrix.
88 * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied
89 * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation
90 * (which is about the X-axis), and the two composite transforms
91 * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary
92 * from the arbitrary axis to the X-axis then back. They are
93 * all elementary rotations.
95 * Rz' is a rotation about the Z-axis, to bring the axis vector
96 * into the x-z plane. Then Ry' is applied, rotating about the
97 * Y-axis to bring the axis vector parallel with the X-axis. The
98 * rotation about the X-axis is then performed. Ry and Rz are
99 * simply the respective inverse transforms to bring the arbitrary
100 * axis back to it's original orientation. The first transforms
101 * Rz' and Ry' are considered inverses, since the data from the
102 * arbitrary axis gives you info on how to get to it, not how
103 * to get away from it, and an inverse must be applied.
105 * The basic calculation used is to recognize that the arbitrary
106 * axis vector (x, y, z), since it is of unit length, actually
107 * represents the sines and cosines of the angles to rotate the
108 * X-axis to the same orientation, with theta being the angle about
109 * Z and phi the angle about Y (in the order described above)
112 * cos ( theta ) = x / sqrt ( 1 - z^2 )
113 * sin ( theta ) = y / sqrt ( 1 - z^2 )
115 * cos ( phi ) = sqrt ( 1 - z^2 )
118 * Note that cos ( phi ) can further be inserted to the above
121 * cos ( theta ) = x / cos ( phi )
122 * sin ( theta ) = y / sin ( phi )
124 * ...etc. Because of those relations and the standard trigonometric
125 * relations, it is pssible to reduce the transforms down to what
126 * is used below. It may be that any primary axis chosen will give the
127 * same results (modulo a sign convention) using thie method.
129 * Particularly nice is to notice that all divisions that might
130 * have caused trouble when parallel to certain planes or
131 * axis go away with care paid to reducing the expressions.
132 * After checking, it does perform correctly under all cases, since
133 * in all the cases of division where the denominator would have
134 * been zero, the numerator would have been zero as well, giving
135 * the expected result.
149 M(0,0) = (one_c * xx) + c;
150 M(0,1) = (one_c * xy) - zs;
151 M(0,2) = (one_c * zx) + ys;
154 M(1,0) = (one_c * xy) + zs;
155 M(1,1) = (one_c * yy) + c;
156 M(1,2) = (one_c * yz) - xs;
159 M(2,0) = (one_c * zx) - ys;
160 M(2,1) = (one_c * yz) + xs;
161 M(2,2) = (one_c * zz) + c;
173 make_translation_matrix (float x, float y, float z, float *m)
175 make_identity_matrix (m);
182 make_scaling_matrix (float x, float y, float z, float *m)
184 make_identity_matrix (m);
190 /* The next two functions are from the matrix FAQ by <hexapod@netcom.com>, see:
191 * http://www.flipcode.com/documents/matrfaq.html
194 /* Quickly convert 3 Euler angles to a rotation matrix. */
196 matrix_euler_to_rotation (float angle_x, float angle_y, float angle_z,
199 float A = cos(angle_x);
200 float B = sin(angle_x);
201 float C = cos(angle_y);
202 float D = sin(angle_y);
203 float E = cos(angle_z);
204 float F = sin(angle_z);
212 mat[1] = -BD * E + A * F;
213 mat[5] = BD * F + A * E;
215 mat[2] = AD * E + B * F;
216 mat[6] = -AD * F + B * E;
219 mat[12] = mat[13] = mat[14] = mat[3] = mat[7] = mat[11] = 0;
224 clamp (float v, float low, float high)
226 /* This is not very efficient ... */
228 while (v > high - low) v -= high - low;
229 while (v < 0) v += high - low;
234 /* Convert a rotation matrix to 3 Euler angles. */
236 matrix_rotation_to_euler (const float *mat,
237 float *angle_x, float *angle_y, float *angle_z)
241 *angle_y = -asin( mat[8]); /* Calculate Y-axis angle */
244 if ( fabs( C ) > 0.005 ) /* Gimball lock? */
246 trx = mat[10] / C; /* No, so get X-axis angle */
249 *angle_x = atan2( try, trx );
251 trx = mat[0] / C; /* Get Z-axis angle */
254 *angle_z = atan2( try, trx );
256 else /* Gimball lock has occurred */
258 trx = mat[5]; /* And calculate Z-axis angle */
261 *angle_z = atan2( try, trx );
264 /* Clamp all angles to range */
265 *angle_x = clamp (*angle_x, 0, 2 * M_PI);
266 *angle_y = clamp (*angle_y, 0, 2 * M_PI);
267 *angle_z = clamp (*angle_z, 0, 2 * M_PI);
270 /* This code is taken from Mesa 3.0.
273 matrix_multiply (const float *a, const float *b,
276 /* This matmul was contributed by Thomas Malik */
279 #define A(row,col) a[(col<<2)+row]
280 #define B(row,col) b[(col<<2)+row]
281 #define P(row,col) product[(col<<2)+row]
284 for (i = 0; i < 4; i++) {
285 float ai0=A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3);
286 P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
287 P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
288 P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
289 P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
297 /* Multiply matrix by vector. */
299 matrix_vec_multiply (const float *m, const float *v,
302 result[0] = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]*v[3];
303 result[1] = m[4]*v[0] + m[5]*v[1] + m[6]*v[2] + m[7]*v[3];
304 result[2] = m[8]*v[0] + m[9]*v[1] + m[10]*v[2] + m[11]*v[3];
305 result[3] = m[12]*v[0] + m[13]*v[1] + m[14]*v[2] + m[15]*v[3];
308 /* Compute the magnitude of a vector. */
310 vec_magnitude (const float *v)
312 return sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
315 /* Compute the magnitude of a 2D vector. */
317 vec_magnitude2d (const float *v)
319 return sqrt (v[0]*v[0] + v[1]*v[1]);
322 /* Normalize a vector. */
324 vec_normalize (const float *v, float *r)
326 float w = vec_magnitude (v);
332 /* Normalize a 2D vector. */
334 vec_normalize2d (const float *v, float *r)
336 float w = vec_magnitude2d (v);
342 vec_unit_normal_to_side (float *side, float *normal)
344 float n[3] = { side[0], side[1], side[2] };
345 vec_normalize (n, normal);
348 /* Compute the dot product of two vectors. */
350 vec_dot_product (const float *v1, const float *v2)
352 return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
355 /* Compute the magnitude of vector v1 in direction vector v2.
356 * If v1 == v2, this returns 1. If v1 = -v2, this returns -1.
357 * If v1 is perpendicular to v2 this returns 0.
360 vec_magnitude_in_direction (const float *v1, const float *v2)
362 static float v1n[3], v2n[3];
364 /* Normalize both vectors. */
365 vec_normalize (v1, v1n);
366 vec_normalize (v2, v2n);
368 /* Return their dot product. */
369 return vec_dot_product (v1n, v2n);
372 /* Compute angle between two vectors. */
374 vec_angle_between (const float *v1, const float *v2)
376 return acos (vec_magnitude_in_direction (v1, v2));
379 /* Scale a vector. */
381 vec_scale (const float *a, float n, float *r)
388 /* Add two vectors. */
390 vec_add (const float *a, const float *b, float *r)
397 /* Subtract two vectors. */
399 vec_subtract (const float *a, const float *b, float *r)
406 /* Calculate midpoint. */
408 point_midpoint (const float *p1, const float *p2, float *mp)
410 mp[0] = (p1[0] + p2[0]) / 2;
411 mp[1] = (p1[1] + p2[1]) / 2;
412 mp[2] = (p1[2] + p2[2]) / 2;
415 /* Calculate midpoint (in 2D). */
417 point_midpoint2d (const float *p1, const float *p2, float *mp)
419 mp[0] = (p1[0] + p2[0]) / 2;
420 mp[1] = (p1[1] + p2[1]) / 2;
423 /* Distance between two points. */
425 point_distance_to_point (const float *p1, const float *p2)
429 vec_subtract (p1, p2, v);
430 return vec_magnitude (v);
433 /* Cross product of vectors v and w.
434 * The cross product is a vector:
436 * v x w = |v| |w| sin t n^
438 * where t is the angle between a and
439 * b, and n^ is a normal vector perpendicular
440 * to a and b such that a,b,n^ form a
444 vec_cross_product (const float *v, const float *w, float *r)
446 r[0] = v[1]*w[2] - v[2]*w[1];
447 r[1] = v[2]*w[0] - v[0]*w[2];
448 r[2] = v[0]*w[1] - v[1]*w[0];
451 /* Distance between two points. */
453 point_distance (const float *p, const float *q)
455 double x = p[0] - q[0];
456 double y = p[1] - q[1];
457 double z = p[2] - q[2];
458 return sqrt (x*x + y*y + z*z);
461 /* Distance from a point to a plane. */
463 point_distance_to_plane (const float *plane, const float *point)
472 float t = (a*x + b*y + c*z + d) / - (a*a + b*b + c*c);
474 float dist = sqrt (t2*a*a + t2*b*b + t2*c*c);
475 /* Don't lose the sign of t. */
476 if (t < 0) return dist; else return -dist;
479 /* Return true if point_distance_to_plane > 0. */
481 point_is_inside_plane (const float *plane, const float *point)
490 float t = (a*x + b*y + c*z + d) / - (a*a + b*b + c*c);
496 float dist = sqrt (t2*a*a + t2*b*b + t2*c*c);
497 /* Don't lose the sign of t. */
498 if (t < 0) return dist; else return -dist;
502 /* See: http://www.greuer.de/efaq.html */
504 point_footprint_on_line (const float *point,
505 const float *line_point, const float *line_vector,
511 vec_subtract (point, line_point, s);
512 t = vec_dot_product (s, line_vector) /
513 vec_dot_product (line_vector, line_vector);
515 vec_scale (line_vector, t, s);
516 vec_add (line_point, s, footprint);
520 point_distance_to_line (const float *point,
521 const float *line_point, const float *line_vector)
526 point_footprint_on_line (point, line_point, line_vector, footprint);
527 return point_distance_to_point (point, footprint);
529 float u[3], p[3], prod[3], dist;
531 /* Normalize vector u along the line. */
532 vec_normalize (line_vector, u);
534 /* The distance is given by | (p-a) x u | where p is the
535 * point, a is a point on the line and x is the cross product.
537 vec_subtract (point, line_point, p);
538 vec_cross_product (p, u, prod);
539 dist = vec_magnitude (prod);
546 point_distance_to_line_segment (const float *point,
547 const float *line_point0,
548 const float *line_point1)
553 vec_subtract (line_point1, line_point0, v);
554 vec_subtract (point, line_point0, s);
555 t = vec_dot_product (s, v) /
556 vec_dot_product (v, v);
558 if (t >= 0 && t <= 1)
563 vec_add (line_point0, s, footprint);
564 return point_distance_to_point (point, footprint);
567 return point_distance_to_point (point, line_point0);
569 return point_distance_to_point (point, line_point1);
573 point_lies_in_face (const float *points, int nr_points, const float *point)
575 float sum = point_face_angle_sum (points, nr_points, point);
576 return fabs (sum - 2*M_PI) < 1e-5;
579 /* See: http://astronomy.swin.edu.au/pbourke/geometry/insidepoly/ */
581 point_face_angle_sum (const float *points, int nr_points, const float *point)
586 for (i = 0, next = 1; i < nr_points; ++i, ++next)
588 float p1[3], p2[3], m1, m2, costheta;
590 if (next == nr_points) next = 0;
592 p1[0] = points[i*3] - point[0];
593 p1[1] = points[i*3+1] - point[1];
594 p1[2] = points[i*3+2] - point[2];
595 p2[0] = points[next*3] - point[0];
596 p2[1] = points[next*3+1] - point[1];
597 p2[2] = points[next*3+2] - point[2];
599 m1 = vec_magnitude (p1);
600 m2 = vec_magnitude (p2);
605 costheta = (p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]) / (m1*m2);
607 sum += acos (costheta);
614 point_distance_to_face (const float *points, int nr_points,
615 const float *plane, const float *point, int *edge)
617 float my_plane_coeffs[4];
618 float a, b, c, d, tq, q[3], dist;
621 /* Calculate plane coefficients, if necessary. */
624 plane = my_plane_coeffs;
625 plane_coefficients (&points[0], &points[3], &points[6], (float *) plane);
633 /* q is the coordinate of the point of intersection of a
634 * normal line from the point to the plane. It may or may
635 * not be on the bounded face (we'll work that out in the
636 * moment). tq is the parameter of point q.
638 tq = - (a*point[0] + b*point[1] + c*point[2] + d) / (a*a + b*b + c*c);
639 q[0] = point[0] + tq*a;
640 q[1] = point[1] + tq*b;
641 q[2] = point[2] + tq*c;
643 /* Is q on the bounded face? */
644 if (point_lies_in_face (points, nr_points, q))
646 /* Compute the distance from the point to the plane. */
649 dist = sqrt (t2*a*a + t2*b*b + t2*c*c);
651 if (edge) *edge = -1;
653 return tq < 0 ? dist : -dist;
656 /* Find the closest edge. */
660 for (i = 0, next = 1; i < nr_points; ++i, ++next)
664 if (next == nr_points) next = 0;
666 d = point_distance_to_line_segment (point,
667 &points[i*3], &points[next*3]);
669 if (e == -1 || d < dist)
678 return tq < 0 ? dist : -dist;
681 /* Compute the four coefficients of a plane which
682 * uniquely specify that plane, given three points
683 * (not colinear) on the plane. Most of the variables
684 * in this function are redundant (optimized out?),
685 * but they get it into the same form as in
689 plane_coefficients (const float *p, const float *q, const float *r,
711 co[0] = (y1-y2) * za + (y2-y3) * zb + (y3-y1) * zc;
712 co[1] = (z1-z2) * xa + (z2-z3) * xb + (z3-z1) * xc;
713 co[2] = (x1-x2) * ya + (x2-x3) * yb + (x3-x1) * yc;
714 co[3] = - (co[0]*x1 + co[1]*y1 + co[2]*z1);
718 plane_translate_along_normal (const float *plane, float distance,
721 float w = vec_magnitude (plane);
723 new_plane[0] = plane[0];
724 new_plane[1] = plane[1];
725 new_plane[2] = plane[2];
726 new_plane[3] = plane[3] - w * distance;
730 face_translate_along_normal (const float *points, int nr_points,
731 const float *plane, float distance,
732 float *new_points, float *new_plane)
734 float w = vec_magnitude (plane), nv[3];
737 new_plane[0] = plane[0];
738 new_plane[1] = plane[1];
739 new_plane[2] = plane[2];
740 new_plane[3] = plane[3] - w * distance;
742 vec_scale (plane, distance / w, nv);
743 for (i = 0; i < nr_points; ++i)
745 new_points[i*3] = points[i*3] + nv[0];
746 new_points[i*3+1] = points[i*3+1] + nv[1];
747 new_points[i*3+2] = points[i*3+2] + nv[2];
751 /* All these quaternion functions are modified from the matrix FAQ again. */
753 quaternion_normalize (const float *a, float *r)
755 float w = vec_magnitude (a);
763 quaternion_conjugate (const float *a, float *r)
772 quaternion_magnitude (const float *a)
774 return sqrt (a[3]*a[3] + a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
778 quaternion_multiply (const float *a, const float *b, float *r)
780 float va[3], vb[3], vc[3];
782 r[3] = vec_dot_product (a, b);
784 vec_cross_product (a, b, va);
785 vec_scale (a, b[3], vb);
786 vec_scale (b, a[3], vc);
787 vec_add (va, vb, va);
790 quaternion_normalize (r, r);
794 quaternion_to_rotation_matrix (const float *q, float *mat)
813 mat[0] = 1 - 2 * ( yy + zz );
814 mat[4] = 2 * ( xy - zw );
815 mat[8] = 2 * ( xz + yw );
817 mat[1] = 2 * ( xy + zw );
818 mat[5] = 1 - 2 * ( xx + zz );
819 mat[9] = 2 * ( yz - xw );
821 mat[2] = 2 * ( xz - yw );
822 mat[6] = 2 * ( yz + xw );
823 mat[10] = 1 - 2 * ( xx + yy );
825 mat[3] = mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0;
830 make_quaternion_from_axis_angle (const float *axis, float angle,
833 double sin_a = sin (angle / 2);
834 double cos_a = cos (angle / 2);
836 q[0] = axis[0] * sin_a;
837 q[1] = axis[1] * sin_a;
838 q[2] = axis[2] * sin_a;
841 quaternion_normalize (q, q);
845 collision_moving_sphere_and_face (const float *p0, const float *p1,
847 const float *points, int nr_points,
849 float *collision_point)
851 float my_plane_coeffs[4], raised_plane[4], t, v[3], quot;
852 float raised_points[3 * nr_points];
854 /* Get the plane coefficients. */
857 plane = my_plane_coeffs;
858 plane_coefficients (&points[0], &points[3], &points[6], (float *) plane);
861 /* Raise the plane up by the distance of one radius. Then we can
862 * just test for the intersection of the ray and the face.This is
863 * something of an approximation, but hopefully will give us good
864 * results in practice. If not, then we may need to rework this
867 face_translate_along_normal (points, nr_points, plane, radius,
868 raised_points, raised_plane);
870 /* Get the intersection point of the ray and the plane, as a
873 vec_subtract (p1, p0, v);
874 quot = raised_plane[0]*v[0] + raised_plane[1]*v[1] + raised_plane[2]*v[2];
883 /* The path of the sphere is nearly parallel to the plane. Don't
884 * count this as a collision at all.
889 t = - (raised_plane[0]*p0[0] + raised_plane[1]*p0[1] + raised_plane[2]*p0[2]
890 + raised_plane[3]) / quot;
893 /* There is no collision. */
897 /* Calculate the actual point of collision. NOTE: This is the centre
898 * of the sphere, NOT the point where the sphere and plane touch.
901 vec_add (p0, v, collision_point);
903 /* Is the collision point actually within the bounded convex polygon
904 * which defines the face? If not, then no collision actually
905 * occurred. Note that we have to translate (ie. raise) the points
908 return point_lies_in_face (raised_points, nr_points, collision_point);