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