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