]> icculus.org git repositories - taylor/freespace2.git/blob - src/math/vecmat.cpp
Initial revision
[taylor/freespace2.git] / src / math / vecmat.cpp
1 /*
2  * $Logfile: /Freespace2/code/Math/VecMat.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module containg functions for manipulating vectors and matricies
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 10    9/08/99 3:36p Jefff
15  * Make sure argument of sqrt is positive in approach.
16  * 
17  * 9     6/22/99 1:51p Danw
18  * Some sanity for vm_vec_dist_to_line(...)
19  * 
20  * 8     6/18/99 5:16p Dave
21  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
22  * dialog to PXO screen.
23  * 
24  * 7     4/28/99 11:13p Dave
25  * Temporary checkin of artillery code.
26  * 
27  * 6     1/24/99 11:37p Dave
28  * First full rev of beam weapons. Very customizable. Removed some bogus
29  * Int3()'s in low level net code.
30  * 
31  * 5     1/12/99 12:53a Dave
32  * More work on beam weapons - made collision detection very efficient -
33  * collide against all object types properly - made 3 movement types
34  * smooth. Put in test code to check for possible non-darkening pixels on
35  * object textures.
36  * 
37  * 4     1/06/99 2:24p Dave
38  * Stubs and release build fixes.
39  * 
40  * 3     11/18/98 4:10p Johnson
41  * Add assert in vm_interpolate_matrix
42  * 
43  * 2     10/07/98 10:53a Dave
44  * Initial checkin.
45  * 
46  * 1     10/07/98 10:49a Dave
47  * 
48  * 72    9/11/98 10:10a Andsager
49  * Optimize and rename matrix_decomp to vm_matrix_to_rot_axis_and_angle,
50  * rename quatern_rot to vm_quaternion_rotate
51  * 
52  * 71    5/01/98 2:25p Andsager
53  * Fix bug in matrix interpolate (approach) when in rotvel is above limit.
54  * 
55  * 70    4/07/98 3:10p Andsager
56  * Make vm_test_parallel based on absolute difference.  Optimize matrix
57  * decomp.  Fix potential bug in get_camera_limits with time = 0.
58  * Optimize vm_forward_interpolate.
59  * 
60  * 69    4/06/98 8:54a Andsager
61  * Fix bug where matrix interpolate gets accel of 0
62  * 
63  * 68    4/03/98 5:34p Andsager
64  * Optimized approach and away (used in matrix interpolation)
65  * 
66  * 67    4/01/98 9:21p John
67  * Made NDEBUG, optimized build with no warnings or errors.
68  * 
69  * 66    3/23/98 1:12p Andsager
70  * Reformat matrix inerpolation code
71  * 
72  * 65    3/23/98 12:53p Andsager
73  * 
74  * 63    3/09/98 3:51p Mike
75  * More error checking.
76  * 
77  * 62    2/26/98 3:28p John
78  * Changed all sqrt's to use fl_sqrt.  Took out isqrt function
79  * 
80  * 61    2/02/98 5:12p Mike
81  * Make vm_vec_rand_vec_quick() detect potential null vector condition and
82  * recover.
83  * 
84  * 60    1/20/98 9:47a Mike
85  * Suppress optimized compiler warnings.
86  * Some secondary weapon work.
87  * 
88  * 59    12/17/97 5:44p Andsager
89  * Change vm_matrix_interpolate so that it does not overshoot if optional
90  * last parameter is 1
91  * 
92  * 58    9/30/97 5:04p Andsager
93  * add vm_estimate_next_orientation
94  * 
95  * 57    9/28/97 2:17p Andsager
96  * added vm_project_point_onto_plane
97  * 
98  * 56    9/09/97 10:15p Andsager
99  * added vm_rotate_vec_to_body() and vm_rotate_vec_to_world()
100  * 
101  * 55    8/20/97 5:33p Andsager
102  * added vm_vec_projection_parallel and vm_vec_projection_onto_surface
103  * 
104  * 54    8/20/97 9:51a Lawrance
105  * swap x and y parameters in atan2_safe() to be consistent with library
106  * atan2()
107  * 
108  * 53    8/20/97 9:40a Lawrance
109  * modified special case values in atan2_safe()
110  * 
111  * 52    8/19/97 11:41p Lawrance
112  * use atan2_safe() instead of atan2()
113  * 
114  * 51    8/18/97 4:46p Hoffoss
115  * Added global default axis vector constants.
116  * 
117  * 50    8/03/97 3:54p Lawrance
118  * added vm_find_bounding_sphere()
119  * 
120  * 49    7/28/97 3:40p Andsager
121  * remove duplicate vm_forwarad_interpolate
122  * 
123  * 48    7/28/97 2:21p John
124  * changed vecmat functions to not return src.  Started putting in code
125  * for inline vector math.    Fixed some bugs with optimizer.
126  * 
127  * 47    7/28/97 3:24p Andsager
128  * 
129  * 46    7/28/97 2:41p Mike
130  * Replace vm_forward_interpolate().
131  * 
132  * 45    7/28/97 1:18p Andsager
133  * implement vm_fvec_matrix_interpolate(), which interpolates matrices on
134  * xy and then z
135  * 
136  * 44    7/28/97 10:28a Mike
137  * Use forward_interpolate() to prevent weird banking behavior.
138  * 
139  * Suppress a couple annoying mprints and clarify another.
140  * 
141  * 43    7/24/97 5:24p Andsager
142  * implement forward vector interpolation
143  * 
144  * 42    7/10/97 8:52a Andsager
145  * optimization and clarification of matrix_decomp()
146  * 
147  * 41    7/09/97 2:54p Mike
148  * More matrix_decomp optimization.
149  * 
150  * 40    7/09/97 2:52p Mike
151  * Optimize and error-prevent matrix_decomp().
152  * 
153  * 39    7/09/97 12:05a Mike
154  * Error prevention in matrix_interpolate().
155  * 
156  * 38    7/07/97 11:58p Lawrance
157  * add get_camera_limits()
158  * 
159  * 37    7/03/97 11:22a Mike
160  * Fix bug in matrix_interpolate.  Was doing result = goal instead of
161  * *result = *goal.
162  * 
163  * 36    7/03/97 9:27a Mike
164  * Hook in Dave's latest version of matrix_interpolate which doesn't jerk.
165  * 
166  * 35    7/02/97 4:25p Mike
167  * Add matrix_interpolate(), but don't call it.
168  * 
169  * 34    7/01/97 3:27p Mike
170  * Improve skill level support.
171  * 
172  * 33    6/25/97 12:27p Hoffoss
173  * Added some functions I needed for Fred.
174  * 
175  * 32    5/21/97 8:49a Lawrance
176  * added vm_vec_same()
177  * 
178  * 31    4/15/97 4:00p Mike
179  * Intermediate checkin caused by getting other files.  Working on camera
180  * slewing system.
181  * 
182  * 30    4/10/97 3:20p Mike
183  * Change hull damage to be like shields.
184  * 
185  * 29    3/17/97 1:55p Hoffoss
186  * Added function for error checking matrices.
187  * 
188  * 28    3/11/97 10:46p Mike
189  * Fix make_rand_vec_quick.  Was generating values in -0.5..1.5 instead of
190  * -1.0..1.0.
191  * 
192  * 27    3/06/97 5:36p Mike
193  * Change vec_normalize_safe() back to vec_normalize().
194  * Spruce up docking a bit.
195  * 
196  * 26    3/06/97 10:56a Mike
197  * Write error checking version of vm_vec_normalize().
198  * Fix resultant problems.
199  * 
200  * 25    3/04/97 3:30p John
201  * added function to interpolate an angle.
202  * 
203  * 24    2/26/97 10:32a John
204  * changed debris collision to use vm_vec_dist_squared.  Changed
205  * vm_vec_dist_squared to not int3 on bad values.
206  * 
207  * 23    2/25/97 5:54p Hoffoss
208  * Improved vector and matrix compare functions.
209  * 
210  * 22    2/25/97 5:28p Hoffoss
211  * added some commented out test code.
212  * 
213  * 21    2/25/97 5:12p John
214  * Added functions to see if two matrices or vectors are close.
215  * 
216  * $NoKeywords: $
217  *
218 */
219
220 #include <stdio.h>
221 #include <math.h>
222
223 #include "vecmat.h"
224 #include "floating.h"
225
226 #define SMALL_NUM       1e-7
227 #define SMALLER_NUM     1e-20
228 #define CONVERT_RADIANS 0.017453                // conversion factor from degrees to radians
229 int index_largest (float a, float b, float c);  // returns index of largest, NO_LARGEST if all less than SMALL_NUM
230
231
232 vector vmd_zero_vector = ZERO_VECTOR;
233 vector vmd_x_vector = { 1.0f, 0.0f, 0.0f };
234 vector vmd_y_vector = { 0.0f, 1.0f, 0.0f };
235 vector vmd_z_vector = { 0.0f, 0.0f, 1.0f };
236 matrix vmd_identity_matrix = IDENTITY_MATRIX;
237
238 #define UNINITIALIZED_VALUE     -12345678.9f
239
240 // -----------------------------------------------------------
241 // atan2_safe()
242 //
243 // Wrapper around atan2() that used atan() to calculate angle.  Safe
244 // for optimized builds.  Handles special cases when x == 0.
245 //
246 float atan2_safe(float y, float x)
247 {
248         float ang;
249
250         // special case, x == 0
251         if ( x == 0 ) {
252                 if ( y == 0 ) 
253                         ang = 0.0f;
254                 else if ( y > 0 )
255                         ang = PI/2;
256                 else
257                         ang = -PI/2;
258
259                 return ang;
260         }
261         
262         ang = (float)atan(y/x);
263         if ( x < 0 ){
264                 ang += PI;
265         }
266
267         return ang;
268 }
269
270 // ---------------------------------------------------------------------
271 // vm_vec_component()
272 //
273 // finds projection of a vector along a unit (normalized) vector 
274 //
275 float vm_vec_projection_parallel(vector *component, vector *src, vector *unit_vec)
276 {
277         float mag;
278         Assert( vm_vec_mag(unit_vec) > 0.999f  &&  vm_vec_mag(unit_vec) < 1.001f );
279
280         mag = vm_vec_dotprod(src, unit_vec);
281         vm_vec_copy_scale(component, unit_vec, mag);
282         return mag;
283 }
284
285 // ---------------------------------------------------------------------
286 // vm_vec_projection_onto_plane()
287 //
288 // finds projection of a vector onto a plane specified by a unit normal vector 
289 //
290 void vm_vec_projection_onto_plane(vector *projection, vector *src, vector *unit_normal)
291 {
292         float mag;
293         Assert( vm_vec_mag(unit_normal) > 0.999f  &&  vm_vec_mag(unit_normal) < 1.001f );
294
295         mag = vm_vec_dotprod(src, unit_normal);
296         *projection = *src;
297         vm_vec_scale_add2(projection, unit_normal, -mag);
298 }
299
300 // ---------------------------------------------------------------------
301 // vm_vec_project_point_onto_plane()
302 //
303 // finds the point on a plane closest to a given point
304 // moves the point in the direction of the plane normal until it is on the plane
305 //
306 void vm_project_point_onto_plane(vector *new_point, vector *point, vector *plane_normal, vector *plane_point)
307 {
308         float D;                // plane constant in Ax+By+Cz+D = 0   or   dot(X,n) - dot(Xp,n) = 0, so D = -dot(Xp,n)
309         float dist;
310         Assert( vm_vec_mag(plane_normal) > 0.999f  &&  vm_vec_mag(plane_normal) < 1.001f );
311
312         D = -vm_vec_dotprod(plane_point, plane_normal);
313         dist = vm_vec_dotprod(point, plane_normal) + D;
314
315         *new_point = *point;
316         vm_vec_scale_add2(new_point, plane_normal, -dist);
317 }
318
319 //      Take abs(x), then sqrt.  Could insert warning message if desired.
320 float asqrt(float x)
321 {
322         if (x < 0.0f)
323                 return fl_sqrt(-x);
324         else
325                 return fl_sqrt(x);
326 }
327
328 void vm_set_identity(matrix *m)
329 {
330         m->rvec.x = 1.0f;       m->rvec.y = 0.0f;       m->rvec.z = 0.0f;
331         m->uvec.x = 0.0f;       m->uvec.y = 1.0f;       m->uvec.z = 0.0f;
332         m->fvec.x = 0.0f;       m->fvec.y = 0.0f;       m->fvec.z = 1.0f;
333 }
334
335 //adds two vectors, fills in dest, returns ptr to dest
336 //ok for dest to equal either source, but should use vm_vec_add2() if so
337 #ifndef _INLINE_VECMAT
338 void vm_vec_add(vector *dest,vector *src0,vector *src1)
339 {
340         dest->x = src0->x + src1->x;
341         dest->y = src0->y + src1->y;
342         dest->z = src0->z + src1->z;
343 }
344 #endif
345
346 //subs two vectors, fills in dest, returns ptr to dest
347 //ok for dest to equal either source, but should use vm_vec_sub2() if so
348 #ifndef _INLINE_VECMAT
349 void vm_vec_sub(vector *dest,vector *src0,vector *src1)
350 {
351         dest->x = src0->x - src1->x;
352         dest->y = src0->y - src1->y;
353         dest->z = src0->z - src1->z;
354 }
355 #endif
356
357
358 //adds one vector to another. returns ptr to dest
359 //dest can equal source
360 #ifndef _INLINE_VECMAT
361 void vm_vec_add2(vector *dest,vector *src)
362 {
363         dest->x += src->x;
364         dest->y += src->y;
365         dest->z += src->z;
366 }
367 #endif
368
369 //subs one vector from another, returns ptr to dest
370 //dest can equal source
371 #ifndef _INLINE_VECMAT
372 void vm_vec_sub2(vector *dest,vector *src)
373 {
374         dest->x -= src->x;
375         dest->y -= src->y;
376         dest->z -= src->z;
377 }
378 #endif
379
380 //averages two vectors. returns ptr to dest
381 //dest can equal either source
382 vector *vm_vec_avg(vector *dest,vector *src0,vector *src1)
383 {
384         dest->x = (src0->x + src1->x) * 0.5f;
385         dest->y = (src0->y + src1->y) * 0.5f;
386         dest->z = (src0->z + src1->z) * 0.5f;
387
388         return dest;
389 }
390
391
392 //averages four vectors. returns ptr to dest
393 //dest can equal any source
394 vector *vm_vec_avg4(vector *dest,vector *src0,vector *src1,vector *src2,vector *src3)
395 {
396         dest->x = (src0->x + src1->x + src2->x + src3->x) * 0.25f;
397         dest->y = (src0->y + src1->y + src2->y + src3->y) * 0.25f;
398         dest->z = (src0->z + src1->z + src2->z + src3->z) * 0.25f;
399         return dest;
400 }
401
402
403 //scales a vector in place.  returns ptr to vector
404 #ifndef _INLINE_VECMAT
405 void vm_vec_scale(vector *dest,float s)
406 {
407         dest->x = dest->x * s;
408         dest->y = dest->y * s;
409         dest->z = dest->z * s;
410 }
411 #endif
412
413
414 //scales and copies a vector.  returns ptr to dest
415 #ifndef _INLINE_VECMAT
416 void vm_vec_copy_scale(vector *dest,vector *src,float s)
417 {
418         dest->x = src->x*s;
419         dest->y = src->y*s;
420         dest->z = src->z*s;
421 }
422 #endif
423
424 //scales a vector, adds it to another, and stores in a 3rd vector
425 //dest = src1 + k * src2
426 #ifndef _INLINE_VECMAT
427 void vm_vec_scale_add(vector *dest,vector *src1,vector *src2,float k)
428 {
429         dest->x = src1->x + src2->x*k;
430         dest->y = src1->y + src2->y*k;
431         dest->z = src1->z + src2->z*k;
432 }
433 #endif
434
435 //scales a vector and adds it to another
436 //dest += k * src
437 #ifndef _INLINE_VECMAT
438 void vm_vec_scale_add2(vector *dest,vector *src,float k)
439 {
440         dest->x += src->x*k;
441         dest->y += src->y*k;
442         dest->z += src->z*k;
443 }
444 #endif
445
446 //scales a vector and adds it to another
447 //dest += k * src
448 #ifndef _INLINE_VECMAT
449 void vm_vec_scale_sub2(vector *dest,vector *src,float k)
450 {
451         dest->x -= src->x*k;
452         dest->y -= src->y*k;
453         dest->z -= src->z*k;
454 }
455 #endif
456
457 //scales a vector in place, taking n/d for scale.  returns ptr to vector
458 //dest *= n/d
459 #ifndef _INLINE_VECMAT
460 void vm_vec_scale2(vector *dest,float n,float d)
461 {       
462         d = 1.0f/d;
463
464         dest->x = dest->x* n * d;
465         dest->y = dest->y* n * d;
466         dest->z = dest->z* n * d;
467 }
468 #endif
469
470 //returns dot product of 2 vectors
471 #ifndef _INLINE_VECMAT
472 float vm_vec_dotprod(vector *v0,vector *v1)
473 {
474         return (v1->x*v0->x)+(v1->y*v0->y)+(v1->z*v0->z);
475 }
476 #endif
477
478
479 //returns dot product of <x,y,z> and vector
480 #ifndef _INLINE_VECMAT
481 float vm_vec_dot3(float x,float y,float z,vector *v)
482 {
483         return (x*v->x)+(y*v->y)+(z*v->z);
484 }
485 #endif
486
487 //returns magnitude of a vector
488 float vm_vec_mag(vector *v)
489 {
490         float x,y,z,mag1, mag2;
491         x = v->x*v->x;
492         y = v->y*v->y;
493         z = v->z*v->z;
494         mag1 = x+y+z;
495         if ( mag1 < 0.0 )
496                 Int3();
497         mag2 = fl_sqrt(mag1);
498         if ( mag2 < 0.0 )
499                 Int3();
500         return mag2;
501 }
502
503 //returns squared magnitude of a vector, useful if you want to compare distances
504 float vm_vec_mag_squared(vector *v)
505 {
506         float x,y,z,mag1;
507         x = v->x*v->x;
508         y = v->y*v->y;
509         z = v->z*v->z;
510         mag1 = x+y+z;
511         return mag1;
512 }
513
514 float vm_vec_dist_squared(vector *v0, vector *v1)
515 {
516         float dx, dy, dz;
517
518         dx = v0->x - v1->x;
519         dy = v0->y - v1->y;
520         dz = v0->z - v1->z;
521         return dx*dx + dy*dy + dz*dz;
522 }
523
524 //computes the distance between two points. (does sub and mag)
525 float vm_vec_dist(vector *v0,vector *v1)
526 {
527         float t1;
528         vector t;
529
530         vm_vec_sub(&t,v0,v1);
531
532         t1 = vm_vec_mag(&t);
533
534         return t1;
535 }
536
537
538
539 //computes an approximation of the magnitude of the vector
540 //uses dist = largest + next_largest*3/8 + smallest*3/16
541 float vm_vec_mag_quick(vector *v)
542 {
543         float a,b,c,bc, t;
544
545         if ( v->x < 0.0 )
546                 a = -v->x;
547         else
548                 a = v->x;
549
550         if ( v->y < 0.0 )
551                 b = -v->y;
552         else
553                 b = v->y;
554
555         if ( v->z < 0.0 )
556                 c = -v->z;
557         else
558                 c = v->z;
559
560         if (a < b) {
561                 float t=a; a=b; b=t;
562         }
563
564         if (b < c) {
565                 float t=b; b=c; c=t;
566
567                 if (a < b) {
568                         float t=a; a=b; b=t;
569                 }
570         }
571
572         bc = (b * 0.25f) + (c * 0.125f);
573
574         t = a + bc + (bc * 0.5f);
575
576         return t;
577 }
578
579 //computes an approximation of the distance between two points.
580 //uses dist = largest + next_largest*3/8 + smallest*3/16
581 float vm_vec_dist_quick(vector *v0,vector *v1)
582 {
583         vector t;
584
585         vm_vec_sub(&t,v0,v1);
586
587         return vm_vec_mag_quick(&t);
588 }
589
590 //normalize a vector. returns mag of source vec
591 float vm_vec_copy_normalize(vector *dest,vector *src)
592 {
593         float m;
594
595         m = vm_vec_mag(src);
596
597         //      Mainly here to trap attempts to normalize a null vector.
598         if (m <= 0.0f) {
599                 Warning(LOCATION,       "Null vector in vector normalize.\n"
600                                                                 "Trace out of vecmat.cpp and find offending code.\n");
601                 dest->x = 1.0f;
602                 dest->y = 0.0f;
603                 dest->z = 0.0f;
604
605                 return 1.0f;
606         }
607
608         float im = 1.0f / m;
609
610         dest->x = src->x * im;
611         dest->y = src->y * im;
612         dest->z = src->z * im;
613         
614         return m;
615 }
616
617 //normalize a vector. returns mag of source vec
618 float vm_vec_normalize(vector *v)
619 {
620         float t;
621         t = vm_vec_copy_normalize(v,v);
622         return t;
623 }
624
625 // Normalize a vector.
626 //      If vector is 0,0,0, return 1,0,0.
627 //      Don't generate a Warning().
628 // returns mag of source vec
629 float vm_vec_normalize_safe(vector *v)
630 {
631         float m;
632
633         m = vm_vec_mag(v);
634
635         //      Mainly here to trap attempts to normalize a null vector.
636         if (m <= 0.0f) {
637                 v->x = 1.0f;
638                 v->y = 0.0f;
639                 v->z = 0.0f;
640                 return 1.0f;
641         }
642
643         float im = 1.0f / m;
644
645         v->x *= im;
646         v->y *= im;
647         v->z *= im;
648
649         return m;
650
651 }
652
653
654 //returns approximation of 1/magnitude of a vector
655 float vm_vec_imag(vector *v)
656 {
657 //      return 1.0f / sqrt( (v->x*v->x)+(v->y*v->y)+(v->z*v->z) );      
658         return fl_isqrt( (v->x*v->x)+(v->y*v->y)+(v->z*v->z) );
659 }
660
661 //normalize a vector. returns 1/mag of source vec. uses approx 1/mag
662 float vm_vec_copy_normalize_quick(vector *dest,vector *src)
663 {
664 //      return vm_vec_copy_normalize(dest, src);
665         float im;
666
667         im = vm_vec_imag(src);
668
669         Assert(im > 0.0f);
670
671         dest->x = src->x*im;
672         dest->y = src->y*im;
673         dest->z = src->z*im;
674
675         return 1.0f/im;
676 }
677
678 //normalize a vector. returns mag of source vec. uses approx mag
679 float vm_vec_normalize_quick(vector *src)
680 {
681 //      return vm_vec_normalize(src);
682
683         float im;
684
685         im = vm_vec_imag(src);
686
687         Assert(im > 0.0f);
688
689         src->x = src->x*im;
690         src->y = src->y*im;
691         src->z = src->z*im;
692
693         return 1.0f/im;
694
695 }
696
697 //normalize a vector. returns mag of source vec. uses approx mag
698 float vm_vec_copy_normalize_quick_mag(vector *dest,vector *src)
699 {
700 //      return vm_vec_copy_normalize(dest, src);
701
702         float m;
703
704         m = vm_vec_mag_quick(src);
705
706         Assert(m > 0.0f);
707
708         float im = 1.0f / m;
709
710         dest->x = src->x * im;
711         dest->y = src->y * im;
712         dest->z = src->z * im;
713
714         return m;
715
716 }
717
718 //normalize a vector. returns mag of source vec. uses approx mag
719 float vm_vec_normalize_quick_mag(vector *v)
720 {
721 //      return vm_vec_normalize(v);
722         float m;
723
724         m = vm_vec_mag_quick(v);
725
726         Assert(m > 0.0f);
727
728         v->x = v->x*m;
729         v->y = v->y*m;
730         v->z = v->z*m;
731
732         return m;
733
734 }
735
736
737
738 //return the normalized direction vector between two points
739 //dest = normalized(end - start).  Returns mag of direction vector
740 //NOTE: the order of the parameters matches the vector subtraction
741 float vm_vec_normalized_dir(vector *dest,vector *end,vector *start)
742 {
743         float t;
744
745         vm_vec_sub(dest,end,start);
746         t = vm_vec_normalize(dest);
747         return t;
748 }
749
750 //return the normalized direction vector between two points
751 //dest = normalized(end - start).  Returns mag of direction vector
752 //NOTE: the order of the parameters matches the vector subtraction
753 float vm_vec_normalized_dir_quick(vector *dest,vector *end,vector *start)
754 {
755         vm_vec_sub(dest,end,start);
756
757         return vm_vec_normalize_quick(dest);
758 }
759
760 //return the normalized direction vector between two points
761 //dest = normalized(end - start).  Returns mag of direction vector
762 //NOTE: the order of the parameters matches the vector subtraction
763 float vm_vec_normalized_dir_quick_mag(vector *dest,vector *end,vector *start)
764 {
765         float t;
766         vm_vec_sub(dest,end,start);
767
768         t = vm_vec_normalize_quick_mag(dest);
769         return t;
770 }
771
772 //computes surface normal from three points. result is normalized
773 //returns ptr to dest
774 //dest CANNOT equal either source
775 vector *vm_vec_normal(vector *dest,vector *p0,vector *p1,vector *p2)
776 {
777         vm_vec_perp(dest,p0,p1,p2);
778
779         vm_vec_normalize(dest);
780
781         return dest;
782 }
783
784
785 //computes cross product of two vectors.
786 //Note: this magnitude of the resultant vector is the
787 //product of the magnitudes of the two source vectors.  This means it is
788 //quite easy for this routine to overflow and underflow.  Be careful that
789 //your inputs are ok.
790 vector *vm_vec_crossprod(vector *dest,vector *src0,vector *src1)
791 {
792         dest->x = (src0->y * src1->z) - (src0->z * src1->y);
793         dest->y = (src0->z * src1->x) - (src0->x * src1->z);
794         dest->z = (src0->x * src1->y) - (src0->y * src1->x);
795
796         return dest;
797 }
798
799 // test if 2 vectors are parallel or not.
800 int vm_test_parallel(vector *src0, vector *src1)
801 {
802         if ( (fl_abs(src0->x - src1->x) < 1e-4) && (fl_abs(src0->y - src1->y) < 1e-4) && (fl_abs(src0->z - src1->z) < 1e-4) ) {
803                 return 1;
804         } else {
805                 return 0;
806         }
807 }
808
809 //computes non-normalized surface normal from three points.
810 //returns ptr to dest
811 //dest CANNOT equal either source
812 vector *vm_vec_perp(vector *dest,vector *p0,vector *p1,vector *p2)
813 {
814         vector t0,t1;
815
816         vm_vec_sub(&t0,p1,p0);
817         vm_vec_sub(&t1,p2,p1);
818
819         return vm_vec_crossprod(dest,&t0,&t1);
820 }
821
822
823 //computes the delta angle between two vectors.
824 //vectors need not be normalized. if they are, call vm_vec_delta_ang_norm()
825 //the forward vector (third parameter) can be NULL, in which case the absolute
826 //value of the angle in returned.  Otherwise the angle around that vector is
827 //returned.
828 float vm_vec_delta_ang(vector *v0,vector *v1,vector *fvec)
829 {
830         float t;
831         vector t0,t1,t2;
832
833         vm_vec_copy_normalize(&t0,v0);
834         vm_vec_copy_normalize(&t1,v1);
835         vm_vec_copy_normalize(&t2,fvec);
836
837         t = vm_vec_delta_ang_norm(&t0,&t1,&t2);
838
839         return t;
840 }
841
842 //computes the delta angle between two normalized vectors.
843 float vm_vec_delta_ang_norm(vector *v0,vector *v1,vector *fvec)
844 {
845         float a;
846         vector t;
847
848         a = (float)acos(vm_vec_dot(v0,v1));
849
850         if (fvec) {
851                 vm_vec_cross(&t,v0,v1);
852                 if ( vm_vec_dotprod(&t,fvec) < 0.0 )    {
853                         a = -a;
854                 }
855         }
856
857         return a;
858 }
859
860 matrix *sincos_2_matrix(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh)
861 {
862         float sbsh,cbch,cbsh,sbch;
863
864
865         sbsh = sinb*sinh;
866         cbch = cosb*cosh;
867         cbsh = cosb*sinh;
868         sbch = sinb*cosh;
869
870         m->rvec.x = cbch + sinp*sbsh;           //m1
871         m->uvec.z = sbsh + sinp*cbch;           //m8
872
873         m->uvec.x = sinp*cbsh - sbch;           //m2
874         m->rvec.z = sinp*sbch - cbsh;           //m7
875
876         m->fvec.x = sinh*cosp;                          //m3
877         m->rvec.y = sinb*cosp;                          //m4
878         m->uvec.y = cosb*cosp;                          //m5
879         m->fvec.z = cosh*cosp;                          //m9
880
881         m->fvec.y = -sinp;                                                              //m6
882
883
884         return m;
885
886 }
887
888 //computes a matrix from a set of three angles.  returns ptr to matrix
889 matrix *vm_angles_2_matrix(matrix *m,angles *a)
890 {
891         matrix * t;
892         float sinp,cosp,sinb,cosb,sinh,cosh;
893
894         sinp = (float)sin(a->p); cosp = (float)cos(a->p);
895         sinb = (float)sin(a->b); cosb = (float)cos(a->b);
896         sinh = (float)sin(a->h); cosh = (float)cos(a->h);
897
898         t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh);
899
900         return t;
901 }
902
903 //computes a matrix from one angle.
904 //      angle_index = 0,1,2 for p,b,h
905 matrix *vm_angle_2_matrix(matrix *m, float a, int angle_index)
906 {
907         matrix * t;
908         float sinp,cosp,sinb,cosb,sinh,cosh;
909
910         sinp = (float)sin(0.0f);        cosp = (float)cos(0.0f);
911         sinb = (float)sin(0.0f);        cosb = (float)cos(0.0f);
912         sinh = (float)sin(0.0f);        cosh = (float)cos(0.0f);
913
914         switch (angle_index) {
915         case 0:
916                 sinp = (float)sin(a); cosp = (float)cos(a);
917                 break;
918         case 1:
919                 sinb = (float)sin(a); cosb = (float)cos(a);
920                 break;
921         case 2:
922                 sinh = (float)sin(a); cosh = (float)cos(a);
923                 break;
924         }
925
926         t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh);
927
928         return t;
929 }
930
931
932 //computes a matrix from a forward vector and an angle
933 matrix *vm_vec_ang_2_matrix(matrix *m,vector *v,float a)
934 {
935         matrix * t;
936         float sinb,cosb,sinp,cosp,sinh,cosh;
937
938         sinb = (float)sin(a); cosb = (float)cos(a);
939
940         sinp = -v->y;
941         cosp = fl_sqrt(1.0 - sinp*sinp);
942
943         sinh = v->x / cosp;
944         cosh = v->z / cosp;
945
946         t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh);
947
948         return t;
949 }
950
951
952 //computes a matrix from one or more vectors. The forward vector is required,
953 //with the other two being optional.  If both up & right vectors are passed,
954 //the up vector is used.  If only the forward vector is passed, a bank of
955 //zero is assumed
956 //returns ptr to matrix
957 matrix *vm_vector_2_matrix(matrix *m,vector *fvec,vector *uvec,vector *rvec)
958 {
959         vector *xvec=&m->rvec,*yvec=&m->uvec,*zvec=&m->fvec;
960
961
962         Assert(fvec != NULL);
963
964         //      This had been commented out, but that's bogus.  Code below relies on a valid zvec.
965         if (vm_vec_copy_normalize(zvec,fvec) == 0.0) {
966                 Assert(0);
967                 return m;
968         }
969
970         if (uvec == NULL) {
971
972                 if (rvec == NULL) {             //just forward vec
973
974 bad_vector2:
975         ;
976
977                         if ((zvec->x==0.0) && (zvec->z==0.0)) {         //forward vec is straight up or down
978
979                                 m->rvec.x = (float)1.0;
980                                 m->uvec.z = (zvec->y<0.0)?(float)1.0:(float)-1.0;
981
982                                 m->rvec.y = m->rvec.z = m->uvec.x = m->uvec.y = (float)0.0;
983                         }
984                         else {          //not straight up or down
985
986                                 xvec->x = zvec->z;
987                                 xvec->y = (float)0.0;
988                                 xvec->z = -zvec->x;
989
990                                 vm_vec_normalize(xvec);
991
992                                 vm_vec_crossprod(yvec,zvec,xvec);
993
994                         }
995
996                 }
997                 else {                                          //use right vec
998
999                         if (vm_vec_copy_normalize(xvec,rvec) == 0.0)
1000                                 goto bad_vector2;
1001
1002                         vm_vec_crossprod(yvec,zvec,xvec);
1003
1004                         //normalize new perpendicular vector
1005                         if (vm_vec_normalize(yvec) == 0.0)
1006                                 goto bad_vector2;
1007
1008                         //now recompute right vector, in case it wasn't entirely perpendiclar
1009                         vm_vec_crossprod(xvec,yvec,zvec);
1010
1011                 }
1012         }
1013         else {          //use up vec
1014
1015                 if (vm_vec_copy_normalize(yvec,uvec) == 0.0f)
1016                         goto bad_vector2;
1017
1018                 vm_vec_crossprod(xvec,yvec,zvec);
1019                 
1020                 //normalize new perpendicular vector
1021                 if (vm_vec_normalize(xvec) == 0.0)
1022                         goto bad_vector2;
1023
1024                 //now recompute up vector, in case it wasn't entirely perpendiclar
1025                 vm_vec_crossprod(yvec,zvec,xvec);
1026
1027         }
1028         return m;
1029 }
1030
1031 //quicker version of vm_vector_2_matrix() that takes normalized vectors
1032 matrix *vm_vector_2_matrix_norm(matrix *m,vector *fvec,vector *uvec,vector *rvec)
1033 {
1034         vector *xvec=&m->rvec,*yvec=&m->uvec,*zvec=&m->fvec;
1035
1036
1037         Assert(fvec != NULL);
1038
1039         *zvec = *fvec;
1040
1041         if (uvec == NULL) {
1042
1043                 if (rvec == NULL) {             //just forward vec
1044
1045 bad_vector2:
1046         ;
1047
1048                         if ((zvec->x==0.0) && (zvec->z==0.0)) {         //forward vec is straight up or down
1049
1050                                 m->rvec.x = (float)1.0;
1051                                 m->uvec.z = (zvec->y<0.0)?(float)1.0:(float)-1.0;
1052
1053                                 m->rvec.y = m->rvec.z = m->uvec.x = m->uvec.y = (float)0.0;
1054                         }
1055                         else {          //not straight up or down
1056
1057                                 xvec->x = zvec->z;
1058                                 xvec->y = (float)0.0;
1059                                 xvec->z = -zvec->x;
1060
1061                                 vm_vec_normalize(xvec);
1062
1063                                 vm_vec_crossprod(yvec,zvec,xvec);
1064
1065                         }
1066
1067                 }
1068                 else {                                          //use right vec
1069
1070                         vm_vec_crossprod(yvec,zvec,xvec);
1071
1072                         //normalize new perpendicular vector
1073                         if (vm_vec_normalize(yvec) == 0.0)
1074                                 goto bad_vector2;
1075
1076                         //now recompute right vector, in case it wasn't entirely perpendiclar
1077                         vm_vec_crossprod(xvec,yvec,zvec);
1078
1079                 }
1080         }
1081         else {          //use up vec
1082
1083                 vm_vec_crossprod(xvec,yvec,zvec);
1084                 
1085                 //normalize new perpendicular vector
1086                 if (vm_vec_normalize(xvec) == 0.0)
1087                         goto bad_vector2;
1088
1089                 //now recompute up vector, in case it wasn't entirely perpendiclar
1090                 vm_vec_crossprod(yvec,zvec,xvec);
1091
1092         }
1093
1094
1095         return m;
1096 }
1097
1098
1099 //rotates a vector through a matrix. returns ptr to dest vector
1100 //dest CANNOT equal source
1101 vector *vm_vec_rotate(vector *dest,vector *src,matrix *m)
1102 {
1103         dest->x = (src->x*m->rvec.x)+(src->y*m->rvec.y)+(src->z*m->rvec.z);
1104         dest->y = (src->x*m->uvec.x)+(src->y*m->uvec.y)+(src->z*m->uvec.z);
1105         dest->z = (src->x*m->fvec.x)+(src->y*m->fvec.y)+(src->z*m->fvec.z);
1106
1107         return dest;
1108 }
1109
1110 //rotates a vector through the transpose of the given matrix. 
1111 //returns ptr to dest vector
1112 //dest CANNOT equal source
1113 // This is a faster replacement for this common code sequence:
1114 //    vm_copy_transpose_matrix(&tempm,src_matrix);
1115 //    vm_vec_rotate(dst_vec,src_vect,&tempm);
1116 // Replace with:
1117 //    vm_vec_unrotate(dst_vec,src_vect, src_matrix)
1118 //
1119 // THIS DOES NOT ACTUALLY TRANSPOSE THE SOURCE MATRIX!!! So if
1120 // you need it transposed later on, you should use the 
1121 // vm_vec_transpose() / vm_vec_rotate() technique.
1122
1123 vector *vm_vec_unrotate(vector *dest,vector *src,matrix *m)
1124 {
1125         dest->x = (src->x*m->rvec.x)+(src->y*m->uvec.x)+(src->z*m->fvec.x);
1126         dest->y = (src->x*m->rvec.y)+(src->y*m->uvec.y)+(src->z*m->fvec.y);
1127         dest->z = (src->x*m->rvec.z)+(src->y*m->uvec.z)+(src->z*m->fvec.z);
1128
1129         return dest;
1130 }
1131
1132 //transpose a matrix in place. returns ptr to matrix
1133 matrix *vm_transpose_matrix(matrix *m)
1134 {
1135         float t;
1136
1137         t = m->uvec.x;  m->uvec.x = m->rvec.y;  m->rvec.y = t;
1138         t = m->fvec.x;  m->fvec.x = m->rvec.z;  m->rvec.z = t;
1139         t = m->fvec.y;  m->fvec.y = m->uvec.z;  m->uvec.z = t;
1140
1141         return m;
1142 }
1143
1144 //copy and transpose a matrix. returns ptr to matrix
1145 //dest CANNOT equal source. use vm_transpose_matrix() if this is the case
1146 matrix *vm_copy_transpose_matrix(matrix *dest,matrix *src)
1147 {
1148
1149         Assert(dest != src);
1150
1151         dest->rvec.x = src->rvec.x;
1152         dest->rvec.y = src->uvec.x;
1153         dest->rvec.z = src->fvec.x;
1154
1155         dest->uvec.x = src->rvec.y;
1156         dest->uvec.y = src->uvec.y;
1157         dest->uvec.z = src->fvec.y;
1158
1159         dest->fvec.x = src->rvec.z;
1160         dest->fvec.y = src->uvec.z;
1161         dest->fvec.z = src->fvec.z;
1162
1163
1164         return dest;
1165 }
1166
1167 //mulitply 2 matrices, fill in dest.  returns ptr to dest
1168 //dest CANNOT equal either source
1169 matrix *vm_matrix_x_matrix(matrix *dest,matrix *src0,matrix *src1)
1170 {
1171
1172         Assert(dest!=src0 && dest!=src1);
1173
1174         dest->rvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->rvec);
1175         dest->uvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->uvec);
1176         dest->fvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->fvec);
1177
1178         dest->rvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->rvec);
1179         dest->uvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->uvec);
1180         dest->fvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->fvec);
1181
1182         dest->rvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->rvec);
1183         dest->uvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->uvec);
1184         dest->fvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->fvec);
1185
1186
1187         return dest;
1188 }
1189
1190
1191 //extract angles from a matrix
1192 angles *vm_extract_angles_matrix(angles *a,matrix *m)
1193 {
1194         float sinh,cosh,cosp;
1195
1196         if (m->fvec.x==0.0 && m->fvec.z==0.0)           //zero head
1197                 a->h = (float)0.0;
1198         else
1199                 // a->h = (float)atan2(m->fvec.z,m->fvec.x);
1200                 a->h = (float)atan2_safe(m->fvec.x,m->fvec.z);
1201
1202         sinh = (float)sin(a->h); cosh = (float)cos(a->h);
1203
1204         if (fl_abs(sinh) > fl_abs(cosh))                                //sine is larger, so use it
1205                 cosp = m->fvec.x*sinh;
1206         else                                                                                    //cosine is larger, so use it
1207                 cosp = m->fvec.z*cosh;
1208
1209         if (cosp==0.0 && m->fvec.y==0.0)
1210                 a->p = (float)0.0;
1211         else
1212                 // a->p = (float)atan2(cosp,-m->fvec.y);
1213                 a->p = (float)atan2_safe(-m->fvec.y, cosp);
1214
1215
1216         if (cosp == 0.0)        //the cosine of pitch is zero.  we're pitched straight up. say no bank
1217
1218                 a->b = (float)0.0;
1219
1220         else {
1221                 float sinb,cosb;
1222
1223                 sinb = m->rvec.y/cosp;
1224                 cosb = m->uvec.y/cosp;
1225
1226                 if (sinb==0.0 && cosb==0.0)
1227                         a->b = (float)0.0;
1228                 else
1229                         // a->b = (float)atan2(cosb,sinb);
1230                         a->b = (float)atan2_safe(sinb,cosb);
1231         }
1232
1233
1234         return a;
1235 }
1236
1237
1238 //extract heading and pitch from a vector, assuming bank==0
1239 angles *vm_extract_angles_vector_normalized(angles *a,vector *v)
1240 {
1241
1242         a->b = 0.0f;            //always zero bank
1243
1244         a->p = (float)asin(-v->y);
1245
1246         if (v->x==0.0f && v->z==0.0f)
1247                 a->h = (float)0.0;
1248         else
1249                 a->h = (float)atan2_safe(v->z,v->x);
1250
1251         return a;
1252 }
1253
1254 //extract heading and pitch from a vector, assuming bank==0
1255 angles *vm_extract_angles_vector(angles *a,vector *v)
1256 {
1257         vector t;
1258
1259         if (vm_vec_copy_normalize(&t,v) != 0.0)
1260                 vm_extract_angles_vector_normalized(a,&t);
1261
1262         return a;
1263 }
1264
1265 //compute the distance from a point to a plane.  takes the normalized normal
1266 //of the plane (ebx), a point on the plane (edi), and the point to check (esi).
1267 //returns distance in eax
1268 //distance is signed, so negative dist is on the back of the plane
1269 float vm_dist_to_plane(vector *checkp,vector *norm,vector *planep)
1270 {
1271         float t1;
1272         vector t;
1273
1274         vm_vec_sub(&t,checkp,planep);
1275
1276         t1 = vm_vec_dot(&t,norm);
1277
1278         return t1;
1279
1280 }
1281
1282 // Given mouse movement in dx, dy, returns a 3x3 rotation matrix in RotMat.
1283 // Taken from Graphics Gems III, page 51, "The Rolling Ball"
1284 // Example:
1285 //if ( (Mouse.dx!=0) || (Mouse.dy!=0) ) {
1286 //   GetMouseRotation( Mouse.dx, Mouse.dy, &MouseRotMat );
1287 //   vm_matrix_x_matrix(&tempm,&LargeView.ev_matrix,&MouseRotMat);
1288 //   LargeView.ev_matrix = tempm;
1289 //}
1290
1291
1292 void vm_trackball( int idx, int idy, matrix * RotMat )
1293 {
1294         float dr, cos_theta, sin_theta, denom, cos_theta1;
1295         float Radius = 100.0f;
1296         float dx,dy;
1297         float dxdr,dydr;
1298
1299         idy *= -1;
1300
1301         dx = (float)idx; dy = (float)idy;
1302
1303         dr = fl_sqrt(dx*dx+dy*dy);
1304
1305         denom = fl_sqrt(Radius*Radius+dr*dr);
1306         
1307         cos_theta = Radius/denom;
1308         sin_theta = dr/denom;
1309
1310         cos_theta1 = 1.0f - cos_theta;
1311
1312         dxdr = dx/dr;
1313         dydr = dy/dr;
1314
1315         RotMat->rvec.x = cos_theta + (dydr*dydr)*cos_theta1;
1316         RotMat->uvec.x = - ((dxdr*dydr)*cos_theta1);
1317         RotMat->fvec.x = (dxdr*sin_theta);
1318
1319         RotMat->rvec.y = RotMat->uvec.x;
1320         RotMat->uvec.y = cos_theta + ((dxdr*dxdr)*cos_theta1);
1321         RotMat->fvec.y = (dydr*sin_theta);
1322
1323         RotMat->rvec.z = -RotMat->fvec.x;
1324         RotMat->uvec.z = -RotMat->fvec.y;
1325         RotMat->fvec.z = cos_theta;
1326 }
1327
1328 //      Compute the outer product of A = A * transpose(A).  1x3 vector becomes 3x3 matrix.
1329 void vm_vec_outer_product(matrix *mat, vector *vec)
1330 {
1331         mat->rvec.x = vec->x * vec->x;
1332         mat->rvec.y = vec->x * vec->y;
1333         mat->rvec.z = vec->x * vec->z;
1334
1335         mat->uvec.x = vec->y * vec->x;
1336         mat->uvec.y = vec->y * vec->y;
1337         mat->uvec.z = vec->y * vec->z;
1338
1339         mat->fvec.x = vec->z * vec->x;
1340         mat->fvec.y = vec->z * vec->y;
1341         mat->fvec.z = vec->z * vec->z;
1342 }
1343
1344 //      Find the point on the line between p0 and p1 that is nearest to int_pnt.
1345 //      Stuff result in nearest_point.
1346 //      Uses algorithm from page 148 of Strang, Linear Algebra and Its Applications.
1347 //      Returns value indicating whether *nearest_point is between *p0 and *p1.
1348 //      0.0f means *nearest_point is *p0, 1.0f means it's *p1. 2.0f means it's beyond p1 by 2x.
1349 //      -1.0f means it's "before" *p0 by 1x.
1350 float find_nearest_point_on_line(vector *nearest_point, vector *p0, vector *p1, vector *int_pnt)
1351 {
1352         vector  norm, xlated_int_pnt, projected_point;
1353         matrix  mat;
1354         float           mag, dot;
1355
1356         vm_vec_sub(&norm, p1, p0);
1357         vm_vec_sub(&xlated_int_pnt, int_pnt, p0);
1358
1359         if (IS_VEC_NULL(&norm)) {
1360                 *nearest_point = *int_pnt;
1361                 return 9999.9f;
1362         }
1363
1364         mag = vm_vec_normalize(&norm);                  //      Normalize vector so we don't have to divide by dot product.
1365         
1366         if (mag < 0.01f) {
1367                 *nearest_point = *int_pnt;
1368                 return 9999.9f;
1369                 // Warning(LOCATION, "Very small magnitude in find_nearest_point_on_line.\n");
1370         }
1371
1372         vm_vec_outer_product(&mat, &norm);
1373
1374         vm_vec_rotate(&projected_point, &xlated_int_pnt, &mat);
1375         vm_vec_add(nearest_point, &projected_point, p0);
1376
1377         dot = vm_vec_dot(&norm, &projected_point);
1378
1379         return dot/mag;
1380 }
1381
1382 //make sure matrix is orthogonal
1383 //computes a matrix from one or more vectors. The forward vector is required,
1384 //with the other two being optional.  If both up & right vectors are passed,
1385 //the up vector is used.  If only the forward vector is passed, a bank of
1386 //zero is assumed
1387 //returns ptr to matrix
1388 void vm_orthogonalize_matrix(matrix *m_src)
1389 {
1390         float umag, rmag;
1391         matrix tempm;
1392         matrix * m = &tempm;
1393
1394         if (vm_vec_copy_normalize(&m->fvec,&m_src->fvec) == 0.0f) {
1395                 Error( LOCATION, "forward vec should not be zero-length" );
1396         }
1397
1398         umag = vm_vec_mag(&m_src->uvec);
1399         rmag = vm_vec_mag(&m_src->rvec);
1400         if (umag <= 0.0f) {  // no up vector to use..
1401                 if (rmag <= 0.0f) {  // no right vector either, so make something up
1402                         if (!m->fvec.x && !m->fvec.z && m->fvec.y)  // vertical vector
1403                                 vm_vec_make(&m->uvec, 0.0f, 0.0f, 1.0f);
1404                         else
1405                                 vm_vec_make(&m->uvec, 0.0f, 1.0f, 0.0f);
1406
1407                 } else {  // use the right vector to figure up vector
1408                         vm_vec_crossprod(&m->uvec, &m->fvec, &m_src->rvec);
1409                         if (vm_vec_normalize(&m->uvec) == 0.0f)
1410                                 Error( LOCATION, "Bad vector!" );
1411                 }
1412
1413         } else {  // use source up vector
1414                 vm_vec_copy_normalize(&m->uvec, &m_src->uvec);
1415         }
1416
1417         // use forward and up vectors as good vectors to calculate right vector
1418         vm_vec_crossprod(&m->rvec, &m->uvec, &m->fvec);
1419                 
1420         //normalize new perpendicular vector
1421         if (vm_vec_normalize(&m->rvec) == 0.0f)
1422                 Error( LOCATION, "Bad vector!" );
1423
1424         //now recompute up vector, in case it wasn't entirely perpendiclar
1425         vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec);
1426         *m_src = tempm;
1427 }
1428
1429 // like vm_orthogonalize_matrix(), except that zero vectors can exist within the
1430 // matrix without causing problems.  Valid vectors will be created where needed.
1431 void vm_fix_matrix(matrix *m)
1432 {
1433         float fmag, umag, rmag;
1434
1435         fmag = vm_vec_mag(&m->fvec);
1436         umag = vm_vec_mag(&m->uvec);
1437         rmag = vm_vec_mag(&m->rvec);
1438         if (fmag <= 0.0f) {
1439                 if ((umag > 0.0f) && (rmag > 0.0f) && !vm_test_parallel(&m->uvec, &m->rvec)) {
1440                         vm_vec_crossprod(&m->fvec, &m->uvec, &m->rvec);
1441                         vm_vec_normalize(&m->fvec);
1442
1443                 } else if (umag > 0.0f) {
1444                         if (!m->uvec.x && !m->uvec.y && m->uvec.z)  // z vector
1445                                 vm_vec_make(&m->fvec, 1.0f, 0.0f, 0.0f);
1446                         else
1447                                 vm_vec_make(&m->fvec, 0.0f, 0.0f, 1.0f);
1448                 }
1449
1450         } else
1451                 vm_vec_normalize(&m->fvec);
1452
1453         // we now have a valid and normalized forward vector
1454
1455         if ((umag <= 0.0f) || vm_test_parallel(&m->fvec, &m->uvec)) {  // no up vector to use..
1456                 if ((rmag <= 0.0f) || vm_test_parallel(&m->fvec, &m->rvec)) {  // no right vector either, so make something up
1457                         if (!m->fvec.x && m->fvec.y && !m->fvec.z)  // vertical vector
1458                                 vm_vec_make(&m->uvec, 0.0f, 0.0f, -1.0f);
1459                         else
1460                                 vm_vec_make(&m->uvec, 0.0f, 1.0f, 0.0f);
1461
1462                 } else {  // use the right vector to figure up vector
1463                         vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec);
1464                         vm_vec_normalize(&m->uvec);
1465                 }
1466
1467         } else
1468                 vm_vec_normalize(&m->uvec);
1469
1470         // we now have both valid and normalized forward and up vectors
1471
1472         vm_vec_crossprod(&m->rvec, &m->uvec, &m->fvec);
1473                 
1474         //normalize new perpendicular vector
1475         vm_vec_normalize(&m->rvec);
1476
1477         //now recompute up vector, in case it wasn't entirely perpendiclar
1478         vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec);
1479 }
1480
1481 //Rotates the orient matrix by the angles in tangles and then
1482 //makes sure that the matrix is orthogonal.
1483 void vm_rotate_matrix_by_angles( matrix *orient, angles *tangles )
1484 {
1485         matrix  rotmat,new_orient;
1486         vm_angles_2_matrix(&rotmat,tangles);
1487         vm_matrix_x_matrix(&new_orient,orient,&rotmat);
1488         *orient = new_orient;
1489         vm_orthogonalize_matrix(orient);
1490 }
1491
1492 //      dir must be normalized!
1493 float vm_vec_dot_to_point(vector *dir, vector *p1, vector *p2)
1494 {
1495         vector  tvec;
1496
1497         vm_vec_sub(&tvec, p2, p1);
1498         vm_vec_normalize(&tvec);
1499
1500         return vm_vec_dot(dir, &tvec);
1501
1502 }
1503
1504 /////////////////////////////////////////////////////////
1505 //      Given a plane and a point, return the point on the plane closest the the point.
1506 //      Result returned in q.
1507 void compute_point_on_plane(vector *q, plane *planep, vector *p)
1508 {
1509         float   k, tv;
1510         vector  normal;
1511
1512         normal.x = planep->A;
1513         normal.y = planep->B;
1514         normal.z = planep->C;
1515
1516         k = (planep->D + vm_vec_dot(&normal, p)) / vm_vec_dot(&normal, &normal);
1517
1518         vm_vec_scale_add(q, p, &normal, -k);
1519
1520         tv = planep->A * q->x + planep->B * q->y + planep->C * q->z + planep->D;
1521 }
1522
1523
1524 //      Generate a fairly random vector that's fairly near normalized.
1525 void vm_vec_rand_vec_quick(vector *rvec)
1526 {
1527         rvec->x = (frand() - 0.5f) * 2;
1528         rvec->y = (frand() - 0.5f) * 2;
1529         rvec->z = (frand() - 0.5f) * 2;
1530
1531         if (IS_VEC_NULL(rvec))
1532                 rvec->x = 1.0f;
1533
1534         vm_vec_normalize_quick(rvec);
1535 }
1536
1537 // Given an point "in" rotate it by "angle" around an
1538 // arbritary line defined by a point on the line "line_point" 
1539 // and the normalized line direction, "line_dir"
1540 // Returns the rotated point in "out".
1541 void vm_rot_point_around_line(vector *out, vector *in, float angle, vector *line_point, vector *line_dir)
1542 {
1543         vector tmp, tmp1;
1544         matrix m, r, im;
1545         angles ta;
1546
1547         vm_vector_2_matrix_norm(&m, line_dir, NULL, NULL );
1548         vm_copy_transpose_matrix(&im,&m);
1549
1550         ta.p = ta.h = 0.0f;
1551         ta.b = angle;
1552         vm_angles_2_matrix(&r,&ta);
1553
1554         vm_vec_sub( &tmp, in, line_point );             // move relative to a point on line
1555         vm_vec_rotate( &tmp1, &tmp, &m);                        // rotate into line's base
1556         vm_vec_rotate( &tmp, &tmp1, &r);                        // rotate around Z
1557         vm_vec_rotate( &tmp1, &tmp, &im);               // unrotate out of line's base
1558         vm_vec_add( out, &tmp1, line_point );   // move back to world coordinates
1559 }
1560
1561 // Given two position vectors, return 0 if the same, else non-zero.
1562 int vm_vec_cmp( vector * a, vector * b )
1563 {
1564         float diff = vm_vec_dist(a,b);
1565 //mprintf(( "Diff=%.32f\n", diff ));
1566         if ( diff > 0.005f )
1567                 return 1;
1568         else
1569                 return 0;
1570 }
1571
1572 // Given two orientation matrices, return 0 if the same, else non-zero.
1573 int vm_matrix_cmp( matrix * a, matrix * b )
1574 {
1575         float tmp1,tmp2,tmp3;
1576         tmp1 = (float)fl_abs(vm_vec_dot( &a->uvec, &b->uvec ) - 1.0f);
1577         tmp2 = (float)fl_abs(vm_vec_dot( &a->fvec, &b->fvec ) - 1.0f);
1578         tmp3 = (float)fl_abs(vm_vec_dot( &a->rvec, &b->rvec ) - 1.0f);
1579 //      mprintf(( "Mat=%.16f, %.16f, %.16f\n", tmp1, tmp2, tmp3 ));
1580          
1581         if ( tmp1 > 0.0000005f ) return 1;
1582         if ( tmp2 > 0.0000005f ) return 1;
1583         if ( tmp3 > 0.0000005f ) return 1;
1584         return 0;
1585 }
1586
1587
1588 // Moves angle 'h' towards 'desired_angle', taking the shortest
1589 // route possible.   It will move a maximum of 'step_size' radians
1590 // each call.   All angles in radians.
1591 void vm_interp_angle( float *h, float desired_angle, float step_size )
1592 {
1593         float delta;
1594
1595         if ( desired_angle < 0.0f ) desired_angle += PI2;
1596         if ( desired_angle > PI2 ) desired_angle -= PI2;
1597
1598         delta = desired_angle - *h;
1599
1600         if ( fl_abs(delta) > PI )       {
1601                 // Go the other way, since it will be shorter.
1602                 if ( delta > 0.0f )     {
1603                         delta = delta - PI2;
1604                 } else {
1605                         delta = PI2 - delta;
1606                 }
1607         }
1608
1609         if ( delta > step_size )
1610                 *h += step_size;
1611         else if ( delta < -step_size )
1612                 *h -= step_size;
1613         else
1614                 *h = desired_angle;
1615
1616         // If we wrap outside of 0 to 2*PI, then put the
1617         // angle back in the range 0 to 2*PI.
1618         if ( *h > PI2 ) *h -= PI2;
1619         if ( *h < 0.0f ) *h += PI2;
1620 }
1621
1622 // check a matrix for zero rows and columns
1623 int vm_check_matrix_for_zeros(matrix *m)
1624 {
1625         if (!m->fvec.x && !m->fvec.y && !m->fvec.z)
1626                 return 1;
1627         if (!m->rvec.x && !m->rvec.y && !m->rvec.z)
1628                 return 1;
1629         if (!m->uvec.x && !m->uvec.y && !m->uvec.z)
1630                 return 1;
1631
1632         if (!m->fvec.x && !m->rvec.x && !m->uvec.x)
1633                 return 1;
1634         if (!m->fvec.y && !m->rvec.y && !m->uvec.y)
1635                 return 1;
1636         if (!m->fvec.z && !m->rvec.z && !m->uvec.z)
1637                 return 1;
1638
1639         return 0;
1640 }
1641
1642 // see if two vectors are the same
1643 int vm_vec_same(vector *v1, vector *v2)
1644 {
1645         if ( v1->x == v2->x && v1->y == v2->y && v1->z == v2->z )
1646                 return 1;
1647
1648         return 0;
1649 }
1650
1651
1652 // --------------------------------------------------------------------------------------
1653
1654 void vm_quaternion_rotate(matrix *M, float theta, vector *u)
1655 //  given an arbitrary rotation axis and rotation angle, function generates the
1656 //  corresponding rotation matrix
1657 //
1658 //  M is the return rotation matrix  theta is the angle of rotation 
1659 //  u is the direction of the axis.
1660 //  this is adapted from Computer Graphics (Hearn and Bker 2nd ed.) p. 420
1661 //
1662 {
1663
1664         float a,b,c, s;
1665
1666         a = (float) (u->x * sin(theta * 0.5f));
1667         b = (float) (u->y * sin(theta * 0.5f));
1668         c = (float) (u->z * sin(theta * 0.5f));
1669         s = (float) cos(theta/2.0);
1670
1671 // 1st ROW vector
1672         M->rvec.x = 1.0f - 2.0f*b*b - 2.0f*c*c;
1673         M->rvec.y = 2.0f*a*b + 2.0f*s*c;
1674         M->rvec.z = 2.0f*a*c - 2.0f*s*b;
1675 // 2nd ROW vector
1676         M->uvec.x = 2.0f*a*b - 2.0f*s*c;
1677         M->uvec.y = 1.0f - 2.0f*a*a - 2.0f*c*c;
1678         M->uvec.z = 2.0f*b*c + 2.0f*s*a;
1679 // 3rd ROW vector
1680         M->fvec.x = 2.0f*a*c + 2.0f*s*b;
1681         M->fvec.y = 2.0f*b*c - 2.0f*s*a;
1682         M->fvec.z = 1.0f - 2.0f*a*a - 2.0f*b*b;
1683 }
1684
1685 // --------------------------------------------------------------------------------------
1686 // function finds the rotation matrix about the z axis for a given rotation angle (in radians)
1687 // this is an optimized version vm_quaternion_rotate
1688 //
1689 //              inputs: m                       =>              point to resultant rotation matrix
1690 //                                      angle           =>              rotation angle about z axis (in radians)
1691 //
1692 void rotate_z ( matrix *m, float theta )
1693 {
1694         m->rvec.x = (float) cos (theta);
1695         m->rvec.y = (float) sin (theta);
1696         m->rvec.z = 0.0f;
1697
1698         m->uvec.x = -m->rvec.y;
1699         m->uvec.y =  m->rvec.x;
1700         m->uvec.z = 0.0f;
1701
1702         m->fvec.x = 0.0f;
1703         m->fvec.y = 0.0f;
1704         m->fvec.z = 1.0f;
1705 }
1706
1707
1708 // --------------------------------------------------------------------------------------
1709
1710 //void vm_matrix_to_rot_axis_and_angle(matrix *m, float *theta, vector *rot_axis)
1711 // Converts a matrix into a rotation axis and an angle around that axis
1712 // Note for angle is very near 0, returns 0 with axis of (1,0,0)
1713 // For angles near PI, returns PI with correct axis
1714 //
1715 // rot_axis - the resultant axis of rotation
1716 // theta - the resultatn rotation around the axis
1717 // m - the initial matrix
1718 void vm_matrix_to_rot_axis_and_angle(matrix *m, float *theta, vector *rot_axis)
1719 {
1720         float trace = m->a2d[0][0] + m->a2d[1][1] + m->a2d[2][2];
1721         float cos_theta = 0.5f * (trace - 1.0f);
1722
1723         if (cos_theta > 0.999999875f) { // angle is less than 1 milirad (0.057 degrees)
1724                 *theta = 0.0f;
1725
1726                 vm_vec_make(rot_axis, 1.0f, 0.0f, 0.0f);
1727         } else if (cos_theta > -0.999999875f) { // angle is within limits between 0 and PI
1728                 *theta = float(acos(cos_theta));
1729                 Assert(!_isnan(*theta));
1730
1731                 rot_axis->x = (m->uvec.z - m->fvec.y);
1732                 rot_axis->y = (m->fvec.x - m->rvec.z);
1733                 rot_axis->z = (m->rvec.y - m->uvec.x);
1734                 vm_vec_normalize(rot_axis);
1735         } else { // angle is PI within limits
1736                 *theta = PI;
1737
1738                 // find index of largest diagonal term
1739                 int largest_diagonal_index = 0;
1740
1741                 if (m->a2d[1][1] > m->a2d[0][0]) {
1742                         largest_diagonal_index = 1;
1743                 }
1744                 if (m->a2d[2][2] > m->a2d[largest_diagonal_index][largest_diagonal_index]) {
1745                         largest_diagonal_index = 2;
1746                 }
1747
1748                 switch (largest_diagonal_index) {
1749                 case 0:
1750                         float ix;
1751                         ix = 1.0f / rot_axis->x;
1752
1753                         rot_axis->x = fl_sqrt(m->a2d[0][0] + 1.0f);
1754                         rot_axis->y = m->a2d[0][1] * ix;
1755                         rot_axis->z = m->a2d[0][2] * ix;
1756                         vm_vec_normalize(rot_axis);
1757                         break;
1758
1759                 case 1:
1760                         float iy;
1761                         iy = 1.0f / rot_axis->y;
1762
1763                         rot_axis->y = fl_sqrt(m->a2d[1][1] + 1.0f);
1764                         rot_axis->x = m->a2d[1][0] * iy;
1765                         rot_axis->z = m->a2d[1][2] * iy;
1766                         vm_vec_normalize(rot_axis);
1767                         break;
1768
1769                 case 2:
1770                         float iz;
1771                         iz = 1.0f / rot_axis->z;
1772
1773                         rot_axis->z = fl_sqrt(m->a2d[2][2] + 1.0f);
1774                         rot_axis->x = m->a2d[2][0] * iz;
1775                         rot_axis->y = m->a2d[2][1] * iz;
1776                         break;
1777
1778                 default:
1779                         Int3();  // this should never happen
1780                         break;
1781                 }
1782
1783                 // normalize rotation axis
1784                 vm_vec_normalize(rot_axis);
1785         }
1786 }
1787
1788
1789 // --------------------------------------------------------------------------------------
1790 // This routine determines the resultant angular displacement and angular velocity in trying to reach a goal
1791 // given an angular velocity APPROACHing a goal.  It uses maximal acceleration to a point (called peak), then maximal
1792 // deceleration to arrive at the goal with zero angular velocity.  This can occasionally cause overshoot.  
1793 // w_in                 > 0
1794 // w_max                        > 0
1795 // theta_goal   > 0
1796 // aa                           > 0 
1797 // returns delta_theta
1798 float away(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot);
1799 float approach(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot)
1800 {
1801         float delta_theta;              // amount rotated during time delta_t
1802         Assert(w_in >= 0);
1803         Assert(theta_goal > 0);
1804         float effective_aa;
1805
1806         if (aa == 0) {
1807                 *w_out = w_in;
1808                 delta_theta = w_in*delta_t;
1809                 return delta_theta;
1810         }
1811
1812         if (no_overshoot && (w_in*w_in > 2.0f*1.05f*aa*theta_goal)) {
1813                 w_in = fl_sqrt(2.0f*aa*theta_goal);
1814         }
1815
1816         if (w_in*w_in > 2.0f*1.05f*aa*theta_goal) {             // overshoot condition
1817                 effective_aa = 1.05f*aa;
1818                 delta_theta = w_in*delta_t - 0.5f*effective_aa*delta_t*delta_t;
1819
1820                 if (delta_theta > theta_goal) { // pass goal during this frame
1821                         float t_goal = (-w_in + fl_sqrt(w_in*w_in +2.0f*effective_aa*theta_goal)) / effective_aa;
1822                         // get time to theta_goal and away
1823                         Assert(t_goal < delta_t);
1824                         w_in -= effective_aa*t_goal;
1825                         delta_theta = w_in*t_goal + 0.5f*effective_aa*t_goal*t_goal;
1826                         delta_theta -= away(-w_in, w_max, 0.0f, aa, delta_t - t_goal, w_out, no_overshoot);
1827                         *w_out = -*w_out;
1828                         return delta_theta;
1829                 } else {        
1830                         if (delta_theta < 0) {
1831                                 // pass goal and return this frame
1832                                 *w_out = 0.0f;
1833                                 return theta_goal;
1834                         } else {
1835                                 // do not pass goal this frame
1836                                 *w_out = w_in - effective_aa*delta_t;
1837                                 return delta_theta;
1838                         }
1839                 }
1840         } else if (w_in*w_in < 2.0f*0.95f*aa*theta_goal) {      // undershoot condition
1841                 // find peak angular velocity
1842                 float wp_sqr = fl_abs(aa*theta_goal + 0.5f*w_in*w_in);
1843                 Assert(wp_sqr >= 0);
1844
1845                 if (wp_sqr > w_max*w_max) {
1846                         float time_to_w_max = (w_max - w_in) / aa;
1847                         if (time_to_w_max < 0) {
1848                                 // speed already too high
1849                                 // TODO: consider possible ramp down to below w_max
1850                                 *w_out = w_in - aa*delta_t;
1851                                 if (*w_out < 0) {
1852                                         *w_out = 0.0f;
1853                                 }
1854
1855                                 delta_theta = 0.5f*(w_in + *w_out)*delta_t;
1856                                 return delta_theta;
1857                         } else if (time_to_w_max > delta_t) {
1858                                 // does not reach w_max this frame
1859                                 *w_out = w_in + aa*delta_t;
1860                                 delta_theta = 0.5f*(w_in + *w_out)*delta_t;
1861                                 return delta_theta;
1862                         } else {
1863                                 // reaches w_max this frame
1864                                 // TODO: consider when to ramp down from w_max
1865                                 *w_out = w_max;
1866                                 delta_theta = 0.5f*(w_in + *w_out)*delta_t;
1867                                 return delta_theta;
1868                         }
1869                 } else {        // wp < w_max
1870                         if (wp_sqr > (w_in + aa*delta_t)*(w_in + aa*delta_t)) {
1871                                 // does not reach wp this frame
1872                                 *w_out = w_in + aa*delta_t;
1873                                 delta_theta = 0.5f*(w_in + *w_out)*delta_t;
1874                                 return delta_theta;
1875                         } else {
1876                                 // reaches wp this frame
1877                                 float wp = fl_sqrt(wp_sqr);
1878                                 float time_to_wp = (wp - w_in) / aa;
1879                                 Assert(time_to_wp > 0);
1880
1881                                 // accel
1882                                 *w_out = wp;
1883                                 delta_theta = 0.5f*(w_in + *w_out)*time_to_wp;
1884
1885                                 // decel
1886                                 float time_remaining = delta_t - time_to_wp;
1887                                 *w_out -= aa*time_remaining;
1888                                 if (*w_out < 0) { // reached goal
1889                                         *w_out = 0.0f;
1890                                         delta_theta = theta_goal;
1891                                         return delta_theta;
1892                                 }
1893                                 delta_theta += 0.5f*(wp + *w_out)*time_remaining;
1894                                 return delta_theta;
1895                         }
1896                 }
1897         } else {                                                                                                                // on target
1898                 // reach goal this frame
1899                 if (w_in - aa*delta_t < 0) {
1900                         // reach goal this frame
1901                         *w_out = 0.0f;
1902                         return theta_goal;
1903                 } else {
1904                         // move toward goal
1905                         *w_out = w_in - aa*delta_t;
1906                         Assert(*w_out >= 0);
1907                         delta_theta = 0.5f*(w_in + *w_out)*delta_t;
1908                         return delta_theta;
1909                 }
1910         }
1911 }
1912
1913
1914 // --------------------------------------------------------------------------------------
1915
1916 // This routine determines the resultant angular displacement and angular velocity in trying to reach a goal
1917 // given an angular velocity AWAY from a goal.  It uses maximal acceleration to a point (called peak), then maximal
1918 // deceleration to arrive at the goal with zero angular acceleration.  
1919 // w_in                 < 0
1920 // w_max                        > 0
1921 // theta_goal   > 0
1922 // aa                           > 0 
1923 // returns angle rotated this frame
1924 float away(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot)
1925
1926 {
1927         float delta_theta;// amount rotated during time
1928         float t0;                       // time to velocity is 0
1929         float t_excess; // time remaining in interval after velocity is 0
1930
1931         Assert(theta_goal >=0);
1932         Assert(w_in <= 0);
1933
1934         if ((-w_in < 1e-5) && (theta_goal < 1e-5)) {
1935                 *w_out = 0.0f;
1936                 return theta_goal;
1937         }
1938
1939         if (aa == 0) {
1940                 *w_out = w_in;
1941                 delta_theta = w_in*delta_t;
1942                 return delta_theta;
1943         }
1944
1945         t0 = -w_in / aa;
1946
1947         if (t0 > delta_t)       {       // no reversal in this time interval
1948                 *w_out = w_in + aa * delta_t;
1949                 delta_theta =  (w_in + *w_out) / 2.0f * delta_t;
1950                 return delta_theta;
1951         }
1952
1953         // use time remaining after v = 0
1954         delta_theta = 0.5f*w_in*t0;
1955         theta_goal -= delta_theta;              // delta_theta is *negative*
1956         t_excess = delta_t - t0;
1957         delta_theta += approach(0.0f, w_max, theta_goal, aa, t_excess, w_out, no_overshoot);
1958         return delta_theta;
1959 }
1960
1961 // --------------------------------------------------------------------------------------
1962
1963 void vm_matrix_interpolate(matrix *goal_orient, matrix *curr_orient, vector *w_in, float delta_t, 
1964                                                                 matrix *next_orient, vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot)
1965 {
1966         matrix rot_matrix;              // rotation matrix from curr_orient to goal_orient
1967         matrix Mtemp1;                          // temp matrix
1968         vector rot_axis;                        // vector indicating direction of rotation axis
1969         vector theta_goal;              // desired angular position at the end of the time interval
1970         vector theta_end;                       // actual angular position at the end of the time interval
1971         float theta;                            // magnitude of rotation about the rotation axis
1972
1973         //      FIND ROTATION NEEDED FOR GOAL
1974         // goal_orient = R curr_orient,  so R = goal_orient curr_orient^-1
1975         vm_copy_transpose_matrix(&Mtemp1, curr_orient);                         // Mtemp1 = curr ^-1
1976         vm_matrix_x_matrix(&rot_matrix, &Mtemp1, goal_orient);  // R = goal * Mtemp1
1977         vm_orthogonalize_matrix(&rot_matrix);
1978         vm_matrix_to_rot_axis_and_angle(&rot_matrix, &theta, &rot_axis);                // determines angle and rotation axis from curr to goal
1979
1980         // find theta to goal
1981         vm_vec_copy_scale(&theta_goal, &rot_axis, theta);
1982
1983         if (theta < SMALL_NUM) {
1984                 *next_orient = *goal_orient;
1985                 vm_vec_zero(w_out);
1986                 return;
1987         }
1988
1989         theta_end = vmd_zero_vector;
1990         float delta_theta;
1991
1992         // find rotation about x
1993         if (theta_goal.x > 0) {
1994                 if (w_in->x >= 0) {
1995                         delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
1996                         theta_end.x = delta_theta;
1997                 } else { // w_in->x < 0
1998                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
1999                         theta_end.x = delta_theta;
2000                 }
2001         } else if (theta_goal.x < 0) {
2002                 if (w_in->x <= 0) {
2003                         delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2004                         theta_end.x = -delta_theta;
2005                         w_out->x = -w_out->x;
2006                 } else { // w_in->x > 0
2007                         delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2008                         theta_end.x = -delta_theta;
2009                         w_out->x = -w_out->x;
2010                 }
2011         } else { // theta_goal == 0
2012                 if (w_in->x < 0) {
2013                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2014                         theta_end.x = delta_theta;
2015                 } else {
2016                         delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2017                         theta_end.x = -delta_theta;
2018                         w_out->x = -w_out->x;
2019                 }
2020         }
2021
2022
2023         // find rotation about y
2024         if (theta_goal.y > 0) {
2025                 if (w_in->y >= 0) {
2026                         delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2027                         theta_end.y = delta_theta;
2028                 } else { // w_in->y < 0
2029                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2030                         theta_end.y = delta_theta;
2031                 }
2032         } else if (theta_goal.y < 0) {
2033                 if (w_in->y <= 0) {
2034                         delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2035                         theta_end.y = -delta_theta;
2036                         w_out->y = -w_out->y;
2037                 } else { // w_in->y > 0
2038                         delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2039                         theta_end.y = -delta_theta;
2040                         w_out->y = -w_out->y;
2041                 }
2042         } else { // theta_goal == 0
2043                 if (w_in->y < 0) {
2044                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2045                         theta_end.y = delta_theta;
2046                 } else {
2047                         delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2048                         theta_end.y = -delta_theta;
2049                         w_out->y = -w_out->y;
2050                 }
2051         }
2052
2053         // find rotation about z
2054         if (theta_goal.z > 0) {
2055                 if (w_in->z >= 0) {
2056                         delta_theta = approach(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2057                         theta_end.z = delta_theta;
2058                 } else { // w_in->z < 0
2059                         delta_theta = away(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2060                         theta_end.z = delta_theta;
2061                 }
2062         } else if (theta_goal.z < 0) {
2063                 if (w_in->z <= 0) {
2064                         delta_theta = approach(-w_in->z, vel_limit->z, -theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2065                         theta_end.z = -delta_theta;
2066                         w_out->z = -w_out->z;
2067                 } else { // w_in->z > 0
2068                         delta_theta = away(-w_in->z, vel_limit->z, -theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2069                         theta_end.z = -delta_theta;
2070                         w_out->z = -w_out->z;
2071                 }
2072         } else { // theta_goal == 0
2073                 if (w_in->z < 0) {
2074                         delta_theta = away(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2075                         theta_end.z = delta_theta;
2076                 } else {
2077                         delta_theta = away(-w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2078                         theta_end.z = -delta_theta;
2079                         w_out->z = -w_out->z;
2080                 }
2081         }
2082
2083         // the amount of rotation about each axis is determined in 
2084         // functions approach and away.  first find the magnitude               
2085         // of the rotation and then normalize the axis
2086         rot_axis = theta_end;
2087         Assert(is_valid_vec(&rot_axis));
2088         Assert(vm_vec_mag(&rot_axis) > 0);
2089
2090         //      normalize rotation axis and determine total rotation angle
2091         theta = vm_vec_normalize(&rot_axis);
2092
2093         // arrived at goal?
2094         if (theta_end.x == theta_goal.x && theta_end.y == theta_goal.y && theta_end.z == theta_goal.z) {
2095                 *next_orient = *goal_orient;
2096         } else {
2097         // otherwise rotate to better position
2098                 vm_quaternion_rotate(&Mtemp1, theta, &rot_axis);
2099                 Assert(is_valid_matrix(&Mtemp1));
2100                 vm_matrix_x_matrix(next_orient, curr_orient, &Mtemp1);
2101                 vm_orthogonalize_matrix(next_orient);
2102         }
2103 }       // end matrix_interpolate
2104
2105
2106 // --------------------------------------------------------------------------------------
2107
2108
2109 void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vector *acc_max, vector *w_max)
2110 {
2111         matrix temp, rot_matrix;
2112         float theta;
2113         vector rot_axis;
2114         vector angle;
2115
2116         // determine the necessary rotation matrix
2117         vm_copy_transpose(&temp, start_camera);
2118         vm_matrix_x_matrix(&rot_matrix, &temp, end_camera);
2119         vm_orthogonalize_matrix(&rot_matrix);
2120
2121         // determine the rotation axis and angle
2122         vm_matrix_to_rot_axis_and_angle(&rot_matrix, &theta, &rot_axis);
2123
2124         // find the rotation about each axis
2125         angle.x = theta * rot_axis.x;
2126         angle.y = theta * rot_axis.y;
2127         angle.z = theta * rot_axis.z;
2128
2129         // allow for 0 time input
2130         if (time <= 1e-5f) {
2131                 vm_vec_make(acc_max, 0.0f, 0.0f, 0.0f);
2132                 vm_vec_make(w_max, 0.0f, 0.0f, 0.0f);
2133         } else {
2134
2135                 // find acceleration limit using  (theta/2) takes (time/2)
2136                 // and using const accel  theta = 1/2 acc * time^2
2137                 acc_max->x = 4.0f * (float)fl_abs(angle.x) / (time * time);
2138                 acc_max->y = 4.0f * (float)fl_abs(angle.y) / (time * time);
2139                 acc_max->z = 4.0f * (float)fl_abs(angle.z) / (time * time);
2140
2141                 // find angular velocity limits
2142                 // w_max = acc_max * time / 2
2143                 w_max->x = acc_max->x * time / 2.0f;
2144                 w_max->y = acc_max->y * time / 2.0f;
2145                 w_max->z = acc_max->z * time / 2.0f;
2146         }
2147 }
2148
2149 // ---------------------------------------------------------------------------------------------
2150 //
2151 //              inputs:         goal_orient     =>              goal orientation matrix
2152 //                                              orient          =>              current orientation matrix (with current forward vector)
2153 //                                              w_in                    =>              current input angular velocity
2154 //                                              delta_t         =>              time to move toward goal
2155 //                                              next_orient     =>              the orientation matrix at time delta_t (with current forward vector)
2156 //                                                                                              NOTE: this does not include any rotation about z (bank)
2157 //                                              w_out                   =>              the angular velocity of the ship at delta_t
2158 //                                              vel_limit       =>              maximum rotational speed
2159 //                                              acc_limit       =>              maximum rotational speed
2160 //
2161 //              function moves the forward vector toward the goal forward vector taking account of anglular
2162 //              momentum (velocity)  Attempt to try to move bank by goal delta_bank.  Rotational velocity
2163 //              on x/y is rotated with bank, giving smoother motion.
2164 void vm_fvec_matrix_interpolate(matrix *goal_orient, matrix *orient, vector *w_in, float delta_t, matrix *next_orient, 
2165                 vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot)
2166 {
2167         matrix  Mtemp1;                         // temporary matrix
2168         matrix  M_intermed;                     // intermediate matrix after xy rotation
2169         vector  local_rot_axis; // vector indicating direction of rotation axis (local coords)
2170         vector  rot_axis;                       // vector indicating direction of rotation axis (world coords)
2171         vector  theta_goal;                     // desired angular position at the end of the time interval
2172         vector  theta_end;                      // actual angular position at the end of the time interval
2173         float           theta;                          // magnitude of rotation about the rotation axis
2174         float           bank;                                   // magnitude of rotation about the forward axis
2175         int             no_bank;                                // flag set if there is no bank for the object
2176         vector  vtemp;                          // temp angular velocity before rotation about z
2177         float           z_dotprod;                      // dotprod of orient->fvec and goal_orient->fvec
2178         float           r_dotprod;                      // dotprod of orient->rvec and goal_orient->rvec
2179         float           delta_bank;
2180
2181         //      FIND XY ROTATION NEEDED FOR GOAL
2182         // rotation vector is (current fvec)  orient->fvec x goal_f
2183         // magnitude = asin ( magnitude of crossprod )
2184         vm_vec_crossprod ( &rot_axis, &orient->fvec, &goal_orient->fvec );
2185
2186         float t = vm_vec_mag(&rot_axis);
2187         if (t > 1.0f)
2188                 t = 1.0f;
2189
2190         z_dotprod = vm_vec_dotprod ( &orient->fvec, &goal_orient->fvec );
2191
2192         if ( t < SMALLER_NUM )  {
2193                 if ( z_dotprod > 0.0f )
2194                         theta = 0.0f;
2195                 else  {  // the forward vector is pointing exactly opposite of goal
2196                                         // arbitrarily choose the x axis to rotate around until t becomes large enough
2197                         theta = PI;
2198                         rot_axis = orient->rvec;
2199                 }
2200         } else {
2201                 theta = (float) asin ( t );
2202                 vm_vec_scale ( &rot_axis, 1/t );
2203                 if ( z_dotprod < 0.0f )
2204                         theta = PI - theta;
2205         }
2206
2207         // rotate rot_axis into ship reference frame
2208         vm_vec_rotate ( &local_rot_axis, &rot_axis, orient );
2209
2210         // find theta to goal
2211         vm_vec_copy_scale(&theta_goal, &local_rot_axis, theta);
2212         Assert ( fl_abs (theta_goal.z) < 0.001f );              // check for proper rotation
2213
2214         theta_end = vmd_zero_vector;
2215         float delta_theta;
2216
2217         // find rotation about x
2218         if (theta_goal.x > 0) {
2219                 if (w_in->x >= 0) {
2220                         delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2221                         theta_end.x = delta_theta;
2222                 } else { // w_in->x < 0
2223                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2224                         theta_end.x = delta_theta;
2225                 }
2226         } else if (theta_goal.x < 0) {
2227                 if (w_in->x <= 0) {
2228                         delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2229                         theta_end.x = -delta_theta;
2230                         w_out->x = -w_out->x;
2231                 } else { // w_in->x > 0
2232                         delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2233                         theta_end.x = -delta_theta;
2234                         w_out->x = -w_out->x;
2235                 }
2236         } else { // theta_goal == 0
2237                 if (w_in->x < 0) {
2238                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2239                         theta_end.x = delta_theta;
2240                 } else {
2241                         delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2242                         theta_end.x = -delta_theta;
2243                         w_out->x = -w_out->x;
2244                 }
2245         }
2246
2247         // find rotation about y
2248         if (theta_goal.y > 0) {
2249                 if (w_in->y >= 0) {
2250                         delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2251                         theta_end.y = delta_theta;
2252                 } else { // w_in->y < 0
2253                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2254                         theta_end.y = delta_theta;
2255                 }
2256         } else if (theta_goal.y < 0) {
2257                 if (w_in->y <= 0) {
2258                         delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2259                         theta_end.y = -delta_theta;
2260                         w_out->y = -w_out->y;
2261                 } else { // w_in->y > 0
2262                         delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2263                         theta_end.y = -delta_theta;
2264                         w_out->y = -w_out->y;
2265                 }
2266         } else { // theta_goal == 0
2267                 if (w_in->y < 0) {
2268                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2269                         theta_end.y = delta_theta;
2270                 } else {
2271                         delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2272                         theta_end.y = -delta_theta;
2273                         w_out->y = -w_out->y;
2274                 }
2275         }
2276
2277         // FIND Z ROTATON MATRIX
2278         theta_end.z = 0.0f;
2279         rot_axis = theta_end;
2280         Assert(is_valid_vec(&rot_axis));
2281
2282         //      normalize rotation axis and determine total rotation angle
2283         theta = vm_vec_mag(&rot_axis);
2284         if (theta < SMALL_NUM)  {
2285                 theta = 0.0f;
2286                 M_intermed = *orient;
2287         } else {
2288                 vm_vec_scale ( &rot_axis, 1/theta );            
2289                 vm_quaternion_rotate ( &Mtemp1, theta, &rot_axis );
2290                 Assert(is_valid_matrix(&Mtemp1));
2291                 vm_matrix_x_matrix ( &M_intermed, orient, &Mtemp1 );
2292                 Assert(is_valid_matrix(&M_intermed));
2293         }
2294
2295
2296         // FIND ROTATION ABOUT Z (IF ANY)
2297         // no rotation if delta_bank and w_in both 0 or rotational acc in forward is 0
2298         no_bank = ( acc_limit->z == 0.0f && vel_limit->z == 0.0f );
2299
2300         if ( no_bank )  {       // no rotation on z, so we're done (no rotation of w)
2301                 *next_orient = M_intermed;
2302                 vm_orthogonalize_matrix ( next_orient );
2303                 return;
2304         } else {
2305         // calculate delta_bank using orient->rvec, goal_orient->rvec
2306         //
2307                 vm_vec_crossprod ( &rot_axis, &orient->rvec, &goal_orient->rvec );
2308
2309                 t = vm_vec_mag(&rot_axis);
2310                 if (t > 1.0f)
2311                         t = 1.0f;
2312
2313                 r_dotprod = vm_vec_dotprod ( &orient->rvec, &goal_orient->rvec );
2314
2315                 if ( t < SMALLER_NUM )  {
2316                         if ( r_dotprod > 0.0f )
2317                                 theta = 0.0f;
2318                         else  {  // the right vector is pointing exactly opposite of goal, so rotate 180 on z
2319                                 theta = PI;
2320                                 rot_axis = orient->fvec;
2321                         }
2322                 } else {
2323                         theta = (float) asin ( t );
2324                         vm_vec_scale ( &rot_axis, 1/t );
2325                         if ( z_dotprod < 0.0f )
2326                                 theta = PI - theta;
2327                 }
2328
2329                 // rotate rot_axis into ship reference frame
2330                 vm_vec_rotate ( &local_rot_axis, &rot_axis, orient );
2331
2332                 // find theta.z to goal
2333                 delta_bank = local_rot_axis.z * theta;
2334                 Assert( fl_abs (local_rot_axis.x) < 0.001f );           // check for proper rotation
2335                 bank = 0.0f;
2336
2337         // end calculate delta_bank
2338         // find rotation about z
2339         if (delta_bank > 0) {
2340                 if (w_in->z >= 0) {
2341                         delta_theta = approach(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2342                         bank = delta_theta;
2343                 } else { // w_in->z < 0
2344                         delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2345                         bank = delta_theta;
2346                 }
2347         } else if (delta_bank < 0) {
2348                 if (w_in->z <= 0) {
2349                         delta_theta = approach(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2350                         bank = -delta_theta;
2351                         w_out->z = -w_out->z;
2352                 } else { // w_in->z > 0
2353                         delta_theta = away(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2354                         bank = -delta_theta;
2355                         w_out->z = -w_out->z;
2356                 }
2357         } else { // theta_goal == 0
2358                 if (w_in->z < 0) {
2359                         delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2360                         bank = delta_theta;
2361                 } else {
2362                         delta_theta = away(-w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2363                         bank = -delta_theta;
2364                         w_out->z = -w_out->z;
2365                 }
2366         }
2367
2368                 if ( fl_abs (bank) < SMALL_NUM )
2369                 {
2370                         *next_orient = M_intermed;
2371                         vm_orthogonalize_matrix ( next_orient );
2372                 } else {
2373
2374                 rotate_z ( &Mtemp1, bank );
2375                 vtemp = *w_out;
2376                 vm_vec_rotate ( w_out, &vtemp, &Mtemp1 );
2377                 vm_matrix_x_matrix ( next_orient, &M_intermed, &Mtemp1 );
2378                 Assert(is_valid_matrix(next_orient));
2379                 vm_orthogonalize_matrix ( next_orient );
2380                 }
2381         }
2382 }       // end vm_fvec_matrix_interpolate
2383
2384
2385 // ---------------------------------------------------------------------------------------------
2386 //
2387 //              inputs:         goal_f          =>              goal forward vector
2388 //                                              orient          =>              current orientation matrix (with current forward vector)
2389 //                                              w_in                    =>              current input angular velocity
2390 //                                              delta_t         =>              time to move toward goal
2391 //                                              delta_bank      =>              desired change in bank in degrees
2392 //                                              next_orient     =>              the orientation matrix at time delta_t (with current forward vector)
2393 //                                                                                              NOTE: this does not include any rotation about z (bank)
2394 //                                              w_out                   =>              the angular velocity of the ship at delta_t
2395 //                                              vel_limit       =>              maximum rotational speed
2396 //                                              acc_limit       =>              maximum rotational speed
2397 //
2398 //              function moves the forward vector toward the goal forward vector taking account of anglular
2399 //              momentum (velocity)  Attempt to try to move bank by goal delta_bank.  Rotational velocity
2400 //              on x/y is rotated with bank, giving smoother motion.
2401 void vm_forward_interpolate(vector *goal_f, matrix *orient, vector *w_in, float delta_t, float delta_bank,
2402                 matrix *next_orient, vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot)
2403 {
2404         matrix Mtemp1;                          // temporary matrix
2405         vector local_rot_axis;  // vector indicating direction of rotation axis (local coords)
2406         vector rot_axis;                        // vector indicating direction of rotation axis (world coords)
2407         vector theta_goal;              // desired angular position at the end of the time interval
2408         vector theta_end;                       // actual angular position at the end of the time interval
2409         float theta;                            // magnitude of rotation about the rotation axis
2410         float bank;                                     // magnitude of rotation about the forward axis
2411         int no_bank;                            // flag set if there is no bank for the object
2412         vector vtemp;                           // 
2413         float z_dotprod;
2414
2415         // FIND ROTATION NEEDED FOR GOAL
2416         // rotation vector is (current fvec)  orient->fvec x goal_f
2417         // magnitude = asin ( magnitude of crossprod )
2418         vm_vec_crossprod( &rot_axis, &orient->fvec, goal_f );
2419
2420         float t = vm_vec_mag(&rot_axis);
2421         if (t > 1.0f)
2422                 t = 1.0f;
2423
2424         z_dotprod = vm_vec_dotprod( &orient->fvec, goal_f );
2425
2426         if ( t < SMALLER_NUM )  {
2427                 if ( z_dotprod > 0.0f )
2428                         theta = 0.0f;
2429                 else  {  // the forward vector is pointing exactly opposite of goal
2430                                         // arbitrarily choose the x axis to rotate around until t becomes large enough
2431                         theta = PI;
2432                         rot_axis = orient->rvec;
2433                 }
2434         } else {
2435                 theta = (float) asin( t );
2436                 vm_vec_scale ( &rot_axis, 1/t );
2437                 if ( z_dotprod < 0.0f )
2438                         theta = PI - theta;
2439         }
2440
2441         // rotate rot_axis into ship reference frame
2442         vm_vec_rotate( &local_rot_axis, &rot_axis, orient );
2443
2444         // find theta to goal
2445         vm_vec_copy_scale(&theta_goal, &local_rot_axis, theta);
2446         Assert(fl_abs(theta_goal.z) < 0.001f);          // check for proper rotation
2447
2448         theta_end = vmd_zero_vector;
2449         float delta_theta;
2450
2451         // find rotation about x
2452         if (theta_goal.x > 0) {
2453                 if (w_in->x >= 0) {
2454                         delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2455                         theta_end.x = delta_theta;
2456                 } else { // w_in->x < 0
2457                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2458                         theta_end.x = delta_theta;
2459                 }
2460         } else if (theta_goal.x < 0) {
2461                 if (w_in->x <= 0) {
2462                         delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2463                         theta_end.x = -delta_theta;
2464                         w_out->x = -w_out->x;
2465                 } else { // w_in->x > 0
2466                         delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2467                         theta_end.x = -delta_theta;
2468                         w_out->x = -w_out->x;
2469                 }
2470         } else { // theta_goal == 0
2471                 if (w_in->x < 0) {
2472                         delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2473                         theta_end.x = delta_theta;
2474                 } else {
2475                         delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot);
2476                         theta_end.x = -delta_theta;
2477                         w_out->x = -w_out->x;
2478                 }
2479         }
2480
2481         // find rotation about y
2482         if (theta_goal.y > 0) {
2483                 if (w_in->y >= 0) {
2484                         delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2485                         theta_end.y = delta_theta;
2486                 } else { // w_in->y < 0
2487                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2488                         theta_end.y = delta_theta;
2489                 }
2490         } else if (theta_goal.y < 0) {
2491                 if (w_in->y <= 0) {
2492                         delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2493                         theta_end.y = -delta_theta;
2494                         w_out->y = -w_out->y;
2495                 } else { // w_in->y > 0
2496                         delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2497                         theta_end.y = -delta_theta;
2498                         w_out->y = -w_out->y;
2499                 }
2500         } else { // theta_goal == 0
2501                 if (w_in->y < 0) {
2502                         delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2503                         theta_end.y = delta_theta;
2504                 } else {
2505                         delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot);
2506                         theta_end.y = -delta_theta;
2507                         w_out->y = -w_out->y;
2508                 }
2509         }
2510
2511         // no rotation if delta_bank and w_in both 0 or rotational acc in forward is 0
2512         no_bank = ( delta_bank == 0.0f && vel_limit->z == 0.0f && acc_limit->z == 0.0f );
2513
2514         // do rotation about z
2515         bank = 0.0f;
2516         if ( !no_bank )  {
2517                 // convert delta_bank to radians
2518                 delta_bank *= (float) CONVERT_RADIANS;
2519
2520                 // find rotation about z
2521                 if (delta_bank > 0) {
2522                         if (w_in->z >= 0) {
2523                                 delta_theta = approach(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2524                                 bank = delta_theta;
2525                         } else { // w_in->z < 0
2526                                 delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2527                                 bank = delta_theta;
2528                         }
2529                 } else if (delta_bank < 0) {
2530                         if (w_in->z <= 0) {
2531                                 delta_theta = approach(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2532                                 bank = -delta_theta;
2533                                 w_out->z = -w_out->z;
2534                         } else { // w_in->z > 0
2535                                 delta_theta = away(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2536                                 bank = -delta_theta;
2537                                 w_out->z = -w_out->z;
2538                         }
2539                 } else { // theta_goal == 0
2540                         if (w_in->z < 0) {
2541                                 delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2542                                 bank = delta_theta;
2543                         } else {
2544                                 delta_theta = away(-w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot);
2545                                 bank = -delta_theta;
2546                                 w_out->z = -w_out->z;
2547                         }
2548                 }
2549         }
2550
2551         // the amount of rotation about each axis is determined in 
2552         // functions approach and away.  first find the magnitude               
2553         // of the rotation and then normalize the axis  (ship coords)
2554         theta_end.z = bank;
2555         rot_axis = theta_end;
2556
2557         //      normalize rotation axis and determine total rotation angle
2558         theta = vm_vec_mag(&rot_axis);
2559         if ( theta > SMALL_NUM )
2560                 vm_vec_scale( &rot_axis, 1/theta );
2561
2562         if ( theta < SMALL_NUM ) {
2563                 *next_orient = *orient;
2564                 return;
2565         } else {
2566                 vm_quaternion_rotate( &Mtemp1, theta, &rot_axis );
2567                 vm_matrix_x_matrix( next_orient, orient, &Mtemp1 );
2568                 Assert(is_valid_matrix(next_orient));
2569                 vtemp = *w_out;
2570                 vm_vec_rotate( w_out, &vtemp, &Mtemp1 );
2571         }
2572 }       // end vm_forward_interpolate
2573
2574 // ------------------------------------------------------------------------------------
2575 // vm_find_bounding_sphere()
2576 //
2577 // Calculate a bounding sphere for a set of points.
2578 //
2579 //      input:  pnts                    =>              array of world positions
2580 //                              num_pnts                =>              number of points inside pnts array
2581 //                              center          =>              OUTPUT PARAMETER:       contains world pos of bounding sphere center
2582 //                              radius          =>              OUTPUT PARAMETER:       continas radius of bounding sphere
2583 //
2584 #define BIGNUMBER       100000000.0f
2585 void vm_find_bounding_sphere(vector *pnts, int num_pnts, vector *center, float *radius)
2586 {
2587         int             i;
2588         float           rad, rad_sq, xspan, yspan, zspan, maxspan;
2589         float           old_to_p, old_to_p_sq, old_to_new;      
2590         vector  diff, xmin, xmax, ymin, ymax, zmin, zmax, dia1, dia2, *p;
2591         
2592         xmin = vmd_zero_vector;
2593         ymin = vmd_zero_vector;
2594         zmin = vmd_zero_vector;
2595         xmax = vmd_zero_vector;
2596         ymax = vmd_zero_vector;
2597         zmax = vmd_zero_vector; 
2598         xmin.x = ymin.y = zmin.z = BIGNUMBER;
2599         xmax.x = ymax.y = zmax.z = -BIGNUMBER;
2600
2601         for ( i = 0; i < num_pnts; i++ ) {
2602                 p = &pnts[i];
2603                 if ( p->x < xmin.x )
2604                         xmin = *p;
2605                 if ( p->x > xmax.x )
2606                         xmax = *p;
2607                 if ( p->y < ymin.y )
2608                         ymin = *p;
2609                 if ( p->y > ymax.y )
2610                         ymax = *p;
2611                 if ( p->z < zmin.z )
2612                         zmin = *p;
2613                 if ( p->z > zmax.z )
2614                         zmax = *p;
2615         }
2616
2617         // find distance between two min and max points (squared)
2618         vm_vec_sub(&diff, &xmax, &xmin);
2619         xspan = vm_vec_mag_squared(&diff);
2620
2621         vm_vec_sub(&diff, &ymax, &ymin);
2622         yspan = vm_vec_mag_squared(&diff);
2623
2624         vm_vec_sub(&diff, &zmax, &zmin);
2625         zspan = vm_vec_mag_squared(&diff);
2626
2627         dia1 = xmin;
2628         dia2 = xmax;
2629         maxspan = xspan;
2630         if ( yspan > maxspan ) {
2631                 maxspan = yspan;
2632                 dia1 = ymin;
2633                 dia2 = ymax;
2634         }
2635         if ( zspan > maxspan ) {
2636                 maxspan = yspan;
2637                 dia1 = zmin;
2638                 dia2 = zmax;
2639         }
2640
2641         // calc inital center
2642         vm_vec_add(center, &dia1, &dia2);
2643         vm_vec_scale(center, 0.5f);
2644
2645         vm_vec_sub(&diff, &dia2, center);
2646         rad_sq = vm_vec_mag_squared(&diff);
2647         rad = fl_sqrt(rad_sq);
2648         Assert( !_isnan(rad) );
2649
2650         // second pass
2651         for ( i = 0; i < num_pnts; i++ ) {
2652                 p = &pnts[i];
2653                 vm_vec_sub(&diff, p, center);
2654                 old_to_p_sq = vm_vec_mag_squared(&diff);
2655                 if ( old_to_p_sq > rad_sq ) {
2656                         old_to_p = fl_sqrt(old_to_p_sq);
2657                         // calc radius of new sphere
2658                         rad = (rad + old_to_p) / 2.0f;
2659                         rad_sq = rad * rad;
2660                         old_to_new = old_to_p - rad;
2661                         // calc new center of sphere
2662                         center->x = (rad*center->x + old_to_new*p->x) / old_to_p;
2663                         center->y = (rad*center->y + old_to_new*p->y) / old_to_p;
2664                         center->z = (rad*center->z + old_to_new*p->z) / old_to_p;
2665                         nprintf(("Alan", "New sphere: cen,rad = %f %f %f  %f\n", center->x, center->y, center->z, rad));
2666                 }
2667         }
2668
2669         *radius = rad;
2670 }
2671
2672 // ----------------------------------------------------------------------------
2673 // vm_rotate_vec_to_body()
2674 //
2675 // rotates a vector from world coordinates to body coordinates
2676 //
2677 // inputs:      body_vec                =>              vector in body coordinates
2678 //                              world_vec       =>              vector in world coordinates
2679 //                              orient          =>              orientation matrix
2680 //
2681 vector* vm_rotate_vec_to_body(vector *body_vec, vector *world_vec, matrix *orient)
2682 {
2683         return vm_vec_unrotate(body_vec, world_vec, orient);
2684 }
2685
2686
2687 // ----------------------------------------------------------------------------
2688 // vm_rotate_vec_to_world()
2689 //
2690 // rotates a vector from body coordinates to world coordinates
2691 //
2692 //      inputs  world_vec       =>              vector in world coordinates
2693 //                              body_vec                =>              vector in body coordinates
2694 //                              orient          =>              orientation matrix
2695 //
2696 vector* vm_rotate_vec_to_world(vector *world_vec, vector *body_vec, matrix *orient)
2697 {
2698         return vm_vec_rotate(world_vec, body_vec, orient);
2699 }
2700
2701
2702 // ----------------------------------------------------------------------------
2703 // vm_estimate_next_orientation()
2704 //
2705 // given a last orientation and current orientation, estimate the next orientation
2706 //
2707 //      inputs: last_orient             =>              last orientation matrix
2708 //                              current_orient  =>              current orientation matrix
2709 //                              next_orient             =>              next orientation matrix         [the result]
2710 //
2711 void vm_estimate_next_orientation(matrix *last_orient, matrix *current_orient, matrix *next_orient)
2712 {
2713         //              R L = C         =>              R = C (L)^-1
2714         //              N = R C         =>              N = C (L)^-1 C
2715
2716         matrix Mtemp;
2717         matrix Rot_matrix;
2718         vm_copy_transpose_matrix(&Mtemp, last_orient);                          // Mtemp = (L)^-1
2719         vm_matrix_x_matrix(&Rot_matrix, &Mtemp, current_orient);        // R = C Mtemp1
2720         vm_matrix_x_matrix(next_orient, current_orient, &Rot_matrix);
2721 }
2722
2723 //      Return true if all elements of *vec are legal, that is, not a NAN.
2724 int is_valid_vec(vector *vec)
2725 {
2726         return !_isnan(vec->x) && !_isnan(vec->y) && !_isnan(vec->z);
2727 }
2728
2729 //      Return true if all elements of *m are legal, that is, not a NAN.
2730 int is_valid_matrix(matrix *m)
2731 {
2732         return is_valid_vec(&m->fvec) && is_valid_vec(&m->uvec) && is_valid_vec(&m->rvec);
2733 }
2734
2735 // interpolate between 2 vectors. t goes from 0.0 to 1.0. at
2736 void vm_vec_interp_constant(vector *out, vector *v0, vector *v1, float t)
2737 {
2738         vector cross;
2739         float total_ang;
2740
2741         // get the cross-product of the 2 vectors
2742         vm_vec_crossprod(&cross, v0, v1);
2743         vm_vec_normalize(&cross);
2744
2745         // get the total angle between the 2 vectors
2746         total_ang = -(float)acos(vm_vec_dot(v0, v1));
2747
2748         // rotate around the cross product vector by the appropriate angle
2749         vm_rot_point_around_line(out, v0, t * total_ang, &vmd_zero_vector, &cross);
2750 }
2751
2752 // randomly perturb a vector around a given (normalized vector) or optional orientation matrix
2753 void vm_vec_random_cone(vector *out, vector *in, float max_angle, matrix *orient)
2754 {
2755         vector t1, t2;
2756         matrix *rot;
2757         matrix m;
2758
2759         // get an orientation matrix
2760         if(orient != NULL){
2761                 rot = orient;
2762         } else {
2763                 vm_vector_2_matrix(&m, in, NULL, NULL);
2764                 rot = &m;
2765         }
2766         
2767         // axis 1
2768         vm_rot_point_around_line(&t1, in, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->fvec);
2769         
2770         // axis 2
2771         vm_rot_point_around_line(&t2, &t1, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->rvec);
2772
2773         // axis 3
2774         vm_rot_point_around_line(out, &t2, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->uvec);
2775 }
2776
2777 // given a start vector, an orientation and a radius, give a point on the plane of the circle
2778 // if on_edge is 1, the point is on the very edge of the circle
2779 void vm_vec_random_in_circle(vector *out, vector *in, matrix *orient, float radius, int on_edge)
2780 {
2781         vector temp;
2782
2783         // point somewhere in the plane
2784         vm_vec_scale_add(&temp, in, &orient->rvec, on_edge ? radius : frand_range(0.0f, radius));
2785
2786         // rotate to a random point on the circle
2787         vm_rot_point_around_line(out, &temp, fl_radian(frand_range(0.0f, 359.0f)), in, &orient->fvec);
2788 }
2789
2790 // find the nearest point on the line to p. if dist is non-NULL, it is filled in
2791 // returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment
2792 int vm_vec_dist_to_line(vector *p, vector *l0, vector *l1, vector *nearest, float *dist)
2793 {
2794         vector a, b, c;
2795         float b_mag, comp;
2796
2797 #ifndef NDEBUG
2798         if(vm_vec_same(l0, l1)){
2799                 *nearest = vmd_zero_vector;
2800                 return -1;
2801         }
2802 #endif
2803
2804         // compb_a == a dot b / len(b)
2805         vm_vec_sub(&a, p, l0);
2806         vm_vec_sub(&b, l1, l0);         
2807         b_mag = vm_vec_copy_normalize(&c, &b);  
2808
2809         // calculate component
2810         comp = vm_vec_dotprod(&a, &b) / b_mag;
2811
2812         // stuff nearest
2813         vm_vec_scale_add(nearest, l0, &c, comp);
2814
2815         // maybe get the distance
2816         if(dist != NULL){               
2817                 *dist = vm_vec_dist(nearest, p);
2818         }
2819
2820         // return the proper value
2821         if(comp < 0.0f){
2822                 return -1;                                              // before the line
2823         } else if(comp > b_mag){
2824                 return 1;                                               // after the line
2825         }
2826         return 0;                                                       // on the line
2827 }