]> icculus.org git repositories - taylor/freespace2.git/blob - src/physics/physics.cpp
More stuff compiles
[taylor/freespace2.git] / src / physics / physics.cpp
1 /*
2  * $Logfile: /Freespace2/code/Physics/Physics.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Physics stuff
8  *
9  * $Log$
10  * Revision 1.2  2002/05/03 13:34:33  theoddone33
11  * More stuff compiles
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:10  root
14  * Initial import.
15  *
16  * 
17  * 5     8/13/99 10:49a Andsager
18  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
19  * modes dont collide big ships.
20  * 
21  * 4     7/03/99 5:50p Dave
22  * Make rotated bitmaps draw properly in padlock views.
23  * 
24  * 3     5/11/99 10:16p Andsager
25  * First pass on engine wash effect.  Rotation (control input), damage,
26  * shake.  
27  * 
28  * 2     10/07/98 10:53a Dave
29  * Initial checkin.
30  * 
31  * 1     10/07/98 10:50a Dave
32  * 
33  * 125   8/26/98 4:00p Johnson
34  * Removed max velocity assert so the new huge cap ship can warp in
35  * safely.
36  * 
37  * 124   5/18/98 5:01p Comet
38  * don't do displacement checks in multiplayer
39  * 
40  * 123   4/06/98 2:38p Duncan
41  * AL: Fix potential negative sqrt due to numerical inaccuracy
42  * 
43  * 122   3/25/98 1:30p Andsager
44  * comment out physics assert
45  * 
46  * 121   3/23/98 9:48a Andsager
47  * comment out printf
48  * 
49  * 120   3/22/98 4:11p Andsager
50  * Remove global Freespace_running
51  * 
52  * 119   3/20/98 5:15p Andsager
53  * Revised calculation of rotation, shake, and velocity in response to
54  * shockwave since capital ships were seen  to shake.
55  * 
56  * 118   3/19/98 1:06p Andsager
57  * Fix bug checking excessive velocity with accelerated time.
58  * 
59  * 117   3/17/98 5:43p Andsager
60  * Modify physics checks for very slow frame rates.
61  * 
62  * 116   3/17/98 9:54a Andsager
63  * Temporary take out Int3()s in velocity checks
64  * 
65  * 115   3/17/98 9:51a Allender
66  * temporarily commented out Int3 that was causing pain
67  * 
68  * 114   3/16/98 6:07p Johnson
69  * DA:  check that ship velocity does not get too high from collisons
70  * 
71  * 113   3/16/98 4:39p Adam
72  * change Dave's asserts so that displacement checks don't get used when
73  * not "in mission"
74  * 
75  * 112   3/16/98 1:11p Andsager
76  * Turn on velocity, displacement limit checks
77  * 
78  * 111   3/12/98 5:21p Andsager
79  * Optimize physics for lasers and dumbfire missiles
80  * 
81  * 110   3/09/98 2:10p Andsager
82  * Put in checks for debris (other) with excessive velocity.
83  * 
84  * 109   3/09/98 12:59p Mike
85  * Put error checking in physics code to detect NANs.
86  * 
87  * 108   3/09/98 12:13a Andsager
88  * Add code to help find jumps in position.
89  * 
90  * 107   3/03/98 10:39a Andsager
91  * Fixed erroneous mprintf for log term in physics_apply_shock
92  * 
93  * 106   2/11/98 4:52p Mike
94  * Better attacking by ships.
95  * Fix stupidity in AI classes, which were in backward order!
96  * 
97  * 105   2/03/98 6:01p Andsager
98  * Fix excessive rotvel in debris_create.  Check using physics function
99  * check_rotvel_limit.
100  * 
101  * 104   2/03/98 10:45a Mike
102  * Comment out mprintf that could occur very often.
103  * 
104  * 103   1/29/98 2:38p Andsager
105  * Fix bug in physics_apply_shock so that large ships have a smaller
106  * effect from shockwaves.
107  * 
108  * 102   1/23/98 11:31a Andsager
109  * Added I_body_inv to phys_init.  Needed for shockwaves hitting ships in
110  * descent style physics.
111  * 
112  * 101   1/23/98 9:02a John
113  * Took out Dave's debugging Int3 since they aren't finding what he
114  * thought they would and they broke Testcode and pofview.
115  * 
116  * 100   1/22/98 5:10p Mike
117  * Fix bug with player's damp getting stuck off.
118  * 
119  * 99    1/20/98 3:13p Allender
120  * check for Player_obj being valid before doing some other andsager
121  * sanity check in physics_sim_vel
122  * 
123  * 98    1/20/98 10:08a Andsager
124  * Remove uninitialized viariable warnings.
125  * 
126  * 97    1/19/98 3:46p Allender
127  * fixed problem where a previous changed caused optimized builds to
128  * break.  Don't do a quick out on velcoity_ramp() anymore
129  * 
130  * 96    1/19/98 12:00p Dave
131  * DA:  Revise instantaneous velocity debug check to work with
132  * multiplayer.
133  * 
134  * 95    1/16/98 3:03p Andsager
135  * Fix debug info from int3() on player death.
136  * 
137  * 94    1/16/98 2:54p Andsager
138  * Fix debug info.
139  * 
140  * 93    1/16/98 2:34p Andsager
141  * Added debug code to find instantaneous acceleration.
142  * 
143  * 92    1/16/98 12:14p Andsager
144  * Add error checking for the current brief stage
145  * 
146  * 91    12/29/97 5:10p Allender
147  * fixed problems with speed not being reported properly in multiplayer
148  * games.  Made read_flying_controls take frametime as a parameter.  More
149  * ship/weapon select stuff
150  * 
151  * 90    12/29/97 12:58p Johnson
152  * don't debug rotational velocity when Fred is running
153  * 
154  * 89    12/08/97 10:29a Andsager
155  * Remove shockwave physics parameters from weapon_area_apply_blast and
156  * move into physics
157  * 
158  * 88    12/05/97 3:31p Andsager
159  * Added view shake if hit by a weapon or collisoin.
160  * 
161  * 87    12/03/97 5:47p Andsager
162  * Changed reduced damping following collision or weapon to run off time
163  * stamp. 
164  * 
165  * 86    11/24/97 1:54p Dan
166  * Mike: Comment out Assert() in physics, debug_rotvel().
167  * 
168  * 85    11/24/97 8:46a Andsager
169  * Added rotational velocity caps and debug info.
170  * 
171  * 84    11/20/97 4:01p Mike
172  * Prevent divide overflow.
173  * 
174  * 83    11/20/97 12:34a Mike
175  * Make ships coast to a stop when their engines have been destroyed.
176  * Tricky because code doesn't use damp in forward dimension.
177  * 
178  * 82    11/19/97 5:57p Mike
179  * Hmm, undid all my changes, except for the crucial removal of a blank
180  * between "physics_sim" and the open paren.
181  * 
182  * 81    11/19/97 1:26a Andsager
183  * Made shockwaves work again, including shake.
184  * 
185  * 80    11/17/97 5:15p Andsager
186  * 
187  * 79    11/13/97 6:01p Andsager
188  * Improve comment in physic_collide_whack
189  * 
190  * 78    11/13/97 5:41p Andsager
191  * Decreased the rotational whack after getting hit by lasers and
192  * missiles.
193  * 
194  * 77    10/29/97 5:01p Andsager
195  * fixed bug in collision physics (physics_collide_whack)
196  * 
197  * 76    9/16/97 5:28p Andsager
198  * calculate velocity in physics_sim_vel
199  * 
200  * 75    9/11/97 5:25p Mike
201  * Fix ! vs. & precedence bug in physics_sim_vel().
202  * 
203  * 74    9/09/97 10:14p Andsager
204  * 
205  * 73    9/04/97 5:09p Andsager
206  * implement physics using moment of inertia and mass (from BSPgen).
207  * Added to phys_info struct.  Updated ship_info, polymodel structs.
208  * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables
209  * 
210  * 72    9/03/97 5:43p Andsager
211  * fixed bug calculating ramp velocity after getting whacked
212  * 
213  * 71    9/02/97 4:19p Mike
214  * Comment out code at end of physics_apply_whack() that made ships nearly
215  * stop.
216  * 
217  * 70    8/29/97 10:13a Allender
218  * work on server/client prediction code -- doesn't work too well.  Made
219  * all clients simulate their own orientation with the server giving
220  * corrections every so often.
221  * 
222  * 69    8/25/97 2:41p Andsager
223  * collision code also changes ramp velocity to take account of collison.
224  * some optimization of collision physics.
225  * 
226  * 68    8/19/97 9:56a John
227  * new thruster effect fairly functional
228  * 
229  * 67    8/18/97 6:26p Andsager
230  * preliminary version of collision physics
231  * 
232  * 66    8/17/97 9:19p Andsager
233  * improvement to collision physics
234  * 
235  * 65    8/13/97 12:16p Andsager
236  * intermediate level checkin for use with collision with extended objects
237  * 
238  * 64    8/05/97 3:13p Andsager
239  * improved comments to apply_whack
240  * 
241  * 63    8/05/97 10:18a Lawrance
242  * my_rand() being used temporarily instead of rand()
243  * 
244  * 62    7/31/97 12:44p Andsager
245  * 
246  * 61    7/25/97 5:05p John
247  * fixed a potential bug in ramp_velocity when delta dist becomes 0, a
248  * bunch of sideways 8 thingys appear :-)
249  * 
250  * 
251  * 60    7/25/97 1:04p Andsager
252  * Modified physics flag PF_REDUCED_DAMP for damping when object is hit.
253  * Flag is now set in physics_apply_whack/shock and turned off in
254  * physics_sim_vel.  Weapons should not directly set this flag.
255  * 
256  * 59    7/25/97 9:07a Andsager
257  * modified apply_whack
258  * 
259  * 58    7/23/97 5:10p Andsager
260  * Enhanced shockwave effect with shake based on blast force.  
261  * 
262  * 57    7/22/97 2:40p Andsager
263  * shockwaves now cause proper rotation of ships
264  * 
265  * 56    7/21/97 4:12p Mike
266  * Two ships move as one while docked, including physics whacks.  Spin of
267  * objects when killed based on speed.
268  * 
269  * 55    7/21/97 2:19p John
270  * made descent-style physics work for testcode. fixed bug in the
271  * velocity_ramp code with t==0.0f
272  * 
273  * 54    7/17/97 8:02p Lawrance
274  * improve comments to physics_apply_whack()
275  * 
276  * 53    7/16/97 4:42p Mike
277  * Make afterburner shake viewer, not ship.
278  * Shake for limited time.
279  * Add timestamp_until() function to timer library.
280  * 
281  * 52    7/16/97 11:53a Andsager
282  * 
283  * 51    7/16/97 11:06a Andsager
284  * Allow damping on z to support shockwaves
285  * 
286  * 50    7/15/97 12:28p Andsager
287  * commented out some debug code.
288  * 
289  * 49    7/15/97 12:25p Andsager
290  * More integration with new physics.
291  * 
292  * 48    7/15/97 12:03p Andsager
293  * New physics stuff
294  * 
295  * 47    7/11/97 11:54a John
296  * added rotated 3d bitmaps.
297  * 
298  * 46    6/25/97 12:22p Mike
299  * Diminish bashing into of objects.  Make ships flying waypoints fly in
300  * formation.
301  * 
302  * 45    6/17/97 10:59p Mike
303  * Comment out irritating mprintf().
304  * 
305  * 44    6/11/97 4:27p Mike
306  * Balance the whack ships take when they get hit.
307  * 
308  * 43    4/17/97 10:02a Lawrance
309  * allow ship shaking for ABURN_DECAY_TIME after afterburners cut out
310  * 
311  * 42    4/16/97 10:48p Mike
312  * Afterburner shake.
313  * Made afterburner engagement a bit in physics flags, removed from ship
314  * flags, removed a parameter to physics_read_flying_controls().
315  * 
316  * 41    4/11/97 3:17p Mike
317  * Modify physics_sim() to use non quick version of vm_vec_mag().  Quick
318  * version was causing cumulative errors in velocity of homing missiles.
319  * 
320  * 40    4/10/97 3:20p Mike
321  * Change hull damage to be like shields.
322  * 
323  * 39    4/04/97 11:08a Adam
324  * played with banking values
325  * 
326  * 38    4/04/97 12:19a Mike
327  * Make ships bank when they turn.
328  * 
329  * 37    3/04/97 3:10p Mike
330  * Intermediate checkin: Had to resolve some build errors.  Working on two
331  * docked ships moving as one.
332  * 
333  * 36    2/25/97 7:39p Lawrance
334  * added afterburner_on flag to physics_read_flying_controls() to account
335  * for afterburner effects
336  * 
337  * 35    2/05/97 6:02p Hoffoss
338  * Added heading rotation around universal Y axis to Fred when controlling
339  * the camera.
340  * 
341  * 34    2/05/97 9:15a Mike
342  * Partial implementation of new docking system, integrated so I could
343  * update on John's new turret code.
344  * 
345  * 33    1/20/97 7:58p John
346  * Fixed some link errors with testcode.
347  * 
348  * $NoKeywords: $
349  */
350
351 #include <stdio.h>
352 #include <stdlib.h>
353 #include <string.h>
354 #include <math.h>
355
356 #include "physics.h"
357 #include "floating.h"
358 #include "player.h"
359 #include "freespace.h"
360 #include "linklist.h"
361 #include "timer.h"
362 #include "key.h"
363
364 // defines for physics functions
365 #define MAX_TURN_LIMIT  0.2618f         // about 15 degrees
366
367 #define ROT_DEBUG
368 #define ROTVEL_TOL              0.1                     // Amount of rotvel is decreased if over cap
369 #define ROTVEL_CAP              14.0                    // Rotational velocity cap for live objects
370 #define DEAD_ROTVEL_CAP 16.3                    // Rotational velocity cap for dead objects
371
372 #define MAX_SHIP_SPEED          300             // Maximum speed allowed after whack or shockwave
373 #define RESET_SHIP_SPEED        240             // Speed that a ship is reset to after exceeding MAX_SHIP_SPEED
374
375 #define SW_ROT_FACTOR                   5               // increase in rotational time constant in shockwave
376 #define SW_BLAST_DURATION               2000    // maximum duration of shockwave
377 #define REDUCED_DAMP_FACTOR     10              // increase in side_slip and acceleration time constants (scaled according to reduced damp time)
378 #define REDUCED_DAMP_VEL                30              // change in velocity at which reduced_damp_time is 2000 ms
379 #define REDUCED_DAMP_TIME               2000    // ms (2.0 sec)
380 #define WEAPON_SHAKE_TIME               500     //      ms (0.5 sec)    viewer shake time after hit by weapon (implemented via afterburner shake)
381 #define SPECIAL_WARP_T_CONST    0.651   // special warp time constant (loose 99 % of excess speed in 3 sec)
382
383 #define PHYS_DEBUG                                              // check if (vel > 500) or (displacement in one frame > 350)
384
385 void update_reduced_damp_timestamp( physics_info *pi, float impulse );
386
387 void physics_init( physics_info * pi )
388 {
389         memset( pi, 0, sizeof(physics_info) );
390
391         pi->mass = 10.0f;                                       // This ship weighs 10 units
392         pi->side_slip_time_const = 0.05f;                                       
393         pi->rotdamp = 0.1f;
394
395         pi->max_vel.x = 100.0f;         //sideways
396         pi->max_vel.y = 100.0f;         //up/down
397         pi->max_vel.z = 100.0f;         //forward
398         pi->max_rear_vel = 100.0f;      //backward -- controlled seperately
399
400         pi->max_rotvel.x = 2.0f;                //pitch 
401         pi->max_rotvel.y = 1.0f;                //heading
402         pi->max_rotvel.z = 2.0f;                //bank
403
404         pi->prev_ramp_vel.x = 0.0f;
405         pi->prev_ramp_vel.y = 0.0f;
406         pi->prev_ramp_vel.z = 0.0f;
407
408         pi->desired_vel.x = 0.0f;
409         pi->desired_vel.y = 0.0f;
410         pi->desired_vel.z = 0.0f;
411
412         pi->slide_accel_time_const=pi->side_slip_time_const;    // slide using max_vel.x & .y
413         pi->slide_decel_time_const=pi->side_slip_time_const;    // slide using max_vel.x & .y
414
415         pi->afterburner_decay = 1;      
416         pi->forward_thrust = 0.0f;
417
418         pi->flags = 0;
419
420         // default values for moment of inetaia
421         vm_vec_make( &pi->I_body_inv.rvec, 1e-5f, 0.0f, 0.0f );
422         vm_vec_make( &pi->I_body_inv.uvec, 0.0f, 1e-5f, 0.0f );
423         vm_vec_make( &pi->I_body_inv.fvec, 0.0f, 0.0f, 1e-5f );
424
425 }
426
427
428 //==========================================================================
429 // apply_physics - This does correct physics independent of frame rate.
430 //
431 // Given:
432 //    damping = damping factor.  Setting this to zero make the object instantly
433 //              go to the target velocity.  Increasing it makes the object ramp
434 //              up or down to the target velocity.
435 //    desired_vel = the target velocity
436 //    initial_vel = velocity at last call
437 //    t = elapsed time since last call
438 //
439 // Returns:
440 //    new_vel = current velocity
441 //    delta_pos = delta position (framevec)
442 // You can extend this to 3d by calling it 3 times, once for each x,y,z component.
443
444 void apply_physics( float damping, float desired_vel, float initial_vel, float t, float * new_vel, float * delta_pos )
445 {
446         if ( damping < 0.0001f )        {
447                 if ( delta_pos )
448                         *delta_pos = desired_vel*t;
449                 if ( new_vel )
450                         *new_vel = desired_vel;
451         } else {
452                 float dv, e;
453                 dv = initial_vel - desired_vel;
454                 e = (float)exp( -t/damping );
455                 if ( delta_pos )
456                         *delta_pos = (1.0f - e)*dv*damping + desired_vel*t;
457                 if ( new_vel )
458                         *new_vel = dv*e + desired_vel;
459         }
460 }
461
462
463
464 float Physics_viewer_bank = 0.0f;
465 int Physics_viewer_direction = PHYSICS_VIEWER_FRONT;
466 static physics_info * Viewer_physics_info = NULL;
467
468 // If you would like Physics_viewer_bank to be tracked (Which is needed
469 // for rotating 3d bitmaps) call this and pass it a pointer to the
470 // viewer's physics info.
471 void physics_set_viewer( physics_info * p, int dir )
472 {
473         if ( (Viewer_physics_info != p) || (Physics_viewer_direction != dir) )  {
474                 Viewer_physics_info = p;
475                 Physics_viewer_bank = 0.0f;
476                 Physics_viewer_direction = dir;
477         }
478 }
479
480
481
482 //      -----------------------------------------------------------------------------------------------------------
483 // add rotational velocity & acceleration
484
485
486
487 void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time )
488 {
489         angles  tangles;
490         vector  new_vel;
491         matrix  tmp;
492         float           shock_amplitude;
493         float           rotdamp;
494         float           shock_fraction_time_left;
495
496         Assert(is_valid_matrix(orient));
497         Assert(is_valid_vec(&pi->rotvel));
498         Assert(is_valid_vec(&pi->desired_rotvel));
499
500         // Handle special case of shockwave
501         shock_amplitude = 0.0f;
502         if ( pi->flags & PF_IN_SHOCKWAVE ) {
503                 if ( timestamp_elapsed(pi->shockwave_decay) ) {
504                         pi->flags &= ~PF_IN_SHOCKWAVE;
505                         rotdamp = pi->rotdamp;
506                 } else {
507                         shock_fraction_time_left = timestamp_until( pi->shockwave_decay ) / (float) SW_BLAST_DURATION;
508                         rotdamp = pi->rotdamp + pi->rotdamp * (SW_ROT_FACTOR - 1) * shock_fraction_time_left;
509                         shock_amplitude = pi->shockwave_shake_amp * shock_fraction_time_left;
510                 }
511         } else {
512                 rotdamp = pi->rotdamp;
513         }
514
515         // Do rotational physics with given damping
516         apply_physics( rotdamp, pi->desired_rotvel.x, pi->rotvel.x, sim_time, &new_vel.x, NULL );
517         apply_physics( rotdamp, pi->desired_rotvel.y, pi->rotvel.y, sim_time, &new_vel.y, NULL );
518         apply_physics( rotdamp, pi->desired_rotvel.z, pi->rotvel.z, sim_time, &new_vel.z, NULL );
519
520         /*
521 #ifdef ROT_DEBUG
522         if (check_rotvel_limit( pi )) {
523                 nprintf(("Physics", "rotvel reset in physics_sim_rot\n"));
524         }
525 #endif
526 */
527         Assert(is_valid_vec(&new_vel));
528
529         pi->rotvel = new_vel;
530
531         tangles.p = pi->rotvel.x*sim_time;
532         tangles.h = pi->rotvel.y*sim_time;
533         tangles.b = pi->rotvel.z*sim_time;
534
535         // If this is the viewer_object, keep track of the
536         // changes in banking so that rotated bitmaps look correct.
537         // This is used by the g3_draw_rotated_bitmap function.
538         if ( pi == Viewer_physics_info )        {
539                 switch(Physics_viewer_direction){
540                 case PHYSICS_VIEWER_FRONT:                              
541                         Physics_viewer_bank -= tangles.b;
542                         break;
543
544                 case PHYSICS_VIEWER_UP:
545                         Physics_viewer_bank -= tangles.h;
546                         break;
547
548                 case PHYSICS_VIEWER_REAR:
549                         Physics_viewer_bank += tangles.b;
550                         break;
551
552                 case PHYSICS_VIEWER_LEFT:
553                         Physics_viewer_bank += tangles.p;
554                         break;
555
556                 case PHYSICS_VIEWER_RIGHT:
557                         Physics_viewer_bank -= tangles.p;
558                         break;
559
560                 default:
561                         Physics_viewer_bank -= tangles.b;
562                         break;
563                 }
564
565                 if ( Physics_viewer_bank < 0.0f ){
566                         Physics_viewer_bank += 2.0f * PI;
567                 }
568
569                 if ( Physics_viewer_bank > 2.0f * PI ){
570                         Physics_viewer_bank -= 2.0f * PI;
571                 }
572         }
573
574 /*      //      Make ship shake due to afterburner.
575         if (pi->flags & PF_AFTERBURNER_ON || !timestamp_elapsed(pi->afterburner_decay) ) {
576                 float   max_speed;
577
578                 max_speed = vm_vec_mag_quick(&pi->max_vel);
579                 tangles.p += (float) (rand()-RAND_MAX/2)/RAND_MAX * pi->speed/max_speed/64.0f;
580                 tangles.h += (float) (rand()-RAND_MAX/2)/RAND_MAX * pi->speed/max_speed/64.0f;
581                 if ( pi->flags & PF_AFTERBURNER_ON ) {
582                         pi->afterburner_decay = timestamp(ABURN_DECAY_TIME);
583                 }
584         }
585 */
586
587         // Make ship shake due to shockwave, decreasing in amplitude at the end of the shockwave
588         if ( pi->flags & PF_IN_SHOCKWAVE ) {
589                 tangles.p += (float) (myrand()-RAND_MAX/2)/RAND_MAX * shock_amplitude;
590                 tangles.h += (float) (myrand()-RAND_MAX/2)/RAND_MAX * shock_amplitude;
591         }
592
593
594         vm_angles_2_matrix(&pi->last_rotmat, &tangles );
595         vm_matrix_x_matrix( &tmp, orient, &pi->last_rotmat );
596         *orient = tmp;
597
598         vm_orthogonalize_matrix(orient);
599                 
600 }
601
602 //      -----------------------------------------------------------------------------------------------------------
603 // add rotational velocity & acceleration
604
605 void physics_sim_rot_editor(matrix * orient, physics_info * pi, float sim_time)
606 {
607         angles  tangles;
608         vector  new_vel;
609         matrix  tmp;
610         angles  t1, t2;
611
612         apply_physics( pi->rotdamp, pi->desired_rotvel.x, pi->rotvel.x, sim_time, 
613                                                                  &new_vel.x, NULL );
614
615         apply_physics( pi->rotdamp, pi->desired_rotvel.y, pi->rotvel.y, sim_time, 
616                                                                  &new_vel.y, NULL );
617
618         apply_physics( pi->rotdamp, pi->desired_rotvel.z, pi->rotvel.z, sim_time, 
619                                                                  &new_vel.z, NULL );
620         
621         pi->rotvel = new_vel;
622
623         tangles.p = pi->rotvel.x*sim_time;
624         tangles.h = pi->rotvel.y*sim_time;
625         tangles.b = pi->rotvel.z*sim_time;
626
627         t1 = t2 = tangles;
628         t1.h = 0.0f;  t1.b = 0.0f;
629         t2.p = 0.0f; t2.b = 0.0f;
630
631         // put in p & b like normal
632         vm_angles_2_matrix(&pi->last_rotmat, &t1 );
633         vm_matrix_x_matrix( &tmp, orient, &pi->last_rotmat );
634         
635         // Put in heading separately
636         vm_angles_2_matrix(&pi->last_rotmat, &t2 );
637         vm_matrix_x_matrix( orient, &pi->last_rotmat, &tmp );
638
639         vm_orthogonalize_matrix(orient);
640                 
641 }
642
643 // Adds velocity to position
644 // finds velocity and displacement in local coords
645 void physics_sim_vel(vector * position, physics_info * pi, float sim_time, matrix *orient)
646 {
647         vector local_disp;              // displacement in this frame
648         vector local_v_in;              // velocity in local coords at the start of this frame
649         vector local_desired_vel;       // desired velocity in local coords
650         vector local_v_out;             // velocity in local coords following this frame
651         vector damp;
652
653         //      Maybe clear the reduced_damp flag.
654         //      This fixes the problem of the player getting near-instantaneous acceleration under unknown circumstances.
655         //      The larger problem is probably that PF_USE_VEL is getting stuck set.
656         if ((pi->flags & PF_REDUCED_DAMP) && (timestamp_elapsed(pi->reduced_damp_decay))) {
657                 pi->flags &= ~PF_REDUCED_DAMP;
658         }
659
660         // Set up damping constants based on special conditions
661         // ie. shockwave, collision, weapon, dead
662         if (pi->flags & PF_DEAD_DAMP) {
663                 // side_slip_time_const is already quite large and now needs to be applied in all directions
664                 vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, pi->side_slip_time_const );
665
666         } else if (pi->flags & PF_REDUCED_DAMP) {
667                 // case of shock, weapon, collide, etc.
668                 if ( timestamp_elapsed(pi->reduced_damp_decay) ) {
669                         vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f );
670                 } else {
671                         // damp is multiplied by fraction and not fraction^2, gives better collision separation
672                         float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME;
673                         damp.x = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left );
674                         damp.y = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left );
675                         damp.z = pi->side_slip_time_const * reduced_damp_fraction_time_left * REDUCED_DAMP_FACTOR;
676                 }
677         } else {
678                 // regular damping
679                 vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f );
680         }
681
682         // Note: CANNOT maintain a *local velocity* since a rotation can occur in this frame. 
683         // thus the local velocity of in the next frame can be different (this would require rotate to change local vel
684         // and this is not desired
685
686         // get local components of current velocity
687         vm_vec_rotate (&local_v_in, &pi->vel, orient);
688
689         // get local components of desired velocity
690         vm_vec_rotate (&local_desired_vel, &pi->desired_vel, orient);
691
692         // find updated LOCAL velocity and position in the local x direction
693         apply_physics (damp.x, local_desired_vel.x, local_v_in.x, sim_time, &local_v_out.x, &local_disp.x);
694
695         // find updated LOCAL velocity and position in the local y direction
696         apply_physics (damp.y, local_desired_vel.y, local_v_in.y, sim_time, &local_v_out.y, &local_disp.y);
697
698         // find updated LOCAL velocity and position in the local z direction
699         // for player ship, damp should normally be zero, but may be altered in a shockwave
700         //  in death, shockwave,etc. we want damping time const large for all 3 axes
701         // warp in test - make excessive speed drop exponentially from max allowed
702         // become (0.01x in 3 sec)
703
704         int special_warp_in = FALSE;
705         float excess = local_v_in.z - pi->max_vel.z;
706         if (excess > 5 && (pi->flags & PF_SPECIAL_WARP_IN)) {
707                 special_warp_in = TRUE;
708                 float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST));
709                 local_v_out.z = pi->max_vel.z + excess * exp_factor;
710                 local_disp.z = (pi->max_vel.z * sim_time) + excess * (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor));
711         } else if (pi->flags & PF_SPECIAL_WARP_OUT) {
712                 float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST));
713                 vector temp;
714                 vm_vec_rotate(&temp, &pi->prev_ramp_vel, orient);
715                 float deficeit = temp.z - local_v_in.z;
716                 local_v_out.z = local_v_in.z + deficeit * (1.0f - exp_factor);
717                 local_disp.z = (local_v_in.z * sim_time) + deficeit * (sim_time - (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor)));
718         } else {
719                 apply_physics (damp.z, local_desired_vel.z, local_v_in.z, sim_time, &local_v_out.z, &local_disp.z);
720         }
721
722         // maybe turn off special warp in flag
723         if ((pi->flags & PF_SPECIAL_WARP_IN) && (excess < 5)) {
724                 pi->flags &= ~(PF_SPECIAL_WARP_IN);
725         }
726
727         // update world position from local to world coords using orient
728         vector world_disp;
729         vm_vec_unrotate (&world_disp, &local_disp, orient);
730 #ifdef PHYS_DEBUG
731         // check for  excess velocity or translation
732         // GET DaveA.
733         if ( (Game_mode & GM_IN_MISSION) && (Game_mode & GM_NORMAL) ) {
734                 // Assert( (sim_time > 0.5f) || (vm_vec_mag_squared(&pi->vel) < 500*500) );
735                 // Assert( (sim_time > 0.5f) || (vm_vec_mag_squared(&world_disp) < 350*350) );
736         }
737 #endif
738         vm_vec_add2 (position, &world_disp);
739
740         // update world velocity
741         vm_vec_unrotate(&pi->vel, &local_v_out, orient);
742
743         if (special_warp_in) {
744                 vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient);
745         }
746 }
747
748 //      -----------------------------------------------------------------------------------------------------------
749 // Simulate a physics object for this frame
750 void physics_sim(vector* position, matrix* orient, physics_info* pi, float sim_time)
751 {
752         // check flag which tells us whether or not to do velocity translation
753         if (pi->flags & PF_CONST_VEL) {
754                 vm_vec_scale_add2(position, &pi->vel, sim_time);
755         } else {
756                 physics_sim_vel(position, pi, sim_time, orient);
757
758                 physics_sim_rot(orient, pi, sim_time);
759
760                 pi->speed = vm_vec_mag(&pi->vel);                                                       //      Note, cannot use quick version, causes cumulative error, increasing speed.
761                 pi->fspeed = vm_vec_dot(&orient->fvec, &pi->vel);               // instead of vector magnitude -- use only forward vector since we are only interested in forward velocity
762         }
763
764 }
765
766 //      -----------------------------------------------------------------------------------------------------------
767 // Simulate a physics object for this frame.  Used by the editor.  The difference between
768 // this function and physics_sim() is that this one uses a heading change to rotate around
769 // the universal Y axis, rather than the local orientation's Y axis.  Banking is also ignored.
770 void physics_sim_editor(vector *position, matrix * orient, physics_info * pi, float sim_time )
771 {
772         physics_sim_vel(position, pi, sim_time, orient);
773         physics_sim_rot_editor(orient, pi, sim_time);
774         pi->speed = vm_vec_mag_quick(&pi->vel);
775         pi->fspeed = vm_vec_dot(&orient->fvec, &pi->vel);               // instead of vector magnitude -- use only forward vector since we are only interested in forward velocity
776 }
777
778 // function to predict an object's position given the delta time and an objects physics info
779 void physics_predict_pos(physics_info *pi, float delta_time, vector *predicted_pos)
780 {
781         apply_physics( pi->side_slip_time_const, pi->desired_vel.x, pi->vel.x, delta_time, 
782                                                                  NULL, &predicted_pos->x );
783
784         apply_physics( pi->side_slip_time_const, pi->desired_vel.y, pi->vel.y, delta_time, 
785                                                                  NULL, &predicted_pos->y );
786
787         apply_physics( pi->side_slip_time_const, pi->desired_vel.z, pi->vel.z, delta_time, 
788                                                                  NULL, &predicted_pos->z );
789 }
790
791 // function to predict an object's velocity given the parameters
792 void physics_predict_vel(physics_info *pi, float delta_time, vector *predicted_vel)
793 {
794         if (pi->flags & PF_CONST_VEL) {
795                 predicted_vel = &pi->vel;
796         } else {
797                 apply_physics( pi->side_slip_time_const, pi->desired_vel.x, pi->vel.x, delta_time, 
798                                                                          &predicted_vel->x, NULL );
799
800                 apply_physics( pi->side_slip_time_const, pi->desired_vel.y, pi->vel.y, delta_time, 
801                                                                          &predicted_vel->y, NULL );
802
803                 apply_physics( pi->side_slip_time_const, pi->desired_vel.z, pi->vel.z, delta_time, 
804                                                                          &predicted_vel->z, NULL );
805         }
806 }
807
808 // function to predict position and velocity of an object
809 void physics_predict_pos_and_vel(physics_info *pi, float delta_time, vector *predicted_vel, vector *predicted_pos)
810 {
811
812         apply_physics( pi->side_slip_time_const, pi->desired_vel.x, pi->vel.x, delta_time, 
813                               &predicted_vel->x, &predicted_pos->x );
814
815         apply_physics( pi->side_slip_time_const, pi->desired_vel.y, pi->vel.y, delta_time, 
816                               &predicted_vel->y, &predicted_pos->y );
817
818         apply_physics( pi->side_slip_time_const, pi->desired_vel.z, pi->vel.z, delta_time, 
819                               &predicted_vel->z, &predicted_pos->z );
820 }
821
822 // physics_read_flying_controls() 
823 //
824 // parmeters:  *orient  ==>
825 //                                      *pi             ==>
826 //                                      *ci             ==> 
827 //      Adam: Uncomment-out this define to enable banking while turning.
828 #define BANK_WHEN_TURN
829
830
831
832 // function looks at the flying controls and the current velocity to determine a goal velocity
833 // function determines velocity in object's reference frame and goal velocity in object's reference frame
834 void physics_read_flying_controls( matrix * orient, physics_info * pi, control_info * ci, float sim_time, vector *wash_rot)
835 {
836         vector goal_vel;                // goal velocity in local coords, *not* accounting for ramping of velcity
837         float ramp_time_const;          // time constant for velocity ramping
838
839         float velocity_ramp (float v_in, float v_goal, float time_const, float t);
840
841 //      if ( keyd_pressed[KEY_LSHIFT] ) {
842 //              keyd_pressed[KEY_LSHIFT] = 0;
843 //              Int3();
844 //      }
845
846         ci->forward += (ci->forward_cruise_percent / 100.0f);
847
848 //      mprintf(("ci->forward == %7.3f\n", ci->forward));
849
850         // give control imput to cause rotation in engine wash
851         extern int Wash_on;
852         if ( wash_rot && Wash_on ) {
853                 ci->pitch += wash_rot->x;
854                 ci->bank += wash_rot->z;
855                 ci->heading += wash_rot->y;
856         }
857
858         if (ci->pitch > 1.0f ) ci->pitch = 1.0f;
859         else if (ci->pitch < -1.0f ) ci->pitch = -1.0f;
860
861         if (ci->vertical > 1.0f ) ci->vertical = 1.0f;
862         else if (ci->vertical < -1.0f ) ci->vertical = -1.0f;
863
864         if (ci->heading > 1.0f ) ci->heading = 1.0f;
865         else if (ci->heading < -1.0f ) ci->heading = -1.0f;
866
867         if (ci->sideways > 1.0f  ) ci->sideways = 1.0f;
868         else if (ci->sideways < -1.0f  ) ci->sideways = -1.0f;
869
870         if (ci->bank > 1.0f ) ci->bank = 1.0f;
871         else if (ci->bank < -1.0f ) ci->bank = -1.0f;
872
873         if ( pi->flags & PF_AFTERBURNER_ON )
874                 ci->forward = 1.0f;
875
876         if (ci->forward > 1.0f ) ci->forward = 1.0f;
877         else if (ci->forward < -1.0f ) ci->forward = -1.0f;
878
879         pi->desired_rotvel.x = ci->pitch * pi->max_rotvel.x;
880         pi->desired_rotvel.y = ci->heading * pi->max_rotvel.y;
881
882         float   delta_bank;
883
884 #ifdef BANK_WHEN_TURN
885         //      To change direction of bank, negate the whole expression.
886         //      To increase magnitude of banking, decrease denominator.
887         //      Adam: The following statement is all the math for banking while turning.
888         delta_bank = - (ci->heading * pi->max_rotvel.y)/2.0f;
889 #else
890         delta_bank = 0.0f;
891 #endif
892
893         pi->desired_rotvel.z = ci->bank * pi->max_rotvel.z + delta_bank;
894         pi->forward_thrust = ci->forward;
895
896         if ( pi->flags & PF_AFTERBURNER_ON ) {
897                 goal_vel.x = ci->sideways*pi->afterburner_max_vel.x;
898                 goal_vel.y = ci->vertical*pi->afterburner_max_vel.y;
899                 goal_vel.z = ci->forward* pi->afterburner_max_vel.z;
900         }
901         else {
902                 goal_vel.x = ci->sideways*pi->max_vel.x;
903                 goal_vel.y = ci->vertical*pi->max_vel.y;
904                 goal_vel.z = ci->forward* pi->max_vel.z;
905         }
906
907         if ( goal_vel.z < -pi->max_rear_vel ) 
908                 goal_vel.z = -pi->max_rear_vel;
909
910
911         if ( pi->flags & PF_ACCELERATES )       {
912                 //
913                 // Determine *resultant* DESIRED VELOCITY (desired_vel) accounting for RAMPING of velocity
914                 // Use LOCAL coordinates
915                 // if slide_enabled, ramp velocity for x and y, otherwise set goal (0)
916                 //    always ramp velocity for z
917                 // 
918
919                 // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast.
920                 // Scale according to reduced_damp_time_expansion.
921                 float reduced_damp_ramp_time_expansion;
922                 if ( pi->flags & PF_REDUCED_DAMP ) {
923                         float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME;
924                         reduced_damp_ramp_time_expansion = 1.0f + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left;
925                 } else {
926                         reduced_damp_ramp_time_expansion = 1.0f;
927                 }
928
929 //      if ( !use_descent && (Player_obj->phys_info.forward_accel_time_const < 0.1) && !(Ships[Player_obj->instance].flags & SF_DYING) && (Player_obj->type != OBJ_OBSERVER) )
930 //                      Int3(); // Get dave A
931
932                 if (pi->flags & PF_SLIDE_ENABLED)  {
933                         // determine the local velocity
934                         // deterimine whether accelerating or decleration toward goal for x
935                         if ( goal_vel.x > 0.0f )  {
936                                 if ( goal_vel.x >= pi->prev_ramp_vel.x ) 
937                                         ramp_time_const = pi->slide_accel_time_const;
938                                 else
939                                         ramp_time_const = pi->slide_decel_time_const;
940                         } else  {  // goal_vel.x <= 0.0
941                                 if ( goal_vel.x <= pi->prev_ramp_vel.x )
942                                         ramp_time_const = pi->slide_accel_time_const;
943                                 else
944                                         ramp_time_const = pi->slide_decel_time_const;
945                         }
946                         // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast
947                         if ( pi->flags & PF_REDUCED_DAMP ) {
948                                 ramp_time_const *= reduced_damp_ramp_time_expansion;
949                         }
950                         pi->prev_ramp_vel.x = velocity_ramp(pi->prev_ramp_vel.x, goal_vel.x, ramp_time_const, sim_time);
951
952                         // deterimine whether accelerating or decleration toward goal for y
953                         if ( goal_vel.y > 0.0f )  {
954                                 if ( goal_vel.y >= pi->prev_ramp_vel.y ) 
955                                         ramp_time_const = pi->slide_accel_time_const;
956                                 else
957                                         ramp_time_const = pi->slide_decel_time_const;
958                         } else  {  // goal_vel.y <= 0.0
959                                 if ( goal_vel.y <= pi->prev_ramp_vel.y )
960                                         ramp_time_const = pi->slide_accel_time_const;
961                                 else
962                                         ramp_time_const = pi->slide_decel_time_const;
963                         }
964                         // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast
965                         if ( pi->flags & PF_REDUCED_DAMP ) {
966                                 ramp_time_const *= reduced_damp_ramp_time_expansion;
967                         }
968                         pi->prev_ramp_vel.y = velocity_ramp( pi->prev_ramp_vel.y, goal_vel.y, ramp_time_const, sim_time);
969                 } else  {
970                         // slide not enabled
971                         pi->prev_ramp_vel.x = 0.0f;
972                         pi->prev_ramp_vel.y = 0.0f;
973                 }
974
975                 // find ramp velocity in the forward direction
976                 if ( goal_vel.z >= pi->prev_ramp_vel.z )  {
977                         if ( pi->flags & PF_AFTERBURNER_ON )
978                                 ramp_time_const = pi->afterburner_forward_accel_time_const;
979                         else
980                                 ramp_time_const = pi->forward_accel_time_const;
981                 } else
982                         ramp_time_const = pi->forward_decel_time_const;
983
984                 // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast
985                 if ( pi->flags & PF_REDUCED_DAMP ) {
986                         ramp_time_const *= reduced_damp_ramp_time_expansion;
987                 }
988                 pi->prev_ramp_vel.z = velocity_ramp( pi->prev_ramp_vel.z, goal_vel.z, ramp_time_const, sim_time);
989
990
991                 // this translates local desired velocities to world velocities
992
993                 vm_vec_zero(&pi->desired_vel);
994                 vm_vec_scale_add2( &pi->desired_vel, &orient->rvec, pi->prev_ramp_vel.x );
995                 vm_vec_scale_add2( &pi->desired_vel, &orient->uvec, pi->prev_ramp_vel.y );
996                 vm_vec_scale_add2( &pi->desired_vel, &orient->fvec, pi->prev_ramp_vel.z );
997         } else  // object does not accelerate  (PF_ACCELERATES not set)
998                 pi->desired_vel = pi->vel;
999 }
1000
1001
1002
1003 //      ----------------------------------------------------------------
1004 //      Do *dest = *delta unless:
1005 //                              *delta is pretty small
1006 //              and     they are of different signs.
1007 void physics_set_rotvel_and_saturate(float *dest, float delta)
1008 {
1009         /*
1010         if ((delta ^ *dest) < 0) {
1011                 if (abs(delta) < F1_0/8) {
1012                         // mprintf((0, "D"));
1013                         *dest = delta/4;
1014                 } else
1015                         // mprintf((0, "d"));
1016                         *dest = delta;
1017         } else {
1018                 // mprintf((0, "!"));
1019                 *dest = delta;
1020         }
1021         */
1022         *dest = delta;
1023 }
1024
1025
1026 // ----------------------------------------------------------------------------
1027 // physics_apply_whack applies an instaneous whack on an object changing
1028 // both the objects velocity and the rotational velocity based on the impulse
1029 // being applied.  
1030 //
1031 //      input:  impulse         =>              impulse vector ( force*time = impulse = change in momentum (mv) )
1032 //                              pos                     =>              vector from center of mass to location of where the force acts
1033 //                              pi                              =>              pointer to phys_info struct of object getting whacked
1034 //                              orient          =>              orientation matrix (needed to set rotational impulse in body coords)
1035 //                              mass                    =>              mass of the object (may be different from pi.mass if docked)
1036 //
1037 #define WHACK_LIMIT     0.001f
1038 #define ROTVEL_WHACK_CONST 0.12
1039 void physics_apply_whack(vector *impulse, vector *pos, physics_info *pi, matrix *orient, float mass)
1040 {
1041         vector  local_torque, torque;
1042 //      vector  npos;
1043
1044         //      Detect null vector.
1045         if ((fl_abs(impulse->x) <= WHACK_LIMIT) && (fl_abs(impulse->y) <= WHACK_LIMIT) && (fl_abs(impulse->z) <= WHACK_LIMIT))
1046                 return;
1047
1048         // first do the rotational velocity
1049         // calculate the torque on the body based on the point on the
1050         // object that was hit and the momentum being applied to the object
1051
1052         vm_vec_crossprod(&torque, pos, impulse);
1053         vm_vec_rotate ( &local_torque, &torque, orient );
1054
1055         vector delta_rotvel;
1056         vm_vec_rotate( &delta_rotvel, &local_torque, &pi->I_body_inv );
1057         vm_vec_scale ( &delta_rotvel, (float) ROTVEL_WHACK_CONST );
1058         vm_vec_add2( &pi->rotvel, &delta_rotvel );
1059
1060 #ifdef ROT_DEBUG
1061         if (check_rotvel_limit( pi )) {
1062                 nprintf(("Physics", "rotvel reset in physics_apply_whack\n"));
1063         }
1064 #endif
1065
1066         //mprintf(("Whack: %7.3f %7.3f %7.3f\n", pi->rotvel.x, pi->rotvel.y, pi->rotvel.z));
1067
1068         // instant whack on the velocity
1069         // reduce damping on all axes
1070         pi->flags |= PF_REDUCED_DAMP;
1071         update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) );
1072
1073         // find time for shake from weapon to end
1074         int dtime = timestamp_until(pi->afterburner_decay);
1075         if (dtime < WEAPON_SHAKE_TIME) {
1076                 pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME );
1077         }
1078
1079         vm_vec_scale_add2( &pi->vel, impulse, 1.0f / pi->mass );
1080         if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) {
1081                 // Get DaveA
1082                 nprintf(("Physics", "speed reset in physics_apply_whack [speed: %f]\n", vm_vec_mag(&pi->vel)));
1083 //              Int3();
1084                 vm_vec_normalize(&pi->vel);
1085                 vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED);
1086         }
1087         vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient );          // set so velocity will ramp starting from current speed
1088                                                                                                                                                                         // ramped velocity is now affected by collision
1089 }
1090
1091 // function generates a velocity ramp with a given time constant independent of frame rate
1092 // uses an exponential approach to desired velocity and a cheat when close to improve closure speed
1093 float velocity_ramp (float v_in, float v_goal, float ramp_time_const, float t)
1094 {
1095         float delta_v;
1096         float decay_factor;
1097         float dist;
1098
1099         // JAS: If no time elapsed, change nothing
1100         if ( t==0.0f )
1101                 return v_in;
1102
1103         delta_v = v_goal - v_in;
1104         dist = (float)fl_abs(delta_v);
1105
1106         // hack to speed up closure when close to goal
1107         if (dist < ramp_time_const/3)
1108                 ramp_time_const = dist / 3;
1109
1110         // Rather than get a divide by zero, just go to the goal
1111         if ( ramp_time_const < 0.0001f )        {
1112                 return v_goal;
1113         }
1114
1115         // determine decay factor  (ranges from 0 for short times to 1 for long times)
1116         // when decay factor is near 0, the velocity in nearly unchanged
1117         // when decay factor in near 1, the velocity approaches goal
1118         decay_factor = (float)exp(- t / ramp_time_const);
1119
1120         return (v_in + delta_v * (1.0f - decay_factor) );
1121 }
1122
1123
1124 // ----------------------------------------------------------------------------
1125 // physics_apply_shock applies applies a shockwave to an object.  This causes a velocity impulse and 
1126 // and a rotational impulse.  This is different than physics_apply_whack since a shock wave is a pressure
1127 // wave which acts over the *surface* of the object, not a point.
1128 //
1129 // inputs:      direction_vec           =>              a position vector whose direction is from the center of the shock wave to the object
1130 //                              pressure                                =>              the pressure of the shock wave at the object
1131 //                              pi                                              =>              physics_info structure
1132 //                              orient                          =>              matrix orientation of the object
1133 //                              min                                     =>              vector of minimum values of the bounding box
1134 //                              max                                     =>              vector of maximum values of the bounding box
1135 //                              radius                          =>              bounding box radius of the object, used for scaling rotation
1136 //
1137 // outputs:     makes changes to physics_info structure rotvel and vel variables
1138 //                              
1139 #define STD_PRESSURE            1000            // amplitude of standard shockwave blasts
1140 #define MIN_RADIUS                      10                      // radius within which full rotvel and shake applied
1141 #define MAX_RADIUS                      50                      // radius at which no rotvel or shake applied
1142 #define MAX_ROTVEL                      0.4             // max rotational velocity
1143 #define MAX_SHAKE                       0.1             // max rotational amplitude of shake
1144 #define MAX_VEL                         8                       // max vel from shockwave 
1145 void physics_apply_shock(vector *direction_vec, float pressure, physics_info *pi, matrix *orient, vector *min, vector *max, float radius)
1146 {
1147         vector normal;
1148         vector local_torque, temp_torque, torque;
1149         vector impact_vec;
1150         vector area;
1151         vector sin;
1152
1153         if (radius > MAX_RADIUS) {
1154                 return;
1155         }
1156
1157         vm_vec_normalize_safe ( direction_vec );
1158
1159         area.x = (max->y - min->z) * (max->z - min->z);
1160         area.y = (max->x - min->x) * (max->z - min->z);
1161         area.z = (max->x - min->x) * (max->y - min->y);
1162
1163         normal.x = vm_vec_dotprod( direction_vec, &orient->rvec );
1164         normal.y = vm_vec_dotprod( direction_vec, &orient->uvec );
1165         normal.z = vm_vec_dotprod( direction_vec, &orient->fvec );
1166
1167         sin.x = fl_sqrt( fl_abs(1.0f - normal.x*normal.x) );
1168         sin.y = fl_sqrt( fl_abs(1.0f - normal.y*normal.y) );
1169         sin.z = fl_sqrt( fl_abs(1.0f - normal.z*normal.z) );    
1170
1171         vm_vec_make( &torque, 0.0f, 0.0f, 0.0f );
1172
1173         // find the torque exerted due to the shockwave hitting each face
1174         //  model the effect of the shockwave as if the shockwave were a plane of projectiles,
1175         //  all moving in the direction direction_vec.  then find the torque as the cross prod
1176         //  of the force (pressure * area * normal * sin * scale * mass)
1177         //  normal takes account the fraction of the surface exposed to the shockwave
1178         //  the sin term is not technically needed but "feels" better
1179         //  scale factors out the increase in area with larger objects
1180         //  more massive objects get less rotation
1181
1182         // find torque due to forces on the right/left face
1183         if ( normal.x < 0.0f )          // normal < 0, hits the right face
1184                 vm_vec_copy_scale( &impact_vec, &orient->rvec, max->x * pressure * area.x *  normal.x * sin.x / pi->mass );
1185         else                                                            // normal > 0, hits the left face
1186                 vm_vec_copy_scale( &impact_vec, &orient->rvec, min->x * pressure * area.x * -normal.x * sin.x / pi->mass );
1187
1188         vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec );
1189         vm_vec_add2( &torque, &temp_torque );
1190
1191         // find torque due to forces on the up/down face
1192         if ( normal.y < 0.0f )
1193                 vm_vec_copy_scale( &impact_vec, &orient->uvec, max->y * pressure * area.y *  normal.y * sin.y / pi->mass );
1194         else
1195                 vm_vec_copy_scale( &impact_vec, &orient->uvec, min->y * pressure * area.y * -normal.y * sin.y / pi->mass );
1196
1197         vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec );
1198         vm_vec_add2( &torque, &temp_torque );
1199
1200         // find torque due to forces on the forward/backward face
1201         if ( normal.z < 0.0f )
1202                 vm_vec_copy_scale( &impact_vec, &orient->fvec, max->z * pressure * area.z *  normal.z * sin.z / pi->mass );
1203         else
1204                 vm_vec_copy_scale( &impact_vec, &orient->fvec, min->z * pressure * area.z * -normal.z * sin.z / pi->mass );
1205
1206         vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec );
1207         vm_vec_add2( &torque, &temp_torque );
1208
1209         // compute delta rotvel, scale according to blast and radius
1210         float scale;
1211         vector delta_rotvel;
1212         vm_vec_rotate( &local_torque, &torque, orient );
1213         vm_vec_copy_normalize(&delta_rotvel, &local_torque);
1214         if (radius < MIN_RADIUS) {
1215                 scale = 1.0f;
1216         } else {
1217                 scale = (MAX_RADIUS - radius)/(MAX_RADIUS-MIN_RADIUS);
1218         }
1219         vm_vec_scale(&delta_rotvel, (float)(MAX_ROTVEL*(pressure/STD_PRESSURE)*scale));
1220         // nprintf(("Physics", "rotvel scale %f\n", (MAX_ROTVEL*(pressure/STD_PRESSURE)*scale)));
1221         vm_vec_add2(&pi->rotvel, &delta_rotvel);
1222
1223         // set shockwave shake amplitude, duration, flag
1224         pi->shockwave_shake_amp = (float)(MAX_SHAKE*(pressure/STD_PRESSURE)*scale);
1225         pi->shockwave_decay = timestamp( SW_BLAST_DURATION );
1226         pi->flags |= PF_IN_SHOCKWAVE;
1227
1228         // set reduced translational damping, set flags
1229         float velocity_scale = (float)MAX_VEL*scale;
1230         pi->flags |= PF_REDUCED_DAMP;
1231         update_reduced_damp_timestamp( pi, velocity_scale*pi->mass );
1232         vm_vec_scale_add2( &pi->vel, direction_vec, velocity_scale );
1233         vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient);    // set so velocity will ramp starting from current speed
1234
1235         // check that kick from shockwave is not too large
1236         if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) {
1237                 // Get DaveA
1238                 nprintf(("Physics", "speed reset in physics_apply_shock [speed: %f]\n", vm_vec_mag(&pi->vel)));
1239 //              Int3();
1240                 vm_vec_normalize(&pi->vel);
1241                 vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED);
1242         }
1243
1244 #ifdef ROT_DEBUG
1245         if (check_rotvel_limit( pi )) {
1246                 nprintf(("Physics", "rotvel reset in physics_apply_shock\n"));
1247         }
1248 #endif
1249
1250                                                                                                                                                                 // ramped velocity is now affected by collision
1251 }
1252
1253 // ----------------------------------------------------------------------------
1254 // physics_collide_whack applies an instaneous whack on an object changing
1255 // both the objects velocity and the rotational velocity based on the impulse
1256 // being applied.  
1257 //
1258 //      input:  impulse                                 =>              impulse vector ( force*time = impulse = change in momentum (mv) )
1259 //                              world_delta_rotvel      =>              change in rotational velocity (already calculated)
1260 //                              pi                                                      =>              pointer to phys_info struct of object getting whacked
1261 //                              orient                                  =>              orientation matrix (needed to set rotational impulse in body coords)
1262 //
1263
1264 // Warning:  Do not change ROTVEL_COLLIDE_WHACK_CONST.  This will mess up collision physics.
1265 // If you need to change the rotation, change  COLLISION_ROTATION_FACTOR in collide_ship_ship.
1266 #define ROTVEL_COLLIDE_WHACK_CONST 1.0
1267 void physics_collide_whack( vector *impulse, vector *world_delta_rotvel, physics_info *pi, matrix *orient )
1268 {
1269         vector  body_delta_rotvel;
1270
1271         //      Detect null vector.
1272         if ((fl_abs(impulse->x) <= WHACK_LIMIT) && (fl_abs(impulse->y) <= WHACK_LIMIT) && (fl_abs(impulse->z) <= WHACK_LIMIT))
1273                 return;
1274
1275         vm_vec_rotate( &body_delta_rotvel, world_delta_rotvel, orient );
1276 //      vm_vec_scale( &body_delta_rotvel, (float)       ROTVEL_COLLIDE_WHACK_CONST );
1277         vm_vec_add2( &pi->rotvel, &body_delta_rotvel );
1278
1279 #ifdef ROT_DEBUG
1280         if (check_rotvel_limit( pi )) {
1281                 nprintf(("Physics", "rotvel reset in physics_collide_whack\n"));
1282         }
1283 #endif
1284
1285         update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) );
1286
1287         // find time for shake from weapon to end
1288         int dtime = timestamp_until(pi->afterburner_decay);
1289         if (dtime < WEAPON_SHAKE_TIME) {
1290                 pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME );
1291         }
1292
1293         pi->flags |= PF_REDUCED_DAMP;
1294         vm_vec_scale_add2( &pi->vel, impulse, 1.0f / pi->mass );
1295         // check that collision does not give ship too much speed
1296         // reset if too high
1297         if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) {
1298                 // Get DaveA
1299                 nprintf(("Physics", "speed reset in physics_collide_whack [speed: %f]\n", vm_vec_mag(&pi->vel)));
1300 //              Int3();
1301                 vm_vec_normalize(&pi->vel);
1302                 vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED);
1303         }
1304         vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient );          // set so velocity will ramp starting from current speed
1305                                                                                                                                                                         // ramped velocity is now affected by collision
1306         // rotate previous ramp velocity (in model coord) to be same as vel (in world coords)
1307 }
1308
1309
1310 int check_rotvel_limit( physics_info *pi )
1311 {
1312         if ( 0 == pi->flags )           // weapon
1313                 return 0;
1314
1315         if ( Fred_running )
1316                 return 0;
1317
1318         int change_made = 0;
1319         if ( !(pi->flags & PF_DEAD_DAMP) ) {
1320                 // case of normal, live ship
1321                 // -- Commented out by MK: Assert( vm_vec_mag_squared(&pi->max_rotvel) > ROTVEL_TOL );
1322                 // Assert( (pi->max_rotvel.x <= ROTVEL_CAP) && (pi->max_rotvel.y <= ROTVEL_CAP) && (pi->max_rotvel.z <= ROTVEL_CAP) );
1323                 //              Warning(LOCATION,"Excessive rotvel (wx: %f, wy: %f, wz:%f)\n", pi->rotvel.x, pi->rotvel.y, pi->rotvel.z);
1324                 if ( fl_abs(pi->rotvel.x) > pi->max_rotvel.x ) {
1325                         pi->rotvel.x = (pi->rotvel.x / fl_abs(pi->rotvel.x)) * (pi->max_rotvel.x - (float) ROTVEL_TOL);
1326                         change_made = 1;
1327                 }
1328                 if ( fl_abs(pi->rotvel.y) > pi->max_rotvel.y ) {
1329                         pi->rotvel.y = (pi->rotvel.y / fl_abs(pi->rotvel.y)) * (pi->max_rotvel.y - (float) ROTVEL_TOL);
1330                         change_made = 1;
1331                 }
1332                 if ( fl_abs(pi->rotvel.z) > pi->max_rotvel.z ) {
1333                         pi->rotvel.z = (pi->rotvel.z / fl_abs(pi->rotvel.z)) * (pi->max_rotvel.z - (float) ROTVEL_TOL);
1334                         change_made = 1;
1335                 }
1336         } else { 
1337                 // case of dead ship
1338                 if ( fl_abs(pi->rotvel.x) > DEAD_ROTVEL_CAP ) {
1339                         pi->rotvel.x = (pi->rotvel.x / fl_abs(pi->rotvel.x)) * (float) (DEAD_ROTVEL_CAP - ROTVEL_TOL);
1340                         change_made = 1;
1341                 }
1342                 if ( fl_abs(pi->rotvel.y) > DEAD_ROTVEL_CAP ) {
1343                         pi->rotvel.y = (pi->rotvel.y / fl_abs(pi->rotvel.y)) * (float) (DEAD_ROTVEL_CAP - ROTVEL_TOL);
1344                         change_made = 1;
1345                 }
1346                 if ( fl_abs(pi->rotvel.z) > DEAD_ROTVEL_CAP ) {
1347                         pi->rotvel.z = (pi->rotvel.z / fl_abs(pi->rotvel.z)) * (float) (DEAD_ROTVEL_CAP - ROTVEL_TOL);
1348                         change_made = 1;
1349                 }
1350         }
1351         return change_made;
1352 }
1353
1354 // ----------------------------------------------------------------------------
1355 // update_reduced_damp_timestamp()
1356 //
1357 void update_reduced_damp_timestamp( physics_info *pi, float impulse )
1358 {
1359
1360         // Compute duration of reduced damp from present
1361         // Compare with current value and increase if greater, otherwise ignore
1362         int reduced_damp_decay_time;
1363         reduced_damp_decay_time = (int) (REDUCED_DAMP_TIME * impulse / (REDUCED_DAMP_VEL * pi->mass));
1364         if ( reduced_damp_decay_time > REDUCED_DAMP_TIME )
1365                 reduced_damp_decay_time = REDUCED_DAMP_TIME;
1366
1367         // Reset timestamp if larger than current (if any)
1368         if ( timestamp_valid( pi->reduced_damp_decay ) ) {
1369                 int time_left = timestamp_until( pi->reduced_damp_decay );
1370                 if ( time_left > 0 ) {
1371                         // increment old time, but apply cap
1372                         int new_time = reduced_damp_decay_time + time_left;
1373                         if ( new_time < REDUCED_DAMP_TIME ) {
1374                                 pi->reduced_damp_decay = timestamp( new_time );
1375                         }
1376                 } else {
1377                         pi->reduced_damp_decay = timestamp( reduced_damp_decay_time );
1378                 }
1379         } else {
1380                 // set if not valid
1381                 pi->reduced_damp_decay = timestamp( reduced_damp_decay_time );
1382         }
1383
1384 }
1385