]> icculus.org git repositories - taylor/freespace2.git/blob - src/object/objcollide.cpp
added copyright header
[taylor/freespace2.git] / src / object / objcollide.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/Object/ObjCollide.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Helper routines for all the collision detection functions
16  * Also keeps track of all the object pairs.
17  *
18  * $Log$
19  * Revision 1.3  2002/06/09 04:41:24  relnev
20  * added copyright header
21  *
22  * Revision 1.2  2002/05/07 03:16:48  theoddone33
23  * The Great Newline Fix
24  *
25  * Revision 1.1.1.1  2002/05/03 03:28:10  root
26  * Initial import.
27  *
28  * 
29  * 18    7/15/99 9:20a Andsager
30  * FS2_DEMO initial checkin
31  * 
32  * 17    6/14/99 3:21p Andsager
33  * Allow collisions between ship and its debris.  Fix up collision pairs
34  * when large ship is warping out.
35  * 
36  * 16    5/08/99 8:25p Dave
37  * Upped object pairs. First run of nebula lightning.
38  * 
39  * 15    4/23/99 12:01p Johnson
40  * Added SIF_HUGE_SHIP
41  * 
42  * 14    4/21/99 6:15p Dave
43  * Did some serious housecleaning in the beam code. Made it ready to go
44  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
45  * a handy macro for recalculating collision pairs for a given object.
46  * 
47  * 13    4/20/99 3:41p Andsager
48  * Modify objects_will_collide to use endpoints
49  * 
50  * 12    3/10/99 6:50p Dave
51  * Changed the way we buffer packets for all clients. Optimized turret
52  * fired packets. Did some weapon firing optimizations.
53  * 
54  * 11    3/05/99 3:32p Dan
55  * Make out of collision pars message npirntf with "collision"
56  * 
57  * 10    1/21/99 2:06p Dave
58  * Final checkin for multiplayer testing.
59  * 
60  * 9     1/21/99 10:44a Dave
61  * More beam weapon stuff. Put in warmdown time.
62  * 
63  * 8     1/15/99 5:01p Dave
64  * Fix for object collision problem.
65  * 
66  * 7     1/12/99 5:45p Dave
67  * Moved weapon pipeline in multiplayer to almost exclusively client side.
68  * Very good results. Bandwidth goes down, playability goes up for crappy
69  * connections. Fixed object update problem for ship subsystems.
70  * 
71  * 6     1/12/99 12:53a Dave
72  * More work on beam weapons - made collision detection very efficient -
73  * collide against all object types properly - made 3 movement types
74  * smooth. Put in test code to check for possible non-darkening pixels on
75  * object textures.
76  * 
77  * 5     11/19/98 11:08p Andsager
78  * Check in of physics and collision detection of rotating submodels
79  * 
80  * 4     11/05/98 5:55p Dave
81  * Big pass at reducing #includes
82  * 
83  * 3     10/26/98 9:42a Dave
84  * Early flak gun support.
85  * 
86  * 2     10/07/98 10:53a Dave
87  * Initial checkin.
88  * 
89  * 1     10/07/98 10:50a Dave
90  * 
91  * 38    7/29/98 3:24p Allender
92  * don't call collision code when either of the object pairs is NULL.
93  * 
94  * 37    4/08/98 8:38a Mike
95  * Make ships avoid player when attacking or guarding a faraway object.
96  * 
97  * 36    4/02/98 11:40a Lawrance
98  * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE
99  * 
100  * 35    4/01/98 1:48p Allender
101  * major changes to ship collision in multiplayer.  Clients now do own
102  * ship/ship collisions (with their own ship only)  Modifed the hull
103  * update packet to be sent quicker when object is target of player.
104  * 
105  * 34    4/01/98 10:57a Mike
106  * Reduce array sizes to save memory.
107  * 
108  * 33    3/31/98 5:18p John
109  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
110  * bunch of debug stuff out of player file.  Made model code be able to
111  * unload models and malloc out only however many models are needed.
112  *  
113  * 
114  * 32    3/06/98 12:45a Lawrance
115  * Rmove a bogus assert in the object pair save/restore
116  * 
117  * 31    3/05/98 3:12p Mike
118  * Fix bugs in asteroid collisions.  Throw asteroids at Galataea (needs to
119  * be made general).  Add skill level support for asteroid throwing.
120  * 
121  * 30    3/05/98 12:12a Mike
122  * Make an asteroid in the way prevent player warpout.
123  * 
124  * 29    2/27/98 4:48p John
125  * Made objects keep track of number of pairs they have associated with
126  * them.  Then, I can early out of the obj_remove_all which was 2.5% of
127  * frametime at beginning of sm2-2 which then is 0% after this.
128  * 
129  * 28    2/26/98 10:07p Hoffoss
130  * Rewrote state saving and restoring to fix bugs and simplify the code.
131  * 
132  * 27    2/23/98 9:00a Andsager
133  * improved back face culling of object:laser pairs to check relative
134  * velocity dot laser forward < 0 vs. v_other dot v_laser < 0
135  * 
136  * 26    2/19/98 4:34p Lawrance
137  * Allow weapon-asteroid collisions
138  * 
139  * 25    2/19/98 10:52a Sandeep
140  * AL: Have asteroids collide with all ships
141  * 
142  * 24    2/12/98 11:45a Andsager
143  * Try to cull debris:weapon collisions before they collisoin pair is
144  * created.  Look at movement in lifetime and whether debris is behind
145  * laser.
146  * 
147  * 23    2/07/98 3:54p John
148  * Added FVI to show_game_resources
149  * 
150  * 22    2/06/98 7:29p John
151  * Made only players ships and player ship weapons do collisions with
152  * asteroids.
153  * 
154  * 21    2/05/98 11:49p Andsager
155  * Don't check laser:ship collisions on same team for small ships if laser
156  * source not player
157  * 
158  * 20    2/05/98 9:21p John
159  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
160  * game.
161  * 
162  * 19    2/05/98 12:51a Mike
163  * Early asteroid stuff.
164  * 
165  * 18    2/03/98 9:40a Andsager
166  * Modify collision object pair creation code so that pairs between debris
167  * and weapons (other than player) are not created.
168  * 
169  * 17    1/28/98 11:06a Andsager
170  * Remove some collision pairs.  Add debug code for displaying collision
171  * pair info.  
172  * 
173  * 16    1/24/98 11:16a Andsager
174  * Remove debug  info.
175  * 
176  * 15    1/24/98 11:09a Andsager
177  * Fixed bug that was removing collision pairs when weapon within the
178  * radius of object.
179  * 
180  * 14    1/23/98 5:07p Andsager
181  * Added tests for object pairs.  (1)  for weapons that do not turn, find
182  * the soonest hit time.  (2)  Check if soonest hit time if after end of
183  * weapon lifetime.
184  * 
185  * 13    1/22/98 6:43p John
186  * Took out debug code.
187  * 
188  * 12    1/22/98 6:42p John
189  * Move game_flush out of debug console into freespace.     Made object
190  * pair code throw out some weapons that turn.   Added stats for how many
191  * object pair are actually checked.
192  * 
193  * 11    1/17/98 10:06p Lawrance
194  * For missiles, only collide if WIF_BOMB flag set.
195  * 
196  * 10    1/14/98 5:21p Allender
197  * system to delete object when buffer is nearly full.  System in place to
198  * delete weapons when nearly out of weapons slots
199  * 
200  * 9     1/13/98 8:09p John
201  * Removed the old collision system that checked all pairs.   Added code
202  * to disable collisions and particles.
203  * 
204  * 8     1/13/98 3:11p Allender
205  * new code to remove old weapons when no more weapon slots available.
206  * Currently not called anywhere pending further testing
207  * 
208  * 7     1/10/98 3:22p Allender
209  * temporary check in to get new code
210  * 
211  * 6     1/10/98 1:14p John
212  * Added explanation to debug console commands
213  * 
214  * 5     1/09/98 9:29a Mike
215  * Enable docked ships to warp out.  Make damage done to a ship not
216  * proportional to its own mass.
217  * 
218  * 4     1/08/98 12:12a Mike
219  * Make ships turn before warping out, if necessary, to avoid a collision.
220  * Warn player if his warpout will collide.  Abort if in stage1.
221  * 
222  * 3     12/21/97 4:33p John
223  * Made debug console functions a class that registers itself
224  * automatically, so you don't need to add the function to
225  * debugfunctions.cpp.  
226  * 
227  * 2     12/16/97 5:23p Allender
228  * don't deal with object collision paris on multiplayer clients
229  * 
230  * 1     9/17/97 2:14p John
231  * Initial revision
232  *
233  * $NoKeywords: $
234  */
235
236 #include "objcollide.h"
237 #include "linklist.h"
238 #include "systemvars.h"
239 #include "timer.h"
240 #include "multi.h"
241 #include "beam.h"
242
243 #ifdef FS2_DEMO
244         #define MAX_PAIRS 3000
245 #else
246         #define MAX_PAIRS 8000  //      Reduced from 10,000 to 6,000 by MK on 4/1/98.
247                                                                         //      Most I saw was 3400 in sm1-06a, the asteriod mission.  No other mission came close.
248 #endif
249
250 // the next 3 variables are used for pair statistics
251 // also in weapon.cpp there is Weapons_created.
252 int Pairs_created = 0;
253 int Num_pairs = 0;
254 int Num_pairs_checked = 0;
255 int pairs_not_created = 0;
256
257 int Num_pairs_hwm = 0;
258
259 obj_pair Obj_pairs[MAX_PAIRS];
260
261 obj_pair pair_used_list;
262 obj_pair pair_free_list;
263
264 void obj_reset_pairs()
265 {
266         int i;
267         
268 //      mprintf(( "Resetting object pairs...\n" ));
269
270         pair_used_list.a = pair_used_list.b = NULL;             
271         pair_used_list.next = NULL;
272         pair_free_list.a = pair_free_list.b = NULL;             
273         pair_free_list.next = &Obj_pairs[0];
274         
275         Num_pairs = 0;
276         for (i=0; i<MAX_PAIRS; i++ )    {
277                 Obj_pairs[i].a = Obj_pairs[i].b = NULL;
278                 Obj_pairs[i].next = &Obj_pairs[i+1];
279         }
280         Obj_pairs[MAX_PAIRS-1].next = NULL;
281 }
282
283 // returns true if we should reject object pair if one is child of other.
284 int reject_obj_pair_on_parent(object *A, object *B)
285 {
286         if (A->type == OBJ_SHIP) {
287                 if (B->type == OBJ_DEBRIS) {
288                         if (B->parent_sig == A->signature) {
289                                 return 0;
290                         }
291                 }
292         }
293
294         if (B->type == OBJ_SHIP) {
295                 if (A->type == OBJ_DEBRIS) {
296                         if (A->parent_sig == B->signature) {
297                                 return 0;
298                         }
299                 }
300         }
301
302         if (A->parent_sig == B->signature) {
303                 return 1;
304         }
305
306         if (B->parent_sig == A->signature) {
307                 return 1;
308         }
309
310         return 0;
311 }
312
313
314 // Adds the pair to the pair list
315 void obj_add_pair( object *A, object *B, int check_time, int add_to_end )
316 {
317         uint ctype;
318         int (*check_collision)( obj_pair *pair );
319         int swapped = 0;        
320         
321         check_collision = NULL;
322
323         if ( A==B ) return;             // Don't check collisions with yourself
324
325         if ( !(A->flags&OF_COLLIDES) ) return;          // This object doesn't collide with anything
326         if ( !(B->flags&OF_COLLIDES) ) return;          // This object doesn't collide with anything
327         
328         // Make sure you're not checking a parent with it's kid or vicy-versy
329 //      if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
330 //      if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
331         if ( reject_obj_pair_on_parent(A,B) ) {
332                 return;
333         }
334
335         Assert( A->type < 127 );
336         Assert( B->type < 127 );
337
338         ctype = COLLISION_OF(A->type,B->type);
339         switch( ctype ) {
340         case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP):
341                 swapped = 1;
342                 check_collision = collide_ship_weapon;
343                 break;
344         case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON):
345                 check_collision = collide_ship_weapon;
346                 break;
347         case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON):
348                 check_collision = collide_debris_weapon;
349                 break;
350         case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS):
351                 swapped = 1;
352                 check_collision = collide_debris_weapon;
353                 break;
354         case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP):
355                 check_collision = collide_debris_ship;          
356                 break;
357         case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS):
358                 check_collision = collide_debris_ship;
359                 swapped = 1;
360                 break;
361         case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON):
362                 // Only check collision's with player weapons
363 //              if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
364                         check_collision = collide_asteroid_weapon;
365 //              }
366                 break;
367         case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID):
368                 swapped = 1;
369                 // Only check collision's with player weapons
370 //              if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
371                         check_collision = collide_asteroid_weapon;
372 //              }
373                 break;
374         case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP):
375                 // Only check collisions with player ships
376 //              if ( B->flags & OF_PLAYER_SHIP )        {
377                         check_collision = collide_asteroid_ship;
378 //              }
379                 break;
380         case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID):
381                 // Only check collisions with player ships
382 //              if ( A->flags & OF_PLAYER_SHIP )        {
383                         check_collision = collide_asteroid_ship;
384 //              }
385                 swapped = 1;
386                 break;
387         case COLLISION_OF(OBJ_SHIP,OBJ_SHIP):
388                 check_collision = collide_ship_ship;
389                 break;  
390         
391         case COLLISION_OF(OBJ_BEAM, OBJ_SHIP):
392                 if(beam_collide_early_out(A, B)){
393                         return;
394                 }
395                 check_collision = beam_collide_ship;
396                 break;
397
398         case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID):
399                 if(beam_collide_early_out(A, B)){
400                         return;
401                 }
402                 check_collision = beam_collide_asteroid;
403                 break;
404
405         case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS):
406                 if(beam_collide_early_out(A, B)){
407                         return;
408                 }
409                 check_collision = beam_collide_debris;
410                 break;
411
412         case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON):
413                 if(beam_collide_early_out(A, B)){
414                         return;
415                 }               
416                 check_collision = beam_collide_missile;
417                 break;
418
419         case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): {
420                 weapon_info *awip, *bwip;
421                 awip = &Weapon_info[Weapons[A->instance].weapon_info_index];
422                 bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];
423                 
424                 if (awip->subtype != WP_LASER || bwip->subtype != WP_LASER) {
425                         if (awip->subtype == WP_LASER) {
426                                 if ( bwip->wi_flags & WIF_BOMB ) {
427                                         check_collision = collide_weapon_weapon;
428                                 }
429                         } else if (bwip->subtype == WP_LASER) {
430                                 if ( awip->wi_flags & WIF_BOMB ) {
431                                         check_collision = collide_weapon_weapon;
432                                         swapped=1;                      
433                                 }
434                         } else {
435                                 if ( (awip->wi_flags&WIF_BOMB) || (bwip->wi_flags&WIF_BOMB) ) {
436                                         check_collision = collide_weapon_weapon;
437                                 }
438                         }
439                 }
440 /*
441                 int     atype, btype;
442
443                 atype = Weapon_info[Weapons[A->instance].weapon_info_index].subtype;
444                 btype = Weapon_info[Weapons[B->instance].weapon_info_index].subtype;
445
446                 if ((atype == WP_LASER) && (btype == WP_MISSILE))
447                         check_collision = collide_weapon_weapon;
448                 else if ((atype == WP_MISSILE) && (btype == WP_LASER)) {
449                         check_collision = collide_weapon_weapon;
450                         swapped = 1;
451                 } else if ((atype == WP_MISSILE) && (btype == WP_MISSILE))
452                         check_collision = collide_weapon_weapon;
453 */
454
455                 break;
456         }
457
458         default:
459                 return;
460         }
461
462         // Swap them if needed
463         if ( swapped )  {
464                 object *tmp = A;
465                 A = B;
466                 B = tmp;
467         }
468
469         // if there are any more obj_pair checks
470         // we should then add function int maybe_not_add_obj_pair()
471         // MWA -- 4/1/98 -- I'd do it, but I don't want to bust anything, so I'm doing my stuff here instead :-)
472         //if ( MULTIPLAYER_CLIENT && !(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE)){
473                 // multiplayer clients will only do ship/ship collisions, and their own ship to boot
474         //      if ( check_collision != collide_ship_ship ){
475         //              return;
476         //      }
477
478         //      if ( (A != Player_obj) && (B != Player_obj) ){
479         //              return;
480         //      }
481         //}     
482
483         // only check debris:weapon collisions for player
484         if (check_collision == collide_debris_weapon) {
485                 // weapon is B
486                 if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
487                 // check for dumbfire weapon
488                         // check if debris is behind laser
489                         float vdot;
490                         if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) {
491                                 vector velocity_rel_weapon;
492                                 vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
493                                 vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.fvec);
494                         } else {
495                                 vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
496                         }
497                         if ( vdot <= 0.0f )     {
498                                 // They're heading in opposite directions...
499                                 // check their positions
500                                 vector weapon2other;
501                                 vm_vec_sub( &weapon2other, &A->pos, &B->pos );
502                                 float pdot = vm_vec_dot( &B->orient.fvec, &weapon2other );
503                                 if ( pdot <= -A->radius )       {
504                                         // The other object is behind the weapon by more than
505                                         // its radius, so it will never hit...
506                                         return;
507                                 }
508                         }
509
510                         // check dist vs. dist moved during weapon lifetime
511                         vector delta_v;
512                         vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
513                         if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) {
514                                 return;
515                         }
516
517                         // for nonplayer ships, only create collision pair if close enough
518                         if ( !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) )
519                                 return;
520                 }
521         }
522
523         // don't check same team laser:ship collisions on small ships if not player
524         if (check_collision == collide_ship_weapon) {
525                 // weapon is B
526                 if ( !(Objects[B->parent].flags & OF_PLAYER_SHIP)
527                         && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) 
528                         && (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) 
529                         && (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) {
530                         pairs_not_created++;
531                         return;
532                 }
533         }
534
535         if ( !check_collision ) return;
536         Pairs_created++;
537
538         // At this point, we have determined that collisions between
539         // these two should be checked, so add the pair to the
540         // collision pair list.
541
542         if ( pair_free_list.next == NULL )      {
543                 nprintf(( "collision", "Out of object pairs!! Not all collisions will work!\n" ));
544                 return;
545         }
546
547         // get a new obj_pair from the free list
548         obj_pair * new_pair = pair_free_list.next;
549         pair_free_list.next = new_pair->next;
550
551         if ( add_to_end ) {
552                 obj_pair *last, *tmp;
553
554                 last = tmp = pair_used_list.next;
555                 while( tmp != NULL )    {
556                         if ( tmp->next == NULL )
557                                 last = tmp;
558
559                         tmp = tmp->next;
560                 }
561
562                 if ( last == NULL )
563                         last = &pair_used_list;
564                         
565                 last->next = new_pair;
566                 Assert(new_pair != NULL);
567                 new_pair->next = NULL;
568         }
569         else {
570                 new_pair->next = pair_used_list.next;
571                 pair_used_list.next = new_pair;
572         }
573         
574         Num_pairs++;
575 /*      if (Num_pairs > Num_pairs_hwm) {
576                 Num_pairs_hwm = Num_pairs;
577                 //nprintf(("AI", "Num_pairs high water mark = %i\n", Num_pairs_hwm));
578         }
579 */
580
581         A->num_pairs++;
582         B->num_pairs++;
583         
584         new_pair->a = A;
585         new_pair->b = B;
586         new_pair->check_collision = check_collision;
587
588         if ( check_time == -1 ){
589                 new_pair->next_check_time = timestamp(0);       // 0 means instantly time out
590         } else {
591                 new_pair->next_check_time = check_time;
592         }
593
594 }
595
596 MONITOR(NumPairs);
597 MONITOR(NumPairsChecked);
598
599 void obj_check_all_collisions()
600 {
601         if ( !(Game_detail_flags & DETAIL_FLAG_COLLISION) )     return;
602
603         obj_pair *parent, *tmp; 
604         // debug info
605         float avg_time_to_next_check = 0.0f;
606                                                         
607         parent = &pair_used_list;
608         tmp = parent->next;
609
610         Num_pairs_checked = 0;
611
612         while( tmp != NULL )    {
613
614                 int removed = 0;
615                 if ( (tmp->a != NULL) && (tmp->b != NULL) && timestamp_elapsed(tmp->next_check_time) )  {
616                         Num_pairs_checked++;
617
618                         if ((*tmp->check_collision)(tmp))       {
619                                 // We never need to check this pair again.
620                                 #if 0   //def DONT_REMOVE_PAIRS
621                                         // Never check it again, but keep the pair around
622                                         // (useful for debugging)
623                                         tmp->next_check_time = timestamp(-1);           
624                                 #else
625                                         // Never check it again, so remove the pair
626                                         removed = 1;
627                                         tmp->a->num_pairs--;    
628                                         Assert( tmp->a->num_pairs > -1 );
629                                         tmp->b->num_pairs--;
630                                         Assert( tmp->b->num_pairs > -1 );
631                                         Num_pairs--;
632                                         // Assert(Num_pairs >= 0);
633                                         parent->next = tmp->next;
634                                         tmp->a = tmp->b = NULL;
635                                         tmp->next = pair_free_list.next;
636                                         pair_free_list.next = tmp;
637                                         tmp = parent->next;
638                                 #endif
639                         }
640                 } 
641
642                 if (!removed)   {
643                         parent = tmp;
644                         tmp = tmp->next;
645                         // debug info
646                         if (tmp) {
647                                 int add_time = timestamp_until( tmp->next_check_time );
648                                 if (add_time > 0)
649                                         avg_time_to_next_check += (float) add_time;
650                         }
651                 }
652         }
653
654         MONITOR_INC(NumPairs,Num_pairs);
655         MONITOR_INC(NumPairsChecked,Num_pairs_checked);
656
657         // #define PAIR_STATS
658         #ifdef PAIR_STATS
659         avg_time_to_next_check = avg_time_to_next_check / Num_pairs;
660         extern int Num_hull_pieces;
661         extern int Weapons_created;
662         // mprintf(( "[pairs checked: %d, start_pairs: %d, num obj: %d, avg next time: %f]\n", n, org_pairs, num_objects, avg_time_to_next_check ));
663         // mprintf(( "[Num_hull_pieces: %3d, Num_weapons_created: %3d, pairs_not_created: %3d, pairs_created: %3d, percent new saved: %9.5f]\n", Num_hull_pieces, Weapons_created, pairs_not_created, Pairs_created, 100.0f*(float)pairs_not_created/(float)(pairs_not_created + Pairs_created) ));
664          mprintf(( "[pairs_created: %3d, pairs_not_created: %3d, percent saved %6.3f]\n", Pairs_created, pairs_not_created, 100.0f*pairs_not_created/(Pairs_created+pairs_not_created) ));
665         pairs_not_created = 0;
666         Weapons_created = 0;
667         Pairs_created = 0;
668         #endif
669
670         // What percent of the pairs did we check?
671         // FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects.
672
673 //      if ( org_pairs > 1 )    {
674 //              Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs);
675 //      } else {
676 //              Object_checked_percentage = 0.0f;
677 //      }
678
679 }
680
681 //      See if two lines intersect by doing recursive subdivision.
682 //      Bails out if larger distance traveled is less than sum of radii + 1.0f.
683 int collide_subdivide(vector *p0, vector *p1, float prad, vector *q0, vector *q1, float qrad)
684 {
685         float   a_dist, b_dist, ab_dist;
686
687         a_dist = vm_vec_dist(p0, p1);
688         b_dist = vm_vec_dist(q0, q1);
689
690         ab_dist = vm_vec_dist(p1, q1);
691
692         //      See if their spheres intersect
693         if (ab_dist < a_dist + b_dist + prad + qrad) {
694                 if (ab_dist  < prad + qrad)
695                         return 1;
696                 else if (vm_vec_dist(p0, q0) < prad + qrad)
697                         return 1;
698                 else if (max(a_dist, b_dist) < prad + qrad + 1.0f)
699                         return 0;
700                 else {
701                         int     r1, r2 = 0;
702                         vector  pa, qa;
703
704                         vm_vec_avg(&pa, p0, p1);
705                         vm_vec_avg(&qa, q0, q1);
706                         r1 = collide_subdivide(p0, &pa, prad, q0, &qa, qrad);
707                         if (!r1)
708                                 r2 = collide_subdivide(&pa, p1, prad, &qa, q1, qrad);
709
710                         return r1 | r2;
711                 }
712         } else
713                 return 0;
714 }
715
716
717
718 //      Return true if object A is expected to collide with object B within time duration
719 //      For purposes of this check, the first object moves from current location to predicted
720 //      location.  The second object is assumed to be where it will be at time duration, NOT
721 //      where it currently is.
722 //      radius_scale is used to control the precision of the check.
723 //              If 0.0, then use polygon models to perform check, slow and accurate
724 //              If !0.0, then use as a scale on the radius of the objects.  1.0 is Descent style
725 //                      collisions.  Larger values can be used to be sloppy about the collisions which
726 //                      is useful if a moving object wants to prevent a collision.
727 int objects_will_collide(object *A, object *B, float duration, float radius_scale)
728 {
729         object  A_future;
730         vector  hitpos;
731
732
733         A_future = *A;
734         vm_vec_scale_add2(&A_future.pos, &A->phys_info.vel, duration);
735
736         if (radius_scale == 0.0f) {
737                 return ship_check_collision_fast(B, &A_future, &hitpos );
738         } else {
739                 float           size_A, size_B, dist, r;
740                 vector  nearest_point;
741
742                 size_A = A->radius * radius_scale;
743                 size_B = B->radius * radius_scale;
744
745                 //      If A is moving, check along vector.
746                 if (A->phys_info.speed != 0.0f) {
747                         r = find_nearest_point_on_line(&nearest_point, &A->pos, &A_future.pos, &B->pos);
748                         if (r < 0) {
749                                 nearest_point = A->pos;
750                         } else if (r > 1) {
751                                 nearest_point = A_future.pos;
752                         }
753                         dist = vm_vec_dist_quick(&B->pos, &nearest_point);
754                         return (dist < size_A + size_B);
755                 } else {
756                         return vm_vec_dist_quick(&B->pos, &A->pos) < size_A + size_B;
757                 }
758         }
759 }
760
761 //      Return true if the vector from *start_pos to *end_pos is within objp->radius*radius_scale of *objp
762 int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale)
763 {
764         float           dist, r;
765         vector  nearest_point;
766
767         r = find_nearest_point_on_line(&nearest_point, start_pos, end_pos, &objp->pos);
768         if ((r >= 0.0f) && (r <= 1.0f)) {
769                 dist = vm_vec_dist_quick(&objp->pos, &nearest_point);
770
771                 return (dist < objp->radius * radius_scale);
772         } else
773                 return 0;
774 }
775
776 // Returns TRUE if the weapon will never hit the other object.
777 // If it can it predicts how long until these two objects need
778 // to be checked and fills the time in in current_pair.
779 int weapon_will_never_hit( object *weapon, object *other, obj_pair * current_pair )
780 {
781
782         Assert( weapon->type == OBJ_WEAPON );
783
784 //      mprintf(( "Frame: %d,  Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair ));
785         
786
787         // Do some checks for weapons that don't turn
788         if ( !(Weapon_info[Weapons[weapon->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
789
790                 // This first check is to see if a weapon is behind an object, and they
791                 // are heading in opposite directions.   If so, we don't need to ever check     
792                 // them again.   This is only valid for weapons that don't turn. 
793
794                 float vdot;
795                 if (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) {
796                         vector velocity_rel_weapon;
797                         vm_vec_sub(&velocity_rel_weapon, &weapon->phys_info.vel, &other->phys_info.vel);
798                         vdot = -vm_vec_dot(&velocity_rel_weapon, &weapon->orient.fvec);
799                 } else {
800                         vdot = vm_vec_dot( &other->phys_info.vel, &weapon->phys_info.vel);
801                 }
802                 if ( vdot <= 0.0f )     {
803                         // They're heading in opposite directions...
804                         // check their positions
805                         vector weapon2other;
806                         vm_vec_sub( &weapon2other, &other->pos, &weapon->pos );
807                         float pdot = vm_vec_dot( &weapon->orient.fvec, &weapon2other );
808                         if ( pdot <= -other->radius )   {
809                                 // The other object is behind the weapon by more than
810                                 // its radius, so it will never hit...
811                                 return 1;
812                         }
813                 }
814
815                 // FUTURE ENHANCEMENT IDEAS 
816
817                 // Given a laser does it hit a slow or not moving object
818                 // in its life or the next n seconds?  We'd actually need to check the 
819                 // model for this.
820                                 
821         }
822
823
824         // This check doesn't care about orient, only looks at the maximum speed
825         // of the two objects, so it knows that in the next n seconds, they can't
826         // go further than some distance, so don't bother checking collisions for 
827         // that time.   This is very rough, but is so general that it works for
828         // everything and immidiately gets rid of a lot of cases.
829         
830         if ( current_pair )     {
831                 // Find the time it will take before these get within each others distances.
832                 // tmp->next_check_time = timestamp(500);
833                 //vector        max_vel;                        //maximum foward velocity in x,y,z
834
835                 float max_vel_weapon, max_vel_other;
836                 max_vel_weapon = weapon->phys_info.max_vel.z;
837                 max_vel_other = other->phys_info.max_vel.z;
838                 if (max_vel_other < 10.0f) {
839                         if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) {
840                                 // bump up velocity from collision
841                                 max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f;
842                         } else {
843                                 max_vel_other = 10.0f;          // object may move from collision
844                         }
845                 }
846
847                 // check weapon that does not turn against sphere expanding at ship maxvel
848                 // compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time
849                 // look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t  Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship 
850                 // Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides
851                 if ( !(Weapon_info[Weapons[weapon->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
852                         vector delta_x, laser_vel;
853                         float a,b,c, delta_x_dot_vl, delta_t;
854                         float root1, root2, root, earliest_time;
855
856                         vm_vec_sub( &delta_x, &weapon->pos, &other->pos );
857                         vm_vec_copy_scale( &laser_vel, &weapon->orient.fvec, max_vel_weapon );
858                         delta_t = (other->radius + 10.0f) / max_vel_other;              // time to get from center to radius of other obj
859                         delta_x_dot_vl = vm_vec_dotprod( &delta_x, &laser_vel );
860
861                         a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other;
862                         b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t);
863                         c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t;
864
865                         float discriminant = b*b - 4.0f*a*c;
866                         if ( discriminant < 0) {
867                                 // never hit
868                                 return 1;
869                         } else {
870                                 root = fl_sqrt( discriminant );
871                                 root1 = (-b + root) / (2.0f * a) * 1000.0f;     // get time in ms
872                                 root2 = (-b - root) / (2.0f * a) * 1000.0f;     // get time in ms
873                         }
874
875                         // find earliest positive time
876                         if ( root1 > root2 ) {
877                                 float temp = root1;
878                                 root1 = root2;
879                                 root2 = temp;
880                         }
881
882                         if (root1 > 0) {
883                                 earliest_time = root1;
884                         } else if (root2 > 0) {
885                                 // root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame
886                                 current_pair->next_check_time = timestamp(0);   // check next time
887                                 return 0;
888                         } else {
889                                 // both times negative, so never collides
890                                 return 1;
891                         }
892
893
894
895                         // check if possible collision occurs after weapon expires
896                         if ( earliest_time > 1000*Weapons[weapon->instance].lifeleft )
897                                 return 1;
898
899                         // Allow one worst case frametime to elapse (~5 fps)
900                         earliest_time -= 200.0f;
901
902                         if (earliest_time > 100) {
903                                 current_pair->next_check_time = timestamp( fl2i(earliest_time) );
904                                 return 0;
905                         } else {
906                                 current_pair->next_check_time = timestamp(0);   // check next time
907                                 return 0;
908                         }
909
910                 } else {
911
912                         float dist, max_vel, time;
913
914                         max_vel = max_vel_weapon + max_vel_other;
915
916                         // suggest that fudge factor for other radius be changed to other_radius + const (~10)
917                         dist = vm_vec_dist( &other->pos, &weapon->pos ) - (other->radius + 10.0f);
918                         if ( dist > 0.0f )      {
919                                 time = (dist*1000.0f) / max_vel;
920                                 int time_ms = fl2i(time);
921
922                                 // check if possible collision occurs after weapon expires
923                                 if ( time_ms > 1000*Weapons[weapon->instance].lifeleft )
924                                         return 1;
925
926                                 time_ms -= 200; // Allow at least one worst case frametime to elapse (~5 fps)
927                                                 
928                                 if ( time_ms > 100 )    {               // If it takes longer than 1/10th of a second, then delay it
929                                         current_pair->next_check_time = timestamp(time_ms);
930                                         //mprintf(( "Delaying %d ms\n", time_ms ));
931                                         return 0;
932                                 }
933                         }
934                         current_pair->next_check_time = timestamp(0);   // check next time
935
936                 }
937         }
938
939         return 0;
940 }
941
942 //      Return true if vector from *curpos to *goalpos intersects with object *goalobjp
943 //      Else, return false.
944 //      radius is radius of object moving from curpos to goalpos.
945 int pp_collide(vector *curpos, vector *goalpos, object *goalobjp, float radius)
946 {
947         mc_info mc;
948
949         Assert(goalobjp->type == OBJ_SHIP);
950
951         ship_model_start(goalobjp);
952
953         mc.model_num = Ships[goalobjp->instance].modelnum;                      // Fill in the model to check
954         mc.orient = &goalobjp->orient;  // The object's orient
955         mc.pos = &goalobjp->pos;                        // The object's position
956         mc.p0 = curpos;                                 // Point 1 of ray to check
957         mc.p1 = goalpos;                                        // Point 2 of ray to check
958         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
959         mc.radius = radius;
960
961         model_collide(&mc);
962
963         ship_model_stop(goalobjp);
964
965         return mc.num_hits;
966 }
967
968 //      Setup and call pp_collide for collide_predict_large_ship
969 //      Returns true if objp will collide with objp2 before it reaches goal_pos.
970 int cpls_aux(vector *goal_pos, object *objp2, object *objp)
971 {
972         float   radius;
973         
974         radius = objp->radius;
975         if (1.5f * radius < 70.0f)
976                 radius *= 1.5f;
977         else
978                 radius = 70.0f;
979
980         if (pp_collide(&objp->pos, goal_pos, objp2, radius))
981                 return 1;
982         else
983                 return 0;
984 }
985
986 //      Return true if objp will collide with some large object.
987 //      Don't check for an object this ship is docked to.
988 int collide_predict_large_ship(object *objp, float distance)
989 {
990         object  *objp2;
991         vector  cur_pos, goal_pos;
992         object  *dock_objp;
993         ship_info       *sip;
994
995         sip = &Ship_info[Ships[objp->instance].ship_info_index];
996
997         dock_objp = NULL;
998         if (objp->type == OBJ_SHIP) {
999                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
1000                 if (aip->dock_objnum != -1)
1001                         dock_objp = &Objects[aip->dock_objnum];
1002         }
1003
1004         cur_pos = objp->pos;
1005
1006         vm_vec_scale_add(&goal_pos, &cur_pos, &objp->orient.fvec, distance);
1007
1008         for ( objp2 = GET_FIRST(&obj_used_list); objp2 != END_OF_LIST(&obj_used_list); objp2 = GET_NEXT(objp2) ) {
1009                 if ((objp != objp2) && (objp2->type == OBJ_SHIP)) {
1010                         if (Ship_info[Ships[objp2->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1011                                 if (objp2 == dock_objp)
1012                                         continue;
1013
1014                                 if (cpls_aux(&goal_pos, objp2, objp))
1015                                         return 1;
1016                         }
1017                 } else if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (objp2->type == OBJ_ASTEROID)) {
1018                         if (vm_vec_dist_quick(&objp2->pos, &objp->pos) < (distance + objp2->radius)*2.5f) {
1019                                 vector  pos, delvec;
1020                                 int             count;
1021                                 float           d1;
1022
1023                                 d1 = 2.5f * distance + objp2->radius;
1024                                 count = (int) (d1/(objp2->radius + objp->radius));      //      Scale up distance, else looks like there would be a collision.
1025                                 pos = cur_pos;
1026                                 vm_vec_normalized_dir(&delvec, &goal_pos, &cur_pos);
1027                                 vm_vec_scale(&delvec, d1/count);
1028
1029                                 for (; count>0; count--) {
1030                                         if (vm_vec_dist_quick(&pos, &objp2->pos) < objp->radius + objp2->radius)
1031                                                 return 1;
1032                                         vm_vec_add2(&pos, &delvec);
1033                                 }
1034                         }
1035                 }
1036         }
1037
1038         return 0;
1039 }
1040
1041 // function to iterate through all object collision pairs looking for weapons
1042 // which could be deleted since they are not going to hit anything.  Passed into this
1043 // function is a 'time' parameter used as watermark for which weapons to check.
1044
1045 #define CRW_NO_OBJECT           -1
1046 #define CRW_NO_PAIR                     0
1047 #define CRW_IN_PAIR                     1
1048 #define CRW_CAN_DELETE          2
1049
1050 #define CRW_MAX_TO_DELETE       4
1051
1052 char crw_status[MAX_WEAPONS];
1053
1054 void crw_check_weapon( int weapon_num, int collide_next_check )
1055 {
1056         float next_check_time;
1057         weapon *wp;
1058
1059         wp = &Weapons[weapon_num];
1060
1061         // if this weapons life left > time before next collision, then we cannot remove it
1062         crw_status[WEAPON_INDEX(wp)] = CRW_IN_PAIR;
1063         next_check_time = ((float)(timestamp_until(collide_next_check)) / 1000.0f);
1064         if ( wp->lifeleft < next_check_time )
1065                 crw_status[WEAPON_INDEX(wp)] = CRW_CAN_DELETE;
1066 }
1067
1068 int collide_remove_weapons( )
1069 {
1070         obj_pair *opp;
1071         int i, num_deleted, oldest_index, j, loop_count;
1072         float oldest_time;
1073
1074         // setup remove_weapon array.  assume we can remove it.
1075         for (i = 0; i < MAX_WEAPONS; i++ ) {
1076                 if ( Weapons[i].objnum == -1 )
1077                         crw_status[i] = CRW_NO_OBJECT;
1078                 else
1079                         crw_status[i] = CRW_NO_PAIR;
1080         }
1081
1082         // first pass is to see if any of the weapons don't have collision pairs.
1083
1084         opp = &pair_used_list;
1085         opp = opp->next;
1086         while( opp != NULL )    {
1087                 // for each collide pair, if the two objects can still collide, then set the remove_weapon
1088                 // parameter for the weapon to 0.  need to check both parameters
1089                 if ( opp->a->type == OBJ_WEAPON )
1090                         crw_check_weapon( opp->a->instance, opp->next_check_time );
1091
1092                 if ( opp->b->type == OBJ_WEAPON )
1093                         crw_check_weapon( opp->b->instance, opp->next_check_time );
1094
1095                 opp = opp->next;
1096         }
1097
1098         // for each weapon which could be removed, delete the object
1099         num_deleted = 0;
1100         for ( i = 0; i < MAX_WEAPONS; i++ ) {
1101                 if ( crw_status[i] == CRW_CAN_DELETE ) {
1102                         Assert( Weapons[i].objnum != -1 );
1103                         obj_delete( Weapons[i].objnum );
1104                         num_deleted++;
1105                 }
1106         }
1107
1108         if ( num_deleted )
1109                 return num_deleted;
1110
1111         // if we didn't remove any weapons, try to the N oldest weapons.  first checking for pairs, then
1112         // checking for oldest weapons in general.  We will go through the loop a max of 2 times.  first time
1113         // through, we check oldest weapons with pairs, next time through, for oldest weapons.
1114         loop_count = 0;
1115         do {
1116                 for ( j = 0; j < CRW_MAX_TO_DELETE; j++ ) {
1117                         oldest_time = 1000.0f;
1118                         oldest_index = -1;
1119                         for (i = 0; i < MAX_WEAPONS; i++ ) {
1120                                 if ( Weapons[i].objnum == -1 )                  // shouldn't happen, but this is the safe thing to do.
1121                                         continue;
1122                                 if ( ((loop_count || crw_status[i] == CRW_NO_PAIR)) && (Weapons[i].lifeleft < oldest_time) ) {
1123                                         oldest_time = Weapons[i].lifeleft;
1124                                         oldest_index = i;
1125                                 }
1126                         }
1127                         if ( oldest_index != -1 ) {
1128                                 obj_delete(Weapons[oldest_index].objnum);
1129                                 num_deleted++;
1130                         }
1131                 }
1132
1133                 // if we deleted some weapons, then we can break
1134                 if ( num_deleted )
1135                         break;
1136
1137                 loop_count++;
1138         } while ( loop_count < 2);
1139
1140         return num_deleted;
1141
1142 }
1143
1144 void set_hit_struct_info(collision_info_struct *hit, mc_info *mc, int submodel_rot_hit)
1145 {
1146         hit->edge_hit = mc->edge_hit;
1147         hit->hit_pos = mc->hit_point_world;
1148         hit->hit_time = mc->hit_dist;
1149         hit->submodel_num = mc->hit_submodel;
1150
1151         hit->submodel_rot_hit = submodel_rot_hit;
1152 }
1153