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