dropped all dependencies apart from SDL into the libs subdir
[summerhack] / libs / lib3ds / matrix.c
1 /*
2  * The 3D Studio File Format Library
3  * Copyright (C) 1996-2001 by J.E. Hoffmann <je-h@gmx.net>
4  * All rights reserved.
5  *
6  * This program is  free  software;  you can redistribute it and/or modify it
7  * under the terms of the  GNU Lesser General Public License  as published by 
8  * the  Free Software Foundation;  either version 2.1 of the License,  or (at 
9  * your option) any later version.
10  *
11  * This  program  is  distributed in  the  hope that it will  be useful,  but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or  FITNESS FOR A  PARTICULAR PURPOSE.  See the  GNU Lesser General Public  
14  * License for more details.
15  *
16  * You should  have received  a copy of the GNU Lesser General Public License
17  * along with  this program;  if not, write to the  Free Software Foundation,
18  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id: matrix.c,v 1.10 2004/11/16 07:41:44 efalk Exp $
21  */
22 #define LIB3DS_EXPORT
23 #include <lib3ds/matrix.h>
24 #include <lib3ds/quat.h>
25 #include <lib3ds/vector.h>
26 #include <string.h>
27 #include <math.h>
28
29
30 /*!
31  * \defgroup matrix Matrix Mathematics
32  *
33  * \author J.E. Hoffmann <je-h@gmx.net>
34  */
35 /*!
36  * \typedef Lib3dsMatrix
37  *   \ingroup matrix
38  */
39
40
41 /*!
42  * Clear a matrix to all zeros.
43  *
44  * \param m Matrix to be cleared.
45  *
46  * \ingroup matrix
47  */
48 void
49 lib3ds_matrix_zero(Lib3dsMatrix m)
50 {
51   int i,j;
52
53   for (i=0; i<4; i++) {
54     for (j=0; j<4; j++) m[i][j]=0.0f;
55   }
56 }
57
58
59 /*!
60  * Set a matrix to identity.
61  *
62  * \param m Matrix to be set.
63  *
64  * \ingroup matrix
65  */
66 void
67 lib3ds_matrix_identity(Lib3dsMatrix m)
68 {
69   int i,j;
70
71   for (i=0; i<4; i++) {
72     for (j=0; j<4; j++) m[i][j]=0.0;
73   }
74   for (i=0; i<4; i++) m[i][i]=1.0;
75 }
76
77
78 /*!
79  * Copy a matrix.
80  *
81  * \ingroup matrix
82  */
83 void
84 lib3ds_matrix_copy(Lib3dsMatrix dest, Lib3dsMatrix src)
85 {
86   memcpy(dest, src, sizeof(Lib3dsMatrix)); 
87 }
88
89
90 /*!
91  * Negate a matrix -- all elements negated.
92  *
93  * \ingroup matrix
94  */
95 void 
96 lib3ds_matrix_neg(Lib3dsMatrix m)
97 {
98   int i,j;
99
100   for (j=0; j<4; j++) {
101     for (i=0; i<4; i++) {
102       m[j][i]=-m[j][i];
103     }
104   }
105 }
106
107
108 /*!
109  * Set all matrix elements to their absolute value.
110  *
111  * \ingroup matrix
112  */
113 void 
114 lib3ds_matrix_abs(Lib3dsMatrix m)
115 {
116   int i,j;
117
118   for (j=0; j<4; j++) {
119     for (i=0; i<4; i++) {
120       m[j][i]=(Lib3dsFloat)fabs(m[j][i]);
121     }
122   }
123 }
124
125
126 /*!
127  * Transpose a matrix in place.
128  *
129  * \ingroup matrix
130  */
131 void
132 lib3ds_matrix_transpose(Lib3dsMatrix m)
133 {
134   int i,j;
135   Lib3dsFloat swp;
136
137   for (j=0; j<4; j++) {
138     for (i=j+1; i<4; i++) {
139       swp=m[j][i];
140       m[j][i]=m[i][j];
141       m[i][j]=swp;
142     }
143   }
144 }
145
146
147 /*!
148  * Add two matrices.
149  *
150  * \ingroup matrix
151  */
152 void
153 lib3ds_matrix_add(Lib3dsMatrix m, Lib3dsMatrix a, Lib3dsMatrix b)
154 {
155   int i,j;
156
157   for (j=0; j<4; j++) {
158     for (i=0; i<4; i++) {
159       m[j][i]=a[j][i]+b[j][i];
160     }
161   }
162 }
163
164
165 /*!
166  * Subtract two matrices.
167  *
168  * \param m Result.
169  * \param a Addend.
170  * \param b Minuend.
171  *
172  * \ingroup matrix
173  */
174 void
175 lib3ds_matrix_sub(Lib3dsMatrix m, Lib3dsMatrix a, Lib3dsMatrix b)
176 {
177   int i,j;
178
179   for (j=0; j<4; j++) {
180     for (i=0; i<4; i++) {
181       m[j][i]=a[j][i]-b[j][i];
182     }
183   }
184 }
185
186
187 /*!
188  * Multiply two matrices.
189  *
190  * \param m Result.
191  * \param a Left matrix.
192  * \param b Right matrix.
193  *
194  * \ingroup matrix
195  */
196 void
197 lib3ds_matrix_mul(Lib3dsMatrix m, Lib3dsMatrix a, Lib3dsMatrix b)
198 {
199   int i,j,k;
200   Lib3dsFloat ab;
201
202   for (j=0; j<4; j++) {
203     for (i=0; i<4; i++) {
204       ab=0.0f;
205       for (k=0; k<4; k++) ab+=a[k][i]*b[j][k];
206       m[j][i]=ab;
207     }
208   }
209 }
210
211
212 /*!
213  * Multiply a matrix by a scalar.
214  *
215  * \param m Matrix to be set.
216  * \param k Scalar.
217  *
218  * \ingroup matrix
219  */
220 void
221 lib3ds_matrix_scalar(Lib3dsMatrix m, Lib3dsFloat k)
222 {
223   int i,j;
224
225   for (j=0; j<4; j++) {
226     for (i=0; i<4; i++) {
227       m[j][i]*=k;
228     }
229   }
230 }
231
232
233 static Lib3dsFloat
234 det2x2(
235   Lib3dsFloat a, Lib3dsFloat b,
236   Lib3dsFloat c, Lib3dsFloat d) 
237 {
238   return((a)*(d)-(b)*(c));
239 }
240
241
242 static Lib3dsFloat
243 det3x3(
244   Lib3dsFloat a1, Lib3dsFloat a2, Lib3dsFloat a3,
245   Lib3dsFloat b1, Lib3dsFloat b2, Lib3dsFloat b3,
246   Lib3dsFloat c1, Lib3dsFloat c2, Lib3dsFloat c3)
247 {
248   return(
249     a1*det2x2(b2,b3,c2,c3)-
250     b1*det2x2(a2,a3,c2,c3)+
251     c1*det2x2(a2,a3,b2,b3)
252   );
253 }
254
255
256 /*!
257  * Find determinant of a matrix.
258  *
259  * \ingroup matrix
260  */
261 Lib3dsFloat
262 lib3ds_matrix_det(Lib3dsMatrix m)
263 {
264   Lib3dsFloat a1,a2,a3,a4,b1,b2,b3,b4,c1,c2,c3,c4,d1,d2,d3,d4;
265
266   a1 = m[0][0];
267   b1 = m[1][0];
268   c1 = m[2][0];
269   d1 = m[3][0];
270   a2 = m[0][1];
271   b2 = m[1][1];
272   c2 = m[2][1];
273   d2 = m[3][1];
274   a3 = m[0][2];
275   b3 = m[1][2];
276   c3 = m[2][2];
277   d3 = m[3][2];
278   a4 = m[0][3];
279   b4 = m[1][3];
280   c4 = m[2][3];
281   d4 = m[3][3];
282   return(
283     a1 * det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)-
284     b1 * det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)+
285     c1 * det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)-
286     d1 * det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4)
287   );
288 }
289
290
291 /*!
292  * Find the adjoint of a matrix.
293  *
294  * \ingroup matrix
295  */
296 void
297 lib3ds_matrix_adjoint(Lib3dsMatrix m)
298 {
299   Lib3dsFloat a1,a2,a3,a4,b1,b2,b3,b4,c1,c2,c3,c4,d1,d2,d3,d4;
300
301   a1 = m[0][0];
302   b1 = m[1][0];
303   c1 = m[2][0];
304   d1 = m[3][0];
305   a2 = m[0][1];
306   b2 = m[1][1];
307   c2 = m[2][1];
308   d2 = m[3][1];
309   a3 = m[0][2];
310   b3 = m[1][2];
311   c3 = m[2][2];
312   d3 = m[3][2];
313   a4 = m[0][3];
314   b4 = m[1][3];
315   c4 = m[2][3];
316   d4 = m[3][3];
317   m[0][0]=  det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4);
318   m[0][1]= -det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4);
319   m[0][2]=  det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4);
320   m[0][3]= -det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4);
321   m[1][0]= -det3x3 (b1, b3, b4, c1, c3, c4, d1, d3, d4);
322   m[1][1]=  det3x3 (a1, a3, a4, c1, c3, c4, d1, d3, d4);
323   m[1][2]= -det3x3 (a1, a3, a4, b1, b3, b4, d1, d3, d4);
324   m[1][3]=  det3x3 (a1, a3, a4, b1, b3, b4, c1, c3, c4);
325   m[2][0]=  det3x3 (b1, b2, b4, c1, c2, c4, d1, d2, d4);
326   m[2][1]= -det3x3 (a1, a2, a4, c1, c2, c4, d1, d2, d4);
327   m[2][2]=  det3x3 (a1, a2, a4, b1, b2, b4, d1, d2, d4);
328   m[2][3]= -det3x3 (a1, a2, a4, b1, b2, b4, c1, c2, c4);
329   m[3][0]= -det3x3 (b1, b2, b3, c1, c2, c3, d1, d2, d3);
330   m[3][1]=  det3x3 (a1, a2, a3, c1, c2, c3, d1, d2, d3);
331   m[3][2]= -det3x3 (a1, a2, a3, b1, b2, b3, d1, d2, d3);
332   m[3][3]=  det3x3 (a1, a2, a3, b1, b2, b3, c1, c2, c3);
333 }
334
335
336 /*!
337  * Invert a matrix in place.
338  *
339  * \param m Matrix to invert.
340  *
341  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
342  * \ingroup matrix
343  *
344  * GGemsII, K.Wu, Fast Matrix Inversion 
345  */
346 Lib3dsBool
347 lib3ds_matrix_inv(Lib3dsMatrix m)
348 {                          
349   int i,j,k;               
350   int pvt_i[4], pvt_j[4];            /* Locations of pivot elements */
351   Lib3dsFloat pvt_val;               /* Value of current pivot element */
352   Lib3dsFloat hold;                  /* Temporary storage */
353   Lib3dsFloat determinat;            
354
355   determinat = 1.0f;
356   for (k=0; k<4; k++)  {
357     /* Locate k'th pivot element */
358     pvt_val=m[k][k];            /* Initialize for search */
359     pvt_i[k]=k;
360     pvt_j[k]=k;
361     for (i=k; i<4; i++) {
362       for (j=k; j<4; j++) {
363         if (fabs(m[i][j]) > fabs(pvt_val)) {
364           pvt_i[k]=i;
365           pvt_j[k]=j;
366           pvt_val=m[i][j];
367         }
368       }
369     }
370
371     /* Product of pivots, gives determinant when finished */
372     determinat*=pvt_val;
373     if (fabs(determinat)<LIB3DS_EPSILON) {    
374       return(LIB3DS_FALSE);  /* Matrix is singular (zero determinant) */
375     }
376
377     /* "Interchange" rows (with sign change stuff) */
378     i=pvt_i[k];
379     if (i!=k) {               /* If rows are different */
380       for (j=0; j<4; j++) {
381         hold=-m[k][j];
382         m[k][j]=m[i][j];
383         m[i][j]=hold;
384       }
385     }
386
387     /* "Interchange" columns */
388     j=pvt_j[k];
389     if (j!=k) {              /* If columns are different */
390       for (i=0; i<4; i++) {
391         hold=-m[i][k];
392         m[i][k]=m[i][j];
393         m[i][j]=hold;
394       }
395     }
396     
397     /* Divide column by minus pivot value */
398     for (i=0; i<4; i++) {
399       if (i!=k) m[i][k]/=( -pvt_val) ; 
400     }
401
402     /* Reduce the matrix */
403     for (i=0; i<4; i++) {
404       hold = m[i][k];
405       for (j=0; j<4; j++) {
406         if (i!=k && j!=k) m[i][j]+=hold*m[k][j];
407       }
408     }
409
410     /* Divide row by pivot */
411     for (j=0; j<4; j++) {
412       if (j!=k) m[k][j]/=pvt_val;
413     }
414
415     /* Replace pivot by reciprocal (at last we can touch it). */
416     m[k][k] = 1.0f/pvt_val;
417   }
418
419   /* That was most of the work, one final pass of row/column interchange */
420   /* to finish */
421   for (k=4-2; k>=0; k--) { /* Don't need to work with 1 by 1 corner*/
422     i=pvt_j[k];            /* Rows to swap correspond to pivot COLUMN */
423     if (i!=k) {            /* If rows are different */
424       for(j=0; j<4; j++) {
425         hold = m[k][j];
426         m[k][j]=-m[i][j];
427         m[i][j]=hold;
428       }
429     }
430
431     j=pvt_i[k];           /* Columns to swap correspond to pivot ROW */
432     if (j!=k)             /* If columns are different */
433     for (i=0; i<4; i++) {
434       hold=m[i][k];
435       m[i][k]=-m[i][j];
436       m[i][j]=hold;
437     }
438   }
439   return(LIB3DS_TRUE);                          
440 }
441
442
443 /*!
444  * Apply a translation to a matrix.
445  *
446  * \ingroup matrix
447  */
448 void
449 lib3ds_matrix_translate_xyz(Lib3dsMatrix m, Lib3dsFloat x, Lib3dsFloat y, Lib3dsFloat z)
450 {
451   int i;
452   
453   for (i=0; i<3; i++) {
454     m[3][i]+= m[0][i]*x + m[1][i]*y + m[2][i]*z;
455   }
456 }
457
458
459 /*!
460  * Apply a translation to a matrix.
461  *
462  * \ingroup matrix
463  */
464 void
465 lib3ds_matrix_translate(Lib3dsMatrix m, Lib3dsVector t)
466 {
467   int i;
468   
469   for (i=0; i<3; i++) {
470     m[3][i]+= m[0][i]*t[0] + m[1][i]*t[1] + m[2][i]*t[2];
471   }
472 }
473
474
475 /*!
476  * Apply scale factors to a matrix.
477  *
478  * \ingroup matrix
479  */
480 void
481 lib3ds_matrix_scale_xyz(Lib3dsMatrix m, Lib3dsFloat x, Lib3dsFloat y, Lib3dsFloat z)
482 {
483   int i;
484
485   for (i=0; i<4; i++) {
486     m[0][i]*=x;
487     m[1][i]*=y;
488     m[2][i]*=z;
489   }
490 }
491
492
493 /*!
494  * Apply scale factors to a matrix.
495  *
496  * \ingroup matrix
497  */
498 void
499 lib3ds_matrix_scale(Lib3dsMatrix m, Lib3dsVector s)
500 {
501   int i;
502
503   for (i=0; i<4; i++) {
504     m[0][i]*=s[0];
505     m[1][i]*=s[1];
506     m[2][i]*=s[2];
507   }
508 }
509
510
511 /*!
512  * Apply a rotation about the x axis to a matrix.
513  *
514  * \ingroup matrix
515  */
516 void
517 lib3ds_matrix_rotate_x(Lib3dsMatrix m, Lib3dsFloat phi)
518 {
519   Lib3dsFloat SinPhi,CosPhi;
520   Lib3dsFloat a1[4],a2[4];
521
522   SinPhi=(Lib3dsFloat)sin(phi);
523   CosPhi=(Lib3dsFloat)cos(phi);
524   memcpy(a1,m[1],4*sizeof(Lib3dsFloat));
525   memcpy(a2,m[2],4*sizeof(Lib3dsFloat));
526   m[1][0]=CosPhi*a1[0]+SinPhi*a2[0];
527   m[1][1]=CosPhi*a1[1]+SinPhi*a2[1];
528   m[1][2]=CosPhi*a1[2]+SinPhi*a2[2];
529   m[1][3]=CosPhi*a1[3]+SinPhi*a2[3];
530   m[2][0]=-SinPhi*a1[0]+CosPhi*a2[0];
531   m[2][1]=-SinPhi*a1[1]+CosPhi*a2[1];
532   m[2][2]=-SinPhi*a1[2]+CosPhi*a2[2];
533   m[2][3]=-SinPhi*a1[3]+CosPhi*a2[3];
534 }
535
536
537 /*!
538  * Apply a rotation about the y axis to a matrix.
539  *
540  * \ingroup matrix
541  */
542 void
543 lib3ds_matrix_rotate_y(Lib3dsMatrix m, Lib3dsFloat phi)
544 {
545   Lib3dsFloat SinPhi,CosPhi;
546   Lib3dsFloat a0[4],a2[4];
547
548   SinPhi=(Lib3dsFloat)sin(phi);
549   CosPhi=(Lib3dsFloat)cos(phi);
550   memcpy(a0,m[0],4*sizeof(Lib3dsFloat));
551   memcpy(a2,m[2],4*sizeof(Lib3dsFloat));
552   m[0][0]=CosPhi*a0[0]-SinPhi*a2[0];
553   m[0][1]=CosPhi*a0[1]-SinPhi*a2[1];
554   m[0][2]=CosPhi*a0[2]-SinPhi*a2[2];
555   m[0][3]=CosPhi*a0[3]-SinPhi*a2[3];
556   m[2][0]=SinPhi*a0[0]+CosPhi*a2[0];
557   m[2][1]=SinPhi*a0[1]+CosPhi*a2[1];
558   m[2][2]=SinPhi*a0[2]+CosPhi*a2[2];
559   m[2][3]=SinPhi*a0[3]+CosPhi*a2[3];
560 }
561
562
563 /*!
564  * Apply a rotation about the z axis to a matrix.
565  *
566  * \ingroup matrix
567  */
568 void
569 lib3ds_matrix_rotate_z(Lib3dsMatrix m, Lib3dsFloat phi)
570 {
571   Lib3dsFloat SinPhi,CosPhi;
572   Lib3dsFloat a0[4],a1[4];
573   
574   SinPhi=(Lib3dsFloat)sin(phi);
575   CosPhi=(Lib3dsFloat)cos(phi);
576   memcpy(a0,m[0],4*sizeof(Lib3dsFloat));
577   memcpy(a1,m[1],4*sizeof(Lib3dsFloat));
578   m[0][0]=CosPhi*a0[0]+SinPhi*a1[0];
579   m[0][1]=CosPhi*a0[1]+SinPhi*a1[1];
580   m[0][2]=CosPhi*a0[2]+SinPhi*a1[2];
581   m[0][3]=CosPhi*a0[3]+SinPhi*a1[3];
582   m[1][0]=-SinPhi*a0[0]+CosPhi*a1[0];
583   m[1][1]=-SinPhi*a0[1]+CosPhi*a1[1];
584   m[1][2]=-SinPhi*a0[2]+CosPhi*a1[2];
585   m[1][3]=-SinPhi*a0[3]+CosPhi*a1[3];
586 }
587
588
589 /*!
590  * Apply a rotation about an arbitrary axis to a matrix.
591  *
592  * \ingroup matrix
593  */
594 void
595 lib3ds_matrix_rotate(Lib3dsMatrix m, Lib3dsQuat q)
596 {
597   Lib3dsFloat s,xs,ys,zs,wx,wy,wz,xx,xy,xz,yy,yz,zz,l;
598   Lib3dsMatrix a,b;
599
600   lib3ds_matrix_copy(a, m);
601
602   l=q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
603   if (fabs(l)<LIB3DS_EPSILON) {
604     s=1.0f;
605   }
606   else {
607     s=2.0f/l;
608   }
609
610   xs = q[0] * s;   ys = q[1] * s;  zs = q[2] * s;
611   wx = q[3] * xs;  wy = q[3] * ys; wz = q[3] * zs;
612   xx = q[0] * xs;  xy = q[0] * ys; xz = q[0] * zs;
613   yy = q[1] * ys;  yz = q[1] * zs; zz = q[2] * zs;
614
615   b[0][0]=1.0f - (yy +zz);
616   b[1][0]=xy - wz;
617   b[2][0]=xz + wy;
618   b[0][1]=xy + wz;
619   b[1][1]=1.0f - (xx +zz);
620   b[2][1]=yz - wx;
621   b[0][2]=xz - wy;
622   b[1][2]=yz + wx;
623   b[2][2]=1.0f - (xx + yy);
624   b[3][0]=b[3][1]=b[3][2]=b[0][3]=b[1][3]=b[2][3]=0.0f;
625   b[3][3]=1.0f;
626
627   lib3ds_matrix_mul(m,a,b);
628 }
629
630
631 /*!
632  * Apply a rotation about an arbitrary axis to a matrix.
633  *
634  * \ingroup matrix
635  */
636 void
637 lib3ds_matrix_rotate_axis(Lib3dsMatrix m, Lib3dsVector axis, Lib3dsFloat angle)
638 {
639   Lib3dsQuat q;
640   
641   lib3ds_quat_axis_angle(q,axis,angle);
642   lib3ds_matrix_rotate(m,q);
643 }
644
645
646 /*!
647  * Compute a camera matrix based on position, target and roll.
648  *
649  * Generates a translate/rotate matrix that maps world coordinates
650  * to camera coordinates.  Resulting matrix does not include perspective
651  * transform.
652  *
653  * \param matrix Destination matrix.
654  * \param pos Camera position
655  * \param tgt Camera target
656  * \param roll Roll angle
657  *
658  * \ingroup matrix
659  */
660 void
661 lib3ds_matrix_camera(Lib3dsMatrix matrix, Lib3dsVector pos,
662   Lib3dsVector tgt, Lib3dsFloat roll)
663 {
664   Lib3dsMatrix M,R;
665   Lib3dsVector x, y, z;
666
667   lib3ds_vector_sub(y, tgt, pos);
668   lib3ds_vector_normalize(y);
669
670   if (y[0] != 0. || y[1] != 0) {
671     z[0] = 0;
672     z[1] = 0;
673     z[2] = 1.0;
674   }
675   else {        /* Special case:  looking straight up or down z axis */
676     z[0] = -1.0;
677     z[1] = 0;
678     z[2] = 0;
679   }
680
681   lib3ds_vector_cross(x, y, z);
682   lib3ds_vector_cross(z, x, y);
683   lib3ds_vector_normalize(x);
684   lib3ds_vector_normalize(z);
685
686   lib3ds_matrix_identity(M);
687   M[0][0] = x[0];
688   M[1][0] = x[1];
689   M[2][0] = x[2];
690   M[0][1] = y[0];
691   M[1][1] = y[1];
692   M[2][1] = y[2];
693   M[0][2] = z[0];
694   M[1][2] = z[1];
695   M[2][2] = z[2];
696
697   lib3ds_matrix_identity(R);
698   lib3ds_matrix_rotate_y(R, roll);
699   lib3ds_matrix_mul(matrix, R,M);
700   lib3ds_matrix_translate_xyz(matrix, -pos[0],-pos[1],-pos[2]);
701 }
702
703
704 /*!
705  * \ingroup matrix
706  */
707 void
708 lib3ds_matrix_dump(Lib3dsMatrix matrix)
709 {
710   int i,j;
711
712   for (i=0; i<4; ++i) {
713     for (j=0; j<4; ++j) {
714       printf("%f ", matrix[j][i]);
715     }
716     printf("\n");
717   }
718 }
719
720
721
722
723