FS2_Open
Open source remastering of the Freespace 2 engine
objcollide.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "globalincs/linklist.h"
13 #include "io/timer.h"
14 #include "object/objcollide.h"
15 #include "object/object.h"
16 #include "object/objectdock.h"
17 #include "ship/ship.h"
18 #include "weapon/beam.h"
19 #include "weapon/weapon.h"
20 
21 
22 
23 //#define MAX_PAIRS 10000 // Bumped back to 10,000 by WMC
24  // Reduced from 10,000 to 6,000 by MK on 4/1/98.
25  // Most I saw was 3400 in sm1-06a, the asteriod mission. No other mission came close.
26 #define MIN_PAIRS 2500 // start out with this many pairs
27 #define PAIRS_BUMP 1000 // increase by this many avialable pairs when more are needed
28 
29 // the next 3 variables are used for pair statistics
30 // also in weapon.cpp there is Weapons_created.
31 int Pairs_created = 0;
32 int Num_pairs = 0;
36 
37 int Num_pairs_hwm = 0;
38 
40 
43 
45 
47 {
48 public:
49  object *a;
50  object *b;
55 
56  // we need to define a constructor because the hash map can
57  // implicitly insert an object when we use the [] operator
59  : a(NULL), b(NULL), signature_a(-1), signature_b(-1), next_check_time(-1), initialized(false)
60  {}
61 };
62 
64 
65 struct checkobject;
67 
68 extern int Cmdline_old_collision_sys;
69 
71 {
72  if (Obj_pairs != NULL) {
73  vm_free(Obj_pairs);
74  Obj_pairs = NULL;
75  }
76 
78 }
79 
80 void obj_all_collisions_retime(int checkdly)
81 // sets all collisions to be checked (in 25ms by default)
82 // this is for when we warp objects
83 {
84  obj_pair *parent, *tmp;
85 
86  parent = &pair_used_list;
87  tmp = parent->next;
88 
89 
90  while (tmp != NULL)
91  {
92  tmp->next_check_time = timestamp(checkdly);
93  tmp = tmp->next;
94  }
95 }
96 
97 
99 {
100  int i;
101 
102 // mprintf(( "Resetting object pairs...\n" ));
103 
104  pair_used_list.a = pair_used_list.b = NULL;
105  pair_used_list.next = NULL;
106  pair_free_list.a = pair_free_list.b = NULL;
107 
108  Num_pairs = 0;
109 
110  if (Obj_pairs != NULL) {
111  vm_free(Obj_pairs);
112  Obj_pairs = NULL;
113  }
114 
115  Obj_pairs = (obj_pair*) vm_malloc_q( sizeof(obj_pair) * MIN_PAIRS );
116 
117  if ( Obj_pairs == NULL ) {
118  mprintf(("Unable to create space for collision pairs!!\n"));
119  return;
120  }
121 
123 
124  memset( Obj_pairs, 0, sizeof(obj_pair) * MIN_PAIRS );
125 
126  for (i = 0; i < MIN_PAIRS; i++) {
127  Obj_pairs[i].next = &Obj_pairs[i+1];
128  }
129 
130  Obj_pairs[MIN_PAIRS-1].next = NULL;
131 
132  pair_free_list.next = &Obj_pairs[0];
133 }
134 
135 // returns true if we should reject object pair if one is child of other.
136 int reject_obj_pair_on_parent(object *A, object *B)
137 {
138  if (A->type == OBJ_SHIP) {
139  if (B->type == OBJ_DEBRIS) {
140  if (B->parent_sig == A->signature) {
141  return 0;
142  }
143  }
144  }
145 
146  if (B->type == OBJ_SHIP) {
147  if (A->type == OBJ_DEBRIS) {
148  if (A->parent_sig == B->signature) {
149  return 0;
150  }
151  }
152  }
153 
154  if (A->parent_sig == B->signature) {
155  return 1;
156  }
157 
158  if (B->parent_sig == A->signature) {
159  return 1;
160  }
161 
162  return 0;
163 }
164 
165 int reject_due_collision_groups(object *A, object *B)
166 {
167  if (A->collision_group_id == 0 || B->collision_group_id == 0)
168  return 0;
169 
170  return (A->collision_group_id & B->collision_group_id);
171 }
172 
173 // Adds the pair to the pair list
174 void obj_add_pair( object *A, object *B, int check_time, int add_to_end )
175 {
176  uint ctype;
177  int (*check_collision)( obj_pair *pair );
178  int swapped = 0;
179 
180  check_collision = NULL;
181 
182  if ( Num_pairs_allocated == 0 ) return; // don't have anything to add the pair too
183 
184  if ( A==B ) return; // Don't check collisions with yourself
185 
186  if ( !(A->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything
187  if ( !(B->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything
188 
189  if ((A->flags & OF_IMMOBILE) && (B->flags & OF_IMMOBILE)) return; // Two immobile objects will never collide with each other
190 
191  // Make sure you're not checking a parent with it's kid or vicy-versy
192 // if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
193 // if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
194  if ( reject_obj_pair_on_parent(A,B) ) {
195  return;
196  }
197 
198  Assert( A->type < 127 );
199  Assert( B->type < 127 );
200 
201  ctype = COLLISION_OF(A->type,B->type);
202  switch( ctype ) {
204  swapped = 1;
205  check_collision = collide_ship_weapon;
206  break;
208  check_collision = collide_ship_weapon;
209  break;
211  check_collision = collide_debris_weapon;
212  break;
214  swapped = 1;
215  check_collision = collide_debris_weapon;
216  break;
218  check_collision = collide_debris_ship;
219  break;
221  check_collision = collide_debris_ship;
222  swapped = 1;
223  break;
225  // Only check collision's with player weapons
226 // if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
227  check_collision = collide_asteroid_weapon;
228 // }
229  break;
231  swapped = 1;
232  // Only check collision's with player weapons
233 // if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
234  check_collision = collide_asteroid_weapon;
235 // }
236  break;
238  // Only check collisions with player ships
239 // if ( B->flags & OF_PLAYER_SHIP ) {
240  check_collision = collide_asteroid_ship;
241 // }
242  break;
244  // Only check collisions with player ships
245 // if ( A->flags & OF_PLAYER_SHIP ) {
246  check_collision = collide_asteroid_ship;
247 // }
248  swapped = 1;
249  break;
251  check_collision = collide_ship_ship;
252  break;
253 
255  if(beam_collide_early_out(A, B)){
256  return;
257  }
258  check_collision = beam_collide_ship;
259  break;
260 
262  if(beam_collide_early_out(A, B)){
263  return;
264  }
265  check_collision = beam_collide_asteroid;
266  break;
267 
269  if(beam_collide_early_out(A, B)){
270  return;
271  }
272  check_collision = beam_collide_debris;
273  break;
274 
276  if(beam_collide_early_out(A, B)){
277  return;
278  }
279  check_collision = beam_collide_missile;
280  break;
281 
283  weapon_info *awip, *bwip;
285  bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];
286 
287  if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) {
288  if (bwip->weapon_hitpoints == 0) {
289  check_collision = collide_weapon_weapon;
290  swapped=1;
291  } else {
292  check_collision = collide_weapon_weapon;
293  }
294  }
295 /*
296 
297  if (awip->subtype != WP_LASER || bwip->subtype != WP_LASER) {
298  if (awip->subtype == WP_LASER) {
299  if ( bwip->wi_flags & WIF_BOMB ) {
300  check_collision = collide_weapon_weapon;
301  }
302  } else if (bwip->subtype == WP_LASER) {
303  if ( awip->wi_flags & WIF_BOMB ) {
304  check_collision = collide_weapon_weapon;
305  swapped=1;
306  }
307  } else {
308  if ( (awip->wi_flags&WIF_BOMB) || (bwip->wi_flags&WIF_BOMB) ) {
309  check_collision = collide_weapon_weapon;
310  }
311  }
312  }
313 */
314 /*
315  int atype, btype;
316 
317  atype = Weapon_info[Weapons[A->instance].weapon_info_index].subtype;
318  btype = Weapon_info[Weapons[B->instance].weapon_info_index].subtype;
319 
320  if ((atype == WP_LASER) && (btype == WP_MISSILE))
321  check_collision = collide_weapon_weapon;
322  else if ((atype == WP_MISSILE) && (btype == WP_LASER)) {
323  check_collision = collide_weapon_weapon;
324  swapped = 1;
325  } else if ((atype == WP_MISSILE) && (btype == WP_MISSILE))
326  check_collision = collide_weapon_weapon;
327 */
328 
329  break;
330  }
331 
332  default:
333  return;
334  }
335 
336  // Swap them if needed
337  if ( swapped ) {
338  object *tmp = A;
339  A = B;
340  B = tmp;
341  }
342 
343  // if there are any more obj_pair checks
344  // we should then add function int maybe_not_add_obj_pair()
345  // MWA -- 4/1/98 -- I'd do it, but I don't want to bust anything, so I'm doing my stuff here instead :-)
346  //if ( MULTIPLAYER_CLIENT && !(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE)){
347  // multiplayer clients will only do ship/ship collisions, and their own ship to boot
348  // if ( check_collision != collide_ship_ship ){
349  // return;
350  // }
351 
352  // if ( (A != Player_obj) && (B != Player_obj) ){
353  // return;
354  // }
355  //}
356 
357  // only check debris:weapon collisions for player
358  if (check_collision == collide_debris_weapon) {
359  // weapon is B
361  // check for dumbfire weapon
362  // check if debris is behind laser
363  float vdot;
365  vec3d velocity_rel_weapon;
366  vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
367  vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec);
368  } else {
369  vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
370  }
371  if ( vdot <= 0.0f ) {
372  // They're heading in opposite directions...
373  // check their positions
374  vec3d weapon2other;
375  vm_vec_sub( &weapon2other, &A->pos, &B->pos );
376  float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other );
377  if ( pdot <= -A->radius ) {
378  // The other object is behind the weapon by more than
379  // its radius, so it will never hit...
380  return;
381  }
382  }
383 
384  // check dist vs. dist moved during weapon lifetime
385  vec3d delta_v;
386  vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
388  return;
389  }
390 
391  // for nonplayer ships, only create collision pair if close enough
392  if ( (B->parent >= 0) && !((Objects[B->parent].signature == B->parent_sig) && (Objects[B->parent].flags & OF_PLAYER_SHIP)) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) )
393  return;
394  }
395  }
396 
397  // don't check same team laser:ship collisions on small ships if not player
398  if (check_collision == collide_ship_weapon) {
399  // weapon is B
400  if ( (B->parent >= 0)
401  && (Objects[B->parent].signature == B->parent_sig)
402  && !(Objects[B->parent].flags & OF_PLAYER_SHIP)
403  && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team)
407  return;
408  }
409  }
410 
411  if ( !check_collision ) return;
412  Pairs_created++;
413 
414  // At this point, we have determined that collisions between
415  // these two should be checked, so add the pair to the
416  // collision pair list.
417 
418  if ( pair_free_list.next == NULL ) {
419  nprintf(( "collision", "Out of object pairs!! Not all collisions will work!\n" ));
420  return;
421  }
422 
423  Num_pairs++;
424 /* if (Num_pairs > Num_pairs_hwm) {
425  Num_pairs_hwm = Num_pairs;
426  //nprintf(("AI", "Num_pairs high water mark = %i\n", Num_pairs_hwm));
427  }
428 */
429 
430  if ( Num_pairs >= (Num_pairs_allocated - 20) ) {
431  int i;
432 
433  Assert( Obj_pairs != NULL );
434 
435  int old_pair_count = Num_pairs_allocated;
436  obj_pair *old_pairs_ptr = Obj_pairs;
437 
438  // determine where we need to update the "previous" ptrs to
439  int prev_free_mark = (pair_free_list.next - old_pairs_ptr);
440  int prev_used_mark = (pair_used_list.next - old_pairs_ptr);
441 
442  Obj_pairs = (obj_pair*) vm_realloc_q( Obj_pairs, sizeof(obj_pair) * (Num_pairs_allocated + PAIRS_BUMP) );
443 
444  // allow us to fail here and only if we don't do we setup the new pairs
445 
446  if (Obj_pairs == NULL) {
447  // failed, just go back to the way we were and use only the pairs we have already
448  Obj_pairs = old_pairs_ptr;
449  } else {
451 
452  Assert( Obj_pairs != NULL );
453 
454  // have to reset all of the "next" ptrs for the old set and handle the new set
455  for (i = 0; i < Num_pairs_allocated; i++) {
456  if (i >= old_pair_count) {
457  memset( &Obj_pairs[i], 0, sizeof(obj_pair) );
458  Obj_pairs[i].next = &Obj_pairs[i+1];
459  } else {
460  if (Obj_pairs[i].next != NULL) {
461  // the "next" ptr will end up going backwards for used pairs so we have
462  // to allow for that with this craziness...
463  int next_mark = (Obj_pairs[i].next - old_pairs_ptr);
464  Obj_pairs[i].next = &Obj_pairs[next_mark];
465  }
466 
467  // catch that last NULL from the previously allocated set
468  if ( i == (old_pair_count-1) ) {
469  Obj_pairs[i].next = &Obj_pairs[i+1];
470  }
471  }
472  }
473 
474  Obj_pairs[Num_pairs_allocated-1].next = NULL;
475 
476  // reset the "previous" ptrs
477  pair_free_list.next = &Obj_pairs[prev_free_mark];
478  pair_used_list.next = &Obj_pairs[prev_used_mark];
479  }
480  }
481 
482  // get a new obj_pair from the free list
483  obj_pair * new_pair = pair_free_list.next;
484  pair_free_list.next = new_pair->next;
485 
486  if ( add_to_end ) {
487  obj_pair *last, *tmp;
488 
489  last = tmp = pair_used_list.next;
490  while( tmp != NULL ) {
491  if ( tmp->next == NULL )
492  last = tmp;
493 
494  tmp = tmp->next;
495  }
496 
497  if ( last == NULL )
498  last = &pair_used_list;
499 
500  last->next = new_pair;
501  Assert(new_pair != NULL);
502  new_pair->next = NULL;
503  }
504  else {
505  new_pair->next = pair_used_list.next;
506  pair_used_list.next = new_pair;
507  }
508 
509  A->num_pairs++;
510  B->num_pairs++;
511 
512  new_pair->a = A;
513  new_pair->b = B;
514  new_pair->check_collision = check_collision;
515 
516  if ( check_time == -1 ){
517  new_pair->next_check_time = timestamp(0); // 0 means instantly time out
518  } else {
519  new_pair->next_check_time = check_time;
520  }
521 
522 }
523 
524 MONITOR(NumPairs)
525 MONITOR(NumPairsChecked)
526 
527 //#define PAIR_STATS
528 
529 extern int Cmdline_dis_collisions;
531 {
532  obj_pair *parent, *tmp;
533 
534 #ifdef PAIR_STATS
535  // debug info
536  float avg_time_to_next_check = 0.0f;
537 #endif
538 
539  if (Cmdline_dis_collisions)
540  return;
541 
543  return;
544 
545 
546  parent = &pair_used_list;
547  tmp = parent->next;
548 
549  Num_pairs_checked = 0;
550 
551  while (tmp != NULL) {
552  int removed = 0;
553 
554  if ( !timestamp_elapsed(tmp->next_check_time) )
555  goto NextPair;
556 
557  if ( (tmp->a) && (tmp->b) ) {
559 
560  if ( (*tmp->check_collision)(tmp) ) {
561  // We never need to check this pair again.
562  #if 0 //def DONT_REMOVE_PAIRS
563  // Never check it again, but keep the pair around
564  // (useful for debugging)
565  tmp->next_check_time = timestamp(-1);
566  #else
567  // Never check it again, so remove the pair
568  removed = 1;
569  tmp->a->num_pairs--;
570  Assert( tmp->a->num_pairs > -1 );
571  tmp->b->num_pairs--;
572  Assert( tmp->b->num_pairs > -1 );
573  Num_pairs--;
574  // Assert(Num_pairs >= 0);
575  parent->next = tmp->next;
576  tmp->a = tmp->b = NULL;
577  tmp->next = pair_free_list.next;
578  pair_free_list.next = tmp;
579  tmp = parent->next;
580  #endif
581  }
582  }
583 
584 NextPair:
585  if ( !removed ) {
586  parent = tmp;
587  tmp = tmp->next;
588 
589 #ifdef PAIR_STATS
590  // debug info
591  if (tmp) {
592  int add_time = timestamp_until( tmp->next_check_time );
593  if (add_time > 0)
594  avg_time_to_next_check += (float) add_time;
595  }
596 #endif
597  }
598  }
599 
600  MONITOR_INC(NumPairs, Num_pairs);
601  MONITOR_INC(NumPairsChecked, Num_pairs_checked);
602 
603 #ifdef PAIR_STATS
604  avg_time_to_next_check = avg_time_to_next_check / Num_pairs;
605  extern int Num_hull_pieces;
606  extern int Weapons_created;
607  // 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 ));
608  // 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) ));
609  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) ));
610  pairs_not_created = 0;
611  Weapons_created = 0;
612  Pairs_created = 0;
613 #endif
614 
615  // What percent of the pairs did we check?
616  // FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects.
617 
618 // if ( org_pairs > 1 ) {
619 // Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs);
620 // } else {
621 // Object_checked_percentage = 0.0f;
622 // }
623 
624 }
625 
626 // See if two lines intersect by doing recursive subdivision.
627 // Bails out if larger distance traveled is less than sum of radii + 1.0f.
628 int collide_subdivide(vec3d *p0, vec3d *p1, float prad, vec3d *q0, vec3d *q1, float qrad)
629 {
630  float a_dist, b_dist, ab_dist;
631 
632  a_dist = vm_vec_dist(p0, p1);
633  b_dist = vm_vec_dist(q0, q1);
634 
635  ab_dist = vm_vec_dist(p1, q1);
636 
637  // See if their spheres intersect
638  if (ab_dist < a_dist + b_dist + prad + qrad) {
639  if (ab_dist < prad + qrad)
640  return 1;
641  else if (vm_vec_dist(p0, q0) < prad + qrad)
642  return 1;
643  else if (MAX(a_dist, b_dist) < prad + qrad + 1.0f)
644  return 0;
645  else {
646  int r1, r2 = 0;
647  vec3d pa, qa;
648 
649  vm_vec_avg(&pa, p0, p1);
650  vm_vec_avg(&qa, q0, q1);
651  r1 = collide_subdivide(p0, &pa, prad, q0, &qa, qrad);
652  if (!r1)
653  r2 = collide_subdivide(&pa, p1, prad, &qa, q1, qrad);
654 
655  return r1 | r2;
656  }
657  } else
658  return 0;
659 }
660 
661 
662 
663 // Return true if object A is expected to collide with object B within time duration
664 // For purposes of this check, the first object moves from current location to predicted
665 // location. The second object is assumed to be where it will be at time duration, NOT
666 // where it currently is.
667 // radius_scale is used to control the precision of the check.
668 // If 0.0, then use polygon models to perform check, slow and accurate
669 // If !0.0, then use as a scale on the radius of the objects. 1.0 is Descent style
670 // collisions. Larger values can be used to be sloppy about the collisions which
671 // is useful if a moving object wants to prevent a collision.
672 int objects_will_collide(object *A, object *B, float duration, float radius_scale)
673 {
674  vec3d prev_pos;
675  vec3d hitpos;
676  int ret;
677 
678 
679  prev_pos = A->pos;
680  vm_vec_scale_add2(&A->pos, &A->phys_info.vel, duration);
681 
682  if (radius_scale == 0.0f) {
683  ret = ship_check_collision_fast(B, A, &hitpos);
684  } else {
685  float size_A, size_B, dist, r;
686  vec3d nearest_point;
687 
688  size_A = A->radius * radius_scale;
689  size_B = B->radius * radius_scale;
690 
691  // If A is moving, check along vector.
692  if (A->phys_info.speed != 0.0f) {
693  r = find_nearest_point_on_line(&nearest_point, &prev_pos, &A->pos, &B->pos);
694  if (r < 0) {
695  nearest_point = prev_pos;
696  } else if (r > 1) {
697  nearest_point = A->pos;
698  }
699  dist = vm_vec_dist_quick(&B->pos, &nearest_point);
700  ret = (dist < size_A + size_B);
701  } else {
702  ret = vm_vec_dist_quick(&B->pos, &prev_pos) < size_A + size_B;
703  }
704  }
705 
706  // Reset the position to the previous value
707  A->pos = prev_pos;
708 
709  return ret;
710 }
711 
712 // Return true if the vector from *start_pos to *end_pos is within objp->radius*radius_scale of *objp
713 int vector_object_collision(vec3d *start_pos, vec3d *end_pos, object *objp, float radius_scale)
714 {
715  float dist, r;
716  vec3d nearest_point;
717 
718  r = find_nearest_point_on_line(&nearest_point, start_pos, end_pos, &objp->pos);
719  if ((r >= 0.0f) && (r <= 1.0f)) {
720  dist = vm_vec_dist_quick(&objp->pos, &nearest_point);
721 
722  return (dist < objp->radius * radius_scale);
723  } else
724  return 0;
725 }
726 
727 // Returns TRUE if the weapon will never hit the other object.
728 // If it can it predicts how long until these two objects need
729 // to be checked and fills the time in in current_pair.
730 int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current_pair )
731 {
732 
733  Assert( obj_weapon->type == OBJ_WEAPON );
734  weapon *wp = &Weapons[obj_weapon->instance];
736 
737 // mprintf(( "Frame: %d, Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair ));
738 
739 
740  // Do some checks for weapons that don't turn
741  if ( !(wip->wi_flags & WIF_TURNS) ) {
742 
743  // This first check is to see if a weapon is behind an object, and they
744  // are heading in opposite directions. If so, we don't need to ever check
745  // them again. This is only valid for weapons that don't turn.
746 
747  float vdot;
748  if (wip->subtype == WP_LASER) {
749  vec3d velocity_rel_weapon;
750  vm_vec_sub(&velocity_rel_weapon, &obj_weapon->phys_info.vel, &other->phys_info.vel);
751  vdot = -vm_vec_dot(&velocity_rel_weapon, &obj_weapon->orient.vec.fvec);
752  } else {
753  vdot = vm_vec_dot( &other->phys_info.vel, &obj_weapon->phys_info.vel);
754  }
755  if ( vdot <= 0.0f ) {
756  // They're heading in opposite directions...
757  // check their positions
758  vec3d weapon2other;
759  vm_vec_sub( &weapon2other, &other->pos, &obj_weapon->pos );
760  float pdot = vm_vec_dot( &obj_weapon->orient.vec.fvec, &weapon2other );
761  if ( pdot <= -other->radius ) {
762  // The other object is behind the weapon by more than
763  // its radius, so it will never hit...
764  return 1;
765  }
766  }
767 
768  // FUTURE ENHANCEMENT IDEAS
769 
770  // Given a laser does it hit a slow or not moving object
771  // in its life or the next n seconds? We'd actually need to check the
772  // model for this.
773 
774  }
775 
776 
777  // This check doesn't care about orient, only looks at the maximum speed
778  // of the two objects, so it knows that in the next n seconds, they can't
779  // go further than some distance, so don't bother checking collisions for
780  // that time. This is very rough, but is so general that it works for
781  // everything and immidiately gets rid of a lot of cases.
782 
783  if ( current_pair ) {
784  // Find the time it will take before these get within each others distances.
785  // tmp->next_check_time = timestamp(500);
786  //vector max_vel; //maximum foward velocity in x,y,z
787 
788  float max_vel_weapon, max_vel_other;
789 
790  //SUSHI: Fix bug where additive weapon velocity screws up collisions
791  //If the PF_CONST_VEL flag is set, we can safely assume it doesn't change speed.
792  if (obj_weapon->phys_info.flags & PF_CONST_VEL)
793  max_vel_weapon = obj_weapon->phys_info.speed;
794  else if (wp->lssm_stage==5)
795  max_vel_weapon = wip->lssm_stage5_vel;
796  else
797  max_vel_weapon = wp->weapon_max_vel;
798 
799  max_vel_other = other->phys_info.max_vel.xyz.z;
800  if (max_vel_other < 10.0f) {
801  if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) {
802  // bump up velocity from collision
803  max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f;
804  } else {
805  max_vel_other = 10.0f; // object may move from collision
806  }
807  }
808 
809  // check weapon that does not turn against sphere expanding at ship maxvel
810  // compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time
811  // look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship
812  // Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides
813  if ( !(wip->wi_flags & WIF_TURNS) ) {
814  vec3d delta_x, laser_vel;
815  float a,b,c, delta_x_dot_vl, delta_t;
816  float root1, root2, root, earliest_time;
817 
818  if (max_vel_weapon == max_vel_other) {
819  // this will give us NAN using the below formula, so check every frame
820  current_pair->next_check_time = timestamp(0);
821  return 0;
822  }
823 
824  vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos );
825  laser_vel = obj_weapon->phys_info.vel;
826  // vm_vec_copy_scale( &laser_vel, &weapon->orient.vec.fvec, max_vel_weapon );
827  delta_t = (other->radius + 10.0f) / max_vel_other; // time to get from center to radius of other obj
828  delta_x_dot_vl = vm_vec_dot( &delta_x, &laser_vel );
829 
830  a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other;
831  b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t);
832  c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t;
833 
834  float discriminant = b*b - 4.0f*a*c;
835  if ( discriminant < 0) {
836  // never hit
837  return 1;
838  } else {
839  root = fl_sqrt( discriminant );
840  root1 = (-b + root) / (2.0f * a) * 1000.0f; // get time in ms
841  root2 = (-b - root) / (2.0f * a) * 1000.0f; // get time in ms
842  }
843 
844  // standard algorithm
845  if (max_vel_weapon > max_vel_other) {
846  // find earliest positive time
847  if ( root1 > root2 ) {
848  float temp = root1;
849  root1 = root2;
850  root2 = temp;
851  }
852 
853  if (root1 > 0) {
854  earliest_time = root1;
855  } else if (root2 > 0) {
856  // root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame
857  current_pair->next_check_time = timestamp(0); // check next time
858  return 0;
859  } else {
860  // both times negative, so never collides
861  return 1;
862  }
863  }
864  // need to modify it for weapons that are slower than ships
865  else {
866  if (root2 > 0) {
867  earliest_time = root2;
868  } else {
869  current_pair->next_check_time = timestamp(0);
870  return 0;
871  }
872  }
873 
874 
875 
876  // check if possible collision occurs after weapon expires
877  if ( earliest_time > 1000*wp->lifeleft )
878  return 1;
879 
880  // Allow one worst case frametime to elapse (~5 fps)
881  earliest_time -= 200.0f;
882 
883  if (earliest_time > 100) {
884  current_pair->next_check_time = timestamp( fl2i(earliest_time) );
885  return 0;
886  } else {
887  current_pair->next_check_time = timestamp(0); // check next time
888  return 0;
889  }
890 
891  } else {
892 
893  float dist, max_vel, time;
894 
895  max_vel = max_vel_weapon + max_vel_other;
896 
897  // suggest that fudge factor for other radius be changed to other_radius + const (~10)
898  dist = vm_vec_dist( &other->pos, &obj_weapon->pos ) - (other->radius + 10.0f);
899  if ( dist > 0.0f ) {
900  time = (dist*1000.0f) / max_vel;
901  int time_ms = fl2i(time);
902 
903  // check if possible collision occurs after weapon expires
904  if ( time_ms > 1000*wp->lifeleft )
905  return 1;
906 
907  time_ms -= 200; // Allow at least one worst case frametime to elapse (~5 fps)
908 
909  if ( time_ms > 100 ) { // If it takes longer than 1/10th of a second, then delay it
910  current_pair->next_check_time = timestamp(time_ms);
911  //mprintf(( "Delaying %d ms\n", time_ms ));
912  return 0;
913  }
914  }
915  current_pair->next_check_time = timestamp(0); // check next time
916 
917  }
918  }
919 
920  return 0;
921 }
922 
923 // Return true if vector from *curpos to *goalpos intersects with object *goalobjp
924 // Else, return false.
925 // radius is radius of object moving from curpos to goalpos.
926 int pp_collide(vec3d *curpos, vec3d *goalpos, object *goalobjp, float radius)
927 {
928  mc_info mc;
929  mc_info_init(&mc);
930 
931  Assert(goalobjp->type == OBJ_SHIP);
932 
934  mc.model_num = Ship_info[Ships[goalobjp->instance].ship_info_index].model_num; // Fill in the model to check
935  mc.orient = &goalobjp->orient; // The object's orient
936  mc.pos = &goalobjp->pos; // The object's position
937  mc.p0 = curpos; // Point 1 of ray to check
938  mc.p1 = goalpos; // Point 2 of ray to check
940  mc.radius = radius;
941 
942  model_collide(&mc);
943 
944  return mc.num_hits;
945 }
946 
947 // Setup and call pp_collide for collide_predict_large_ship
948 // Returns true if objp will collide with objp2 before it reaches goal_pos.
949 int cpls_aux(vec3d *goal_pos, object *objp2, object *objp)
950 {
951  float radius;
952 
953  radius = objp->radius;
954  if (1.5f * radius < 70.0f)
955  radius *= 1.5f;
956  else
957  radius = 70.0f;
958 
959  if (pp_collide(&objp->pos, goal_pos, objp2, radius))
960  return 1;
961  else
962  return 0;
963 }
964 
965 // Return true if objp will collide with some large object.
966 // Don't check for an object this ship is docked to.
967 int collide_predict_large_ship(object *objp, float distance)
968 {
969  object *objp2;
970  vec3d cur_pos, goal_pos;
971  ship_info *sip;
972 
973  sip = &Ship_info[Ships[objp->instance].ship_info_index];
974 
975  cur_pos = objp->pos;
976 
977  vm_vec_scale_add(&goal_pos, &cur_pos, &objp->orient.vec.fvec, distance);
978 
979  for ( objp2 = GET_FIRST(&obj_used_list); objp2 != END_OF_LIST(&obj_used_list); objp2 = GET_NEXT(objp2) ) {
980  if ((objp != objp2) && (objp2->type == OBJ_SHIP)) {
981  if (Ship_info[Ships[objp2->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
982  if (dock_check_find_docked_object(objp, objp2))
983  continue;
984 
985  if (cpls_aux(&goal_pos, objp2, objp))
986  return 1;
987  }
988  } else if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (objp2->type == OBJ_ASTEROID)) {
989  if (vm_vec_dist_quick(&objp2->pos, &objp->pos) < (distance + objp2->radius)*2.5f) {
990  vec3d pos, delvec;
991  int count;
992  float d1;
993 
994  d1 = 2.5f * distance + objp2->radius;
995  count = (int) (d1/(objp2->radius + objp->radius)); // Scale up distance, else looks like there would be a collision.
996  pos = cur_pos;
997  vm_vec_normalized_dir(&delvec, &goal_pos, &cur_pos);
998  vm_vec_scale(&delvec, d1/count);
999 
1000  for (; count>0; count--) {
1001  if (vm_vec_dist_quick(&pos, &objp2->pos) < objp->radius + objp2->radius)
1002  return 1;
1003  vm_vec_add2(&pos, &delvec);
1004  }
1005  }
1006  }
1007  }
1008 
1009  return 0;
1010 }
1011 
1012 // function to iterate through all object collision pairs looking for weapons
1013 // which could be deleted since they are not going to hit anything. Passed into this
1014 // function is a 'time' parameter used as watermark for which weapons to check.
1015 
1016 #define CRW_NO_OBJECT -1
1017 #define CRW_NO_PAIR 0
1018 #define CRW_IN_PAIR 1
1019 #define CRW_CAN_DELETE 2
1020 
1021 #define CRW_MAX_TO_DELETE 4
1022 
1024 
1025 void crw_check_weapon( int weapon_num, int collide_next_check )
1026 {
1027  float next_check_time;
1028  weapon *wp;
1029 
1030  wp = &Weapons[weapon_num];
1031 
1032  // if this weapons life left > time before next collision, then we cannot remove it
1034  next_check_time = ((float)(timestamp_until(collide_next_check)) / 1000.0f);
1035  if ( wp->lifeleft < next_check_time )
1037 }
1038 
1040 {
1041  obj_pair *opp;
1042  int i, num_deleted, oldest_index, j, loop_count;
1043  float oldest_time;
1044 
1045  // setup remove_weapon array. assume we can remove it.
1046  for (i = 0; i < MAX_WEAPONS; i++ ) {
1047  if ( Weapons[i].objnum == -1 )
1049  else
1051  }
1052 
1053  // first pass is to see if any of the weapons don't have collision pairs.
1054 
1055  if ( Cmdline_old_collision_sys ) {
1056  opp = &pair_used_list;
1057  opp = opp->next;
1058  while( opp != NULL ) {
1059  // for each collide pair, if the two objects can still collide, then set the remove_weapon
1060  // parameter for the weapon to 0. need to check both parameters
1061  if ( opp->a->type == OBJ_WEAPON )
1062  crw_check_weapon( opp->a->instance, opp->next_check_time );
1063 
1064  if ( opp->b->type == OBJ_WEAPON )
1065  crw_check_weapon( opp->b->instance, opp->next_check_time );
1066 
1067  opp = opp->next;
1068  }
1069  } else {
1071  collider_pair *pair_obj;
1072 
1073  for ( it = Collision_cached_pairs.begin(); it != Collision_cached_pairs.end(); ++it ) {
1074  pair_obj = &it->second;
1075 
1076  if ( !pair_obj->initialized ) {
1077  continue;
1078  }
1079 
1080  if ( pair_obj->a->type == OBJ_WEAPON && pair_obj->signature_a == pair_obj->a->signature ) {
1081  crw_check_weapon(pair_obj->a->instance, pair_obj->next_check_time);
1082 
1083  if ( crw_status[pair_obj->a->instance] == CRW_CAN_DELETE ) {
1084  pair_obj->initialized = false;
1085  }
1086  }
1087 
1088  if ( pair_obj->b->type == OBJ_WEAPON && pair_obj->signature_b == pair_obj->b->signature ) {
1089  crw_check_weapon(pair_obj->b->instance, pair_obj->next_check_time);
1090 
1091  if ( crw_status[pair_obj->b->instance] == CRW_CAN_DELETE ) {
1092  pair_obj->initialized = false;
1093  }
1094  }
1095  }
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 
1154 //Previously, this was done with
1155 //memset(&ship_ship_hit_info, -1, sizeof(collision_info_struct));
1156 //All those -1s are to replicate that logic
1158 {
1159  memset(cis, -1, sizeof(collision_info_struct));
1160  cis->is_landing = false;
1161 }
1162 
1163 void obj_add_collider(int obj_index)
1164 {
1165  object *objp = &Objects[obj_index];
1166 
1167 #ifdef OBJECT_CHECK
1168  CheckObjects[obj_index].type = objp->type;
1169  CheckObjects[obj_index].signature = objp->signature;
1170  CheckObjects[obj_index].flags = objp->flags & ~(OF_NOT_IN_COLL);
1171  CheckObjects[obj_index].parent_sig = objp->parent_sig;
1172  CheckObjects[obj_index].parent_type = objp->parent_type;
1173 #endif
1174 
1175  if(!(objp->flags & OF_NOT_IN_COLL)){
1176  return;
1177  }
1178 
1179  Collision_sort_list.push_back(obj_index);
1180 
1181  objp->flags &= ~OF_NOT_IN_COLL;
1182 }
1183 
1184 void obj_remove_collider(int obj_index)
1185 {
1186 #ifdef OBJECT_CHECK
1187  CheckObjects[obj_index].flags |= OF_NOT_IN_COLL;
1188 #endif
1189 
1190  size_t i;
1191 
1192  for ( i = 0; i < Collision_sort_list.size(); ++i ) {
1193  if ( Collision_sort_list[i] == obj_index ) {
1194  Collision_sort_list[i] = Collision_sort_list.back();
1195  Collision_sort_list.pop_back();
1196  break;
1197  }
1198  }
1199 
1200  Objects[obj_index].flags |= OF_NOT_IN_COLL;
1201 }
1202 
1204 {
1205  Collision_sort_list.clear();
1206  Collision_cached_pairs.clear();
1207 }
1208 
1210 {
1212 
1213  for ( it = Collision_cached_pairs.begin(); it != Collision_cached_pairs.end(); ++it ) {
1214  it->second.next_check_time = timestamp(checkdly);
1215  }
1216 }
1217 
1219 {
1221  return;
1222 
1224  return;
1225 
1226  SCP_vector<int> sort_list_y;
1227  SCP_vector<int> sort_list_z;
1228 
1229  sort_list_y.clear();
1230  obj_quicksort_colliders(&Collision_sort_list, 0, Collision_sort_list.size() - 1, 0);
1231  obj_find_overlap_colliders(&sort_list_y, &Collision_sort_list, 0, false);
1232 
1233  sort_list_z.clear();
1234  obj_quicksort_colliders(&sort_list_y, 0, sort_list_y.size() - 1, 1);
1235  obj_find_overlap_colliders(&sort_list_z, &sort_list_y, 1, false);
1236 
1237  sort_list_y.clear();
1238  obj_quicksort_colliders(&sort_list_z, 0, sort_list_z.size() - 1, 2);
1239  obj_find_overlap_colliders(&sort_list_y, &sort_list_z, 2, true);
1240 }
1241 
1242 void obj_find_overlap_colliders(SCP_vector<int> *overlap_list_out, SCP_vector<int> *list, int axis, bool collide)
1243 {
1244  size_t i, j;
1245  bool overlapped;
1246  bool first_not_added = true;
1247  SCP_vector<int> overlappers;
1248 
1249  float min;
1250  float overlap_max;
1251 
1252  overlappers.clear();
1253 
1254  for ( i = 0; i < (*list).size(); ++i ) {
1255  overlapped = false;
1256 
1257  min = obj_get_collider_endpoint((*list)[i], axis, true);
1258 
1259  for ( j = 0; j < overlappers.size(); ) {
1260  overlap_max = obj_get_collider_endpoint(overlappers[j], axis, false);
1261  if ( min <= overlap_max ) {
1262  overlapped = true;
1263 
1264  if ( overlappers.size() == 1 && first_not_added ) {
1265  first_not_added = false;
1266  overlap_list_out->push_back(overlappers[j]);
1267  }
1268 
1269  if ( collide ) {
1270  obj_collide_pair(&Objects[(*list)[i]], &Objects[overlappers[j]]);
1271  }
1272  } else {
1273  overlappers[j] = overlappers.back();
1274  overlappers.pop_back();
1275  continue;
1276  }
1277 
1278  ++j;
1279  }
1280 
1281  if ( overlappers.size() == 0 ) {
1282  first_not_added = true;
1283  }
1284 
1285  if ( overlapped ) {
1286  overlap_list_out->push_back((*list)[i]);
1287  }
1288 
1289  overlappers.push_back((*list)[i]);
1290  }
1291 
1292  overlapped = true;
1293 }
1294 
1295 float obj_get_collider_endpoint(int obj_num, int axis, bool min)
1296 {
1297  if ( Objects[obj_num].type == OBJ_BEAM ) {
1298  beam *b = &Beams[Objects[obj_num].instance];
1299 
1300  // use the last start and last shot as endpoints
1301  float min_end, max_end;
1302  if ( b->last_start.a1d[axis] > b->last_shot.a1d[axis] ) {
1303  min_end = b->last_shot.a1d[axis];
1304  max_end = b->last_start.a1d[axis];
1305  } else {
1306  min_end = b->last_start.a1d[axis];
1307  max_end = b->last_shot.a1d[axis];
1308  }
1309 
1310  if ( min ) {
1311  return min_end;
1312  } else {
1313  return max_end;
1314  }
1315  } else if ( Objects[obj_num].type == OBJ_WEAPON ) {
1316  float min_end, max_end;
1317 
1318  if ( Objects[obj_num].pos.a1d[axis] > Objects[obj_num].last_pos.a1d[axis] ) {
1319  min_end = Objects[obj_num].last_pos.a1d[axis];
1320  max_end = Objects[obj_num].pos.a1d[axis];
1321  } else {
1322  min_end = Objects[obj_num].pos.a1d[axis];
1323  max_end = Objects[obj_num].last_pos.a1d[axis];
1324  }
1325 
1326  if ( min ) {
1327  return min_end - Objects[obj_num].radius;
1328  } else {
1329  return max_end + Objects[obj_num].radius;
1330  }
1331  } else {
1332  vec3d *pos = &Objects[obj_num].pos;
1333 
1334  if ( min ) {
1335  return pos->a1d[axis] - Objects[obj_num].radius;
1336  } else {
1337  return pos->a1d[axis] + Objects[obj_num].radius;
1338  }
1339  }
1340 }
1341 
1342 void obj_quicksort_colliders(SCP_vector<int> *list, int left, int right, int axis)
1343 {
1344  Assert( axis >= 0 );
1345  Assert( axis <= 2 );
1346 
1347  if ( right > left ) {
1348  int pivot_index = left + (right - left) / 2;
1349 
1350  float pivot_value = obj_get_collider_endpoint((*list)[pivot_index], axis, true);
1351 
1352  // swap!
1353  int temp = (*list)[pivot_index];
1354  (*list)[pivot_index] = (*list)[right];
1355  (*list)[right] = temp;
1356 
1357  int store_index = left;
1358 
1359  int i;
1360  for ( i = left; i < right; ++i ) {
1361  if ( obj_get_collider_endpoint((*list)[i], axis, true) <= pivot_value ) {
1362  temp = (*list)[i];
1363  (*list)[i] = (*list)[store_index];
1364  (*list)[store_index] = temp;
1365  store_index++;
1366  }
1367  }
1368 
1369  temp = (*list)[right];
1370  (*list)[right] = (*list)[store_index];
1371  (*list)[store_index] = temp;
1372 
1373  obj_quicksort_colliders(list, left, store_index - 1, axis);
1374  obj_quicksort_colliders(list, store_index + 1, right, axis);
1375  }
1376 }
1377 
1378 void obj_collide_pair(object *A, object *B)
1379 {
1380  uint ctype;
1381  int (*check_collision)( obj_pair *pair );
1382  int swapped = 0;
1383 
1384  check_collision = NULL;
1385 
1386  if ( A==B ) return; // Don't check collisions with yourself
1387 
1388  if ( !(A->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything
1389  if ( !(B->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything
1390 
1391  if ((A->flags & OF_IMMOBILE) && (B->flags & OF_IMMOBILE)) return; // Two immobile objects will never collide with each other
1392 
1393  // Make sure you're not checking a parent with it's kid or vicy-versy
1394 // if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
1395 // if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
1396  if ( reject_obj_pair_on_parent(A,B) ) {
1397  return;
1398  }
1399 
1400  Assert( A->type < 127 );
1401  Assert( B->type < 127 );
1402 
1403  ctype = COLLISION_OF(A->type,B->type);
1404  switch( ctype ) {
1406  swapped = 1;
1407  check_collision = collide_ship_weapon;
1408  break;
1410  check_collision = collide_ship_weapon;
1411  break;
1413  check_collision = collide_debris_weapon;
1414  break;
1416  swapped = 1;
1417  check_collision = collide_debris_weapon;
1418  break;
1420  check_collision = collide_debris_ship;
1421  break;
1423  check_collision = collide_debris_ship;
1424  swapped = 1;
1425  break;
1427  // Only check collision's with player weapons
1428 // if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
1429  check_collision = collide_asteroid_weapon;
1430 // }
1431  break;
1433  swapped = 1;
1434  // Only check collision's with player weapons
1435 // if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
1436  check_collision = collide_asteroid_weapon;
1437 // }
1438  break;
1440  // Only check collisions with player ships
1441 // if ( B->flags & OF_PLAYER_SHIP ) {
1442  check_collision = collide_asteroid_ship;
1443 // }
1444  break;
1446  // Only check collisions with player ships
1447 // if ( A->flags & OF_PLAYER_SHIP ) {
1448  check_collision = collide_asteroid_ship;
1449 // }
1450  swapped = 1;
1451  break;
1453  check_collision = collide_ship_ship;
1454  break;
1455 
1457  if(beam_collide_early_out(B, A)){
1458  return;
1459  }
1460  swapped = 1;
1461  check_collision = beam_collide_ship;
1462  break;
1463 
1465  if(beam_collide_early_out(A, B)){
1466  return;
1467  }
1468  check_collision = beam_collide_ship;
1469  break;
1470 
1472  if(beam_collide_early_out(B, A)) {
1473  return;
1474  }
1475  swapped = 1;
1476  check_collision = beam_collide_asteroid;
1477  break;
1478 
1480  if(beam_collide_early_out(A, B)){
1481  return;
1482  }
1483  check_collision = beam_collide_asteroid;
1484  break;
1486  if(beam_collide_early_out(B, A)) {
1487  return;
1488  }
1489  swapped = 1;
1490  check_collision = beam_collide_debris;
1491  break;
1493  if(beam_collide_early_out(A, B)){
1494  return;
1495  }
1496  check_collision = beam_collide_debris;
1497  break;
1499  if(beam_collide_early_out(B, A)) {
1500  return;
1501  }
1502  swapped = 1;
1503  check_collision = beam_collide_missile;
1504  break;
1505 
1507  if(beam_collide_early_out(A, B)){
1508  return;
1509  }
1510  check_collision = beam_collide_missile;
1511  break;
1512 
1514  weapon_info *awip, *bwip;
1516  bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];
1517 
1518  if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) {
1519  if (bwip->weapon_hitpoints == 0) {
1520  check_collision = collide_weapon_weapon;
1521  swapped=1;
1522  } else {
1523  check_collision = collide_weapon_weapon;
1524  }
1525  }
1526 
1527  break;
1528  }
1529 
1530  default:
1531  return;
1532  }
1533 
1534  if ( !check_collision ) return;
1535 
1536  // Swap them if needed
1537  if ( swapped ) {
1538  object *tmp = A;
1539  A = B;
1540  B = tmp;
1541  }
1542 
1543  collider_pair *collision_info = NULL;
1544  bool valid = false;
1545  uint key = (OBJ_INDEX(A) << 12) + OBJ_INDEX(B);
1546 
1547  collision_info = &Collision_cached_pairs[key];
1548 
1549  if ( collision_info->initialized ) {
1550  // make sure we're referring to the correct objects in case the original pair was deleted
1551  if ( collision_info->signature_a == collision_info->a->signature &&
1552  collision_info->signature_b == collision_info->b->signature ) {
1553  valid = true;
1554  } else {
1555  collision_info->a = A;
1556  collision_info->b = B;
1557  collision_info->signature_a = A->signature;
1558  collision_info->signature_b = B->signature;
1559  collision_info->next_check_time = timestamp(0);
1560  }
1561  } else {
1562  collision_info->a = A;
1563  collision_info->b = B;
1564  collision_info->signature_a = A->signature;
1565  collision_info->signature_b = B->signature;
1566  collision_info->initialized = true;
1567  collision_info->next_check_time = timestamp(0);
1568  }
1569 
1570  if ( valid && A->type != OBJ_BEAM ) {
1571  // if this signature is valid, make the necessary checks to see if we need to collide check
1572  if ( collision_info->next_check_time == -1 ) {
1573  return;
1574  } else {
1575  if ( !timestamp_elapsed(collision_info->next_check_time) ) {
1576  return;
1577  }
1578  }
1579  } else {
1580  //if ( A->type == OBJ_BEAM ) {
1581  //if(beam_collide_early_out(A, B)){
1582  //collision_info->next_check_time = -1;
1583  //return;
1584  //}
1585  //}
1586 
1587  // only check debris:weapon collisions for player
1588  if (check_collision == collide_debris_weapon) {
1589  // weapon is B
1591  // check for dumbfire weapon
1592  // check if debris is behind laser
1593  float vdot;
1595  vec3d velocity_rel_weapon;
1596  vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
1597  vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec);
1598  } else {
1599  vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
1600  }
1601  if ( vdot <= 0.0f ) {
1602  // They're heading in opposite directions...
1603  // check their positions
1604  vec3d weapon2other;
1605  vm_vec_sub( &weapon2other, &A->pos, &B->pos );
1606  float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other );
1607  if ( pdot <= -A->radius ) {
1608  // The other object is behind the weapon by more than
1609  // its radius, so it will never hit...
1610  collision_info->next_check_time = -1;
1611  return;
1612  }
1613  }
1614 
1615  // check dist vs. dist moved during weapon lifetime
1616  vec3d delta_v;
1617  vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
1619  collision_info->next_check_time = -1;
1620  return;
1621  }
1622 
1623  // for nonplayer ships, only create collision pair if close enough
1624  if ( (B->parent >= 0) && !((Objects[B->parent].signature == B->parent_sig) && (Objects[B->parent].flags & OF_PLAYER_SHIP)) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) ) {
1625  collision_info->next_check_time = -1;
1626  return;
1627  }
1628  }
1629  }
1630 
1631  // don't check same team laser:ship collisions on small ships if not player
1632  if (check_collision == collide_ship_weapon) {
1633  // weapon is B
1634  if ( (B->parent >= 0)
1635  && (Objects[B->parent].signature == B->parent_sig)
1636  && !(Objects[B->parent].flags & OF_PLAYER_SHIP)
1637  && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team)
1640  collision_info->next_check_time = -1;
1641  return;
1642  }
1643  }
1644  }
1645 
1646  obj_pair new_pair;
1647 
1648  new_pair.a = A;
1649  new_pair.b = B;
1650  new_pair.check_collision = check_collision;
1651  new_pair.next_check_time = collision_info->next_check_time;
1652 
1653  if ( check_collision(&new_pair) ) {
1654  // don't have to check ever again
1655  collision_info->next_check_time = -1;
1656  } else {
1657  collision_info->next_check_time = new_pair.next_check_time;
1658  }
1659 }
void mc_info_init(mc_info *mc)
Definition: model.h:1138
#define PAIRS_BUMP
Definition: objcollide.cpp:27
int beam_collide_asteroid(obj_pair *pair)
Definition: beam.cpp:2625
int model_collide(mc_info *mc_info_obj)
#define vm_malloc_q(size)
Definition: pstypes.h:554
int timestamp(int delta_ms)
Definition: timer.cpp:226
void obj_quicksort_colliders(SCP_vector< int > *list, int left, int right, int axis)
int collide_ship_ship(obj_pair *pair)
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
int pairs_not_created
Definition: objcollide.cpp:35
weapon Weapons[MAX_WEAPONS]
Definition: weapons.cpp:78
#define OF_NOT_IN_COLL
Definition: object.h:114
int vector_object_collision(vec3d *start_pos, vec3d *end_pos, object *objp, float radius_scale)
Definition: objcollide.cpp:713
int cpls_aux(vec3d *goal_pos, object *objp2, object *objp)
Definition: objcollide.cpp:949
vec3d * pos
Definition: model.h:1113
int collide_weapon_weapon(obj_pair *pair)
int team
Definition: ship.h:606
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
int collide_asteroid_ship(obj_pair *pair)
float obj_get_collider_endpoint(int obj_num, int axis, bool min)
float vm_vec_mag(const vec3d *v)
Definition: vecmat.cpp:325
char parent_type
Definition: object.h:149
int(* check_collision)(obj_pair *pair)
Definition: objcollide.h:57
void obj_all_collisions_retime(int checkdly)
Definition: objcollide.cpp:80
Definition: weapon.h:163
int collide_predict_large_ship(object *objp, float distance)
Definition: objcollide.cpp:967
bool dock_check_find_docked_object(object *objp, object *other_objp)
Definition: objectdock.cpp:96
physics_info phys_info
Definition: object.h:157
#define WIF_TURNS
Definition: weapon.h:56
int model_instance_num
Definition: ship.h:802
int beam_collide_missile(obj_pair *pair)
Definition: beam.cpp:2721
Assert(pm!=NULL)
Definition: pstypes.h:88
#define mprintf(args)
Definition: pstypes.h:238
void obj_reset_colliders()
#define OBJ_ASTEROID
Definition: object.h:44
float weapon_max_vel
Definition: weapon.h:226
hull_check p0
Definition: lua.cpp:5051
#define PF_CONST_VEL
Definition: physics.h:26
int parent_type
Definition: object.h:203
struct vec3d::@225::@227 xyz
#define CRW_NO_PAIR
vec3d max_vel
Definition: physics.h:49
GLclampf f
Definition: Glext.h:7097
#define MAX_OBJECTS
Definition: globals.h:83
int beam_collide_debris(obj_pair *pair)
Definition: beam.cpp:2814
char crw_status[MAX_WEAPONS]
object obj_used_list
Definition: object.cpp:53
void obj_find_overlap_colliders(SCP_vector< int > *overlap_list_out, SCP_vector< int > *list, int axis, bool collide)
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
#define OF_COLLIDES
Definition: object.h:104
int collide_debris_ship(obj_pair *pair)
matrix * B
Definition: lua.cpp:445
#define OF_PLAYER_SHIP
Definition: object.h:109
int key
object * objp
Definition: lua.cpp:3105
float lifeleft
Definition: weapon.h:169
int next_check_time
Definition: objcollide.h:58
float hit_dist
Definition: model.h:1122
int subtype
Definition: weapon.h:326
int collide_remove_weapons()
int reject_due_collision_groups(object *A, object *B)
Definition: objcollide.cpp:165
uint flags
Definition: object.h:201
vec3d pos
Definition: object.h:152
int objects_will_collide(object *A, object *B, float duration, float radius_scale)
Definition: objcollide.cpp:672
int beam_collide_early_out(object *a, object *b)
Definition: beam.cpp:2906
vec3d * p0
Definition: model.h:1114
void obj_remove_collider(int obj_index)
float vm_vec_mag_squared(const vec3d *v)
Definition: vecmat.cpp:339
int signature
Definition: object.h:145
int model_num
Definition: model.h:1110
GLenum type
Definition: Gl.h:1492
void obj_sort_and_collide()
int weapon_info_index
Definition: weapon.h:164
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
Definition: beam.h:114
int instance
Definition: object.h:150
int edge_hit
Definition: model.h:1130
int weapon_hitpoints
Definition: weapon.h:530
object * a
Definition: objcollide.h:55
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
float a1d[3]
Definition: pstypes.h:93
vec3d last_pos
Definition: object.h:155
SCP_unordered_map< uint, collider_pair > Collision_cached_pairs
Definition: objcollide.cpp:63
#define OF_IMMOBILE
Definition: object.h:122
void obj_add_collider(int obj_index)
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
#define SIF_BIG_SHIP
Definition: ship.h:944
int flags
Definition: model.h:1116
struct matrix::@228::@230 vec
#define DETAIL_FLAG_COLLISION
Definition: systemvars.h:116
unsigned int uint
Definition: pstypes.h:64
void obj_collide_pair(object *A, object *B)
void vm_vec_scale(vec3d *dest, float s)
Definition: vecmat.cpp:248
int timestamp_until(int stamp)
Definition: timer.cpp:242
#define nprintf(args)
Definition: pstypes.h:239
#define COLLISION_OF(a, b)
Definition: objcollide.h:63
#define CRW_CAN_DELETE
GLboolean GLboolean GLboolean GLboolean a
Definition: Glext.h:5781
int Num_pairs_checked
Definition: objcollide.cpp:34
vec3d * p1
Definition: model.h:1115
int flags
Definition: ship.h:1227
#define SIF_SMALL_SHIP
Definition: ship.h:943
float speed
Definition: physics.h:79
float lssm_stage5_vel
Definition: weapon.h:490
#define OBJ_WEAPON
Definition: object.h:33
int type
Definition: object.h:199
#define WP_LASER
Definition: weapon.h:27
void crw_check_weapon(int weapon_num, int collide_next_check)
obj_pair pair_free_list
Definition: objcollide.cpp:42
#define OBJ_DEBRIS
Definition: object.h:37
int parent
Definition: object.h:147
struct obj_pair * next
Definition: objcollide.h:59
mc_info * collide
Definition: lua.cpp:4532
int weapon_will_never_hit(object *obj_weapon, object *other, obj_pair *current_pair)
Definition: objcollide.cpp:730
hull_check p1
Definition: lua.cpp:5052
int hit_submodel
Definition: model.h:1125
int collide_debris_weapon(obj_pair *pair)
float vm_vec_normalized_dir(vec3d *dest, const vec3d *end, const vec3d *start)
Definition: vecmat.cpp:591
float vm_vec_dist(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:355
object * b
Definition: objcollide.cpp:50
int lssm_stage
Definition: weapon.h:216
#define MONITOR(function_name)
Definition: pstypes.h:454
float radius
Definition: model.h:1117
int pp_collide(vec3d *curpos, vec3d *goalpos, object *goalobjp, float radius)
Definition: objcollide.cpp:926
int collide_ship_weapon(obj_pair *pair)
#define MC_CHECK_SPHERELINE
Definition: model.h:1177
beam Beams[MAX_BEAMS]
Definition: beam.cpp:60
float vm_vec_dist_squared(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:344
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
#define MONITOR_INC(function_name, inc)
Definition: pstypes.h:457
int wi_flags
Definition: weapon.h:384
uint flags
Definition: physics.h:37
SCP_vector< int > Collision_sort_list
Definition: objcollide.cpp:44
#define OBJ_INDEX(objp)
Definition: object.h:235
vec3d hit_point_world
Definition: model.h:1124
matrix orient
Definition: object.h:153
int parent_sig
Definition: object.h:202
#define OBJ_SHIP
Definition: object.h:32
void set_hit_struct_info(collision_info_struct *hit, mc_info *mc, int submodel_rot_hit)
int ship_check_collision_fast(object *obj, object *other_obj, vec3d *hitpos)
Definition: ship.cpp:9521
#define SIF_HUGE_SHIP
Definition: ship.h:945
GLdouble GLdouble right
Definition: Glext.h:10330
int beam_collide_ship(obj_pair *pair)
Definition: beam.cpp:2397
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
vec3d vel
Definition: physics.h:77
int collide_asteroid_weapon(obj_pair *pair)
int time_ms
Definition: lua.cpp:9774
GLboolean GLboolean GLboolean b
Definition: Glext.h:5781
int num_hits
Definition: model.h:1121
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
#define vm_realloc_q(ptr, size)
Definition: pstypes.h:555
uint Game_detail_flags
Definition: systemvars.cpp:52
int Cmdline_dis_collisions
Definition: cmdline.cpp:490
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
checkobject CheckObjects[MAX_OBJECTS]
int Cmdline_old_collision_sys
Definition: cmdline.cpp:489
void obj_delete(int objnum)
Definition: object.cpp:522
void obj_add_pair(object *A, object *B, int check_time, int add_to_end)
Definition: objcollide.cpp:174
int collide_subdivide(vec3d *p0, vec3d *p1, float prad, vec3d *q0, vec3d *q1, float qrad)
Definition: objcollide.cpp:628
float vm_vec_dist_quick(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:417
int Num_pairs
Definition: objcollide.cpp:32
int num_pairs
Definition: object.h:164
void obj_pairs_close()
Definition: objcollide.cpp:70
int signature
Definition: object.h:200
#define fl2i(fl)
Definition: floating.h:33
int Num_hull_pieces
Definition: debris.cpp:38
#define MC_CHECK_MODEL
Definition: model.h:1169
int Pairs_created
Definition: objcollide.cpp:31
#define MIN_PAIRS
Definition: objcollide.cpp:26
#define fl_sqrt(fl)
Definition: floating.h:29
int ship_info_index
Definition: ship.h:539
int reject_obj_pair_on_parent(object *A, object *B)
Definition: objcollide.cpp:136
int Num_pairs_hwm
Definition: objcollide.cpp:37
vec3d last_shot
Definition: beam.h:142
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
vec3d * vm_vec_avg(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:217
object * b
Definition: objcollide.h:56
#define timestamp_elapsed(stamp)
Definition: timer.h:102
#define MAX_WEAPONS
Definition: globals.h:71
hull_check pos
Definition: lua.cpp:5050
#define CRW_IN_PAIR
int model_instance_num
Definition: model.h:1109
float vm_vec_dot(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:312
void init_collision_info_struct(collision_info_struct *cis)
GLint GLsizei count
Definition: Gl.h:1491
matrix * A
Definition: lua.cpp:444
void obj_check_all_collisions()
Definition: objcollide.cpp:530
#define CRW_NO_OBJECT
int temp
Definition: lua.cpp:4996
int Num_pairs_allocated
Definition: objcollide.cpp:33
void obj_reset_pairs()
Definition: objcollide.cpp:98
#define MAX(a, b)
Definition: pstypes.h:299
#define OBJ_BEAM
Definition: object.h:46
int collision_group_id
Definition: object.h:169
false
Definition: lua.cpp:6789
uint flags
Definition: object.h:151
float radius
Definition: object.h:154
vec3d last_start
Definition: beam.h:143
char type
Definition: object.h:146
float find_nearest_point_on_line(vec3d *nearest_point, const vec3d *p0, const vec3d *p1, const vec3d *int_pnt)
Definition: vecmat.cpp:1209
#define wp(p)
Definition: modelsinc.h:69
void obj_collide_retime_cached_pairs(int checkdly)
obj_pair * Obj_pairs
Definition: objcollide.cpp:39
matrix * orient
Definition: model.h:1112
int Weapons_created
Definition: weapons.cpp:5245
int parent_sig
Definition: object.h:148
obj_pair pair_used_list
Definition: objcollide.cpp:41
#define CRW_MAX_TO_DELETE
#define WEAPON_INDEX(wp)
Definition: weapon.h:612
const GLubyte * c
Definition: Glext.h:8376
object * a
Definition: objcollide.cpp:49
GLint left
Definition: Glext.h:7283