Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
unit_collide.cpp
Go to the documentation of this file.
1 
4 #include "vegastrike.h"
5 #include "beam.h"
6 
7 #include "bolt.h"
8 #include "gfx/mesh.h"
9 #include "unit_collide.h"
10 #include "physics.h"
11 
14 #include "collide2/basecollider.h"
15 
16 #include "hashtable.h"
17 
18 #include <string>
19 #include "vs_globals.h"
20 #include "configxml.h"
21 #include "collide.h"
22 static bool operator==( const Collidable &a, const Collidable &b )
23 {
24  return memcmp( &a, &b, sizeof (Collidable) ) == 0;
25 }
26 
28 {
29  for (unsigned int locind = 0; locind < NUM_COLLIDE_MAPS; ++locind)
30  if ( !is_null( this->location[locind] ) ) {
31  if (activeStarSystem == NULL) {
32  printf( "NONFATAL NULL activeStarSystem detected...please fix\n" );
34  }
35  static bool collidemap_sanity_check =
36  XMLSupport::parse_bool( vs_config->getVariable( "physics", "collidemap_sanity_check", "false" ) );
37  if (collidemap_sanity_check) {
38  if (0) {
41 
42  bool found = false;
43  for (i = activeStarSystem->collidemap[locind]->begin();
44  i != activeStarSystem->collidemap[locind]->end(); ++i) {
45  if (i == this->location[locind]) {
46  printf( "hussah %d\n", *i == *this->location[locind] );
47  found = true;
48  }
49  if (**i < **j) {
50  printf( "(%f %f %f) and (%f %f %f) %f < %f %d!!!",
51  (**i).GetPosition().i,
52  (**i).GetPosition().j,
53  (**i).GetPosition().k,
54  (**j).GetPosition().i,
55  (**j).GetPosition().j,
56  (**j).GetPosition().k,
57  (**i).GetPosition().MagnitudeSquared(),
58  (**j).GetPosition().MagnitudeSquared(),
59  (**i).GetPosition().MagnitudeSquared()
60  < (**j).GetPosition().MagnitudeSquared() );
61  }
62  j = i;
63  }
64  printf( "fin %d %d ", *(int*) &i, found );
66  assert( 0 );
67  }
68  }
69  activeStarSystem->collidemap[locind]->erase( this->location[locind] );
70  set_null( this->location[locind] );
71  }
72  for (int j = 0; j < GetNumMounts(); ++j)
73  if (mounts[j].type->type == weapon_info::BEAM)
74  if (mounts[j].ref.gun)
75  mounts[j].ref.gun->RemoveFromSystem( true );
76  activeStarSystem = NULL;
77 }
78 
79 void Unit::UpdateCollideQueue( StarSystem *ss, CollideMap::iterator hint[NUM_COLLIDE_MAPS] )
80 {
81  if (activeStarSystem == NULL)
82  activeStarSystem = ss;
83 
84  else
85  assert( activeStarSystem == ss );
86  for (unsigned int locind = 0; locind < NUM_COLLIDE_MAPS; ++locind)
87  if ( is_null( location[locind] ) ) {
88  assert( !isSubUnit() );
89  if ( !isSubUnit() )
90  location[locind] = ss->collidemap[locind]->insert( Collidable( this ), hint[locind] );
91  }
92 }
93 
95 {
96  static bool noUnitCollisions = XMLSupport::parse_bool( vs_config->getVariable( "physics", "no_unit_collisions", "false" ) );
97  if (isSubUnit() || killed || noUnitCollisions)
98  return;
99  for (unsigned int locind = 0; locind < NUM_COLLIDE_MAPS; ++locind)
100  if ( is_null( this->location[locind] ) )
101  this->location[locind] = this->getStarSystem()->collidemap[locind]->insert( Collidable( this ) );
103  cm->CheckCollisions( this, *this->location[Unit::UNIT_BOLT] );
104 }
105 
106 Vector Vabs( const Vector &in )
107 {
108  return Vector( in.i >= 0 ? in.i : -in.i,
109  in.j >= 0 ? in.j : -in.j,
110  in.k >= 0 ? in.k : -in.k );
111 }
112 
113 //Slated for removal 0.5
115 {
116  if ( un->GetWarpVelocity().MagnitudeSquared()*SIMULATION_ATOM*SIMULATION_ATOM < un->rSize()*un->rSize() ) {
117  return ctm;
118  } else {
119  Matrix k( ctm );
120  const Vector v( Vector( 1, 1, 1 )+Vabs( ctm.getR()*ctm.getR().Dot( un->GetWarpVelocity().Scale(
121  100*SIMULATION_ATOM/un->rSize() ) ) ) );
122  k.r[0] *= v.i;
123  k.r[1] *= v.j;
124  k.r[2] *= v.k;
125 
126  k.r[3] *= v.i;
127  k.r[4] *= v.j;
128  k.r[5] *= v.k;
129 
130  k.r[6] *= v.i;
131  k.r[7] *= v.j;
132  k.r[8] *= v.k;
133  return k;
134  }
135 }
136 
137 //do each of these bubbled subunits collide with the other unit?
138 bool Unit::Inside( const QVector &target, const float radius, Vector &normal, float &dist )
139 {
140  if ( !querySphere( target, radius ) )
141  return false;
142  normal = ( target-Position() ).Cast();
143  ::Normalize( normal );
144  //if its' in the sphre, that's enough
145  if(isPlanet() )
146  return true;
147  return false;
148 }
149 
151  QVector &bigpos,
152  Vector &bigNormal,
153  QVector &smallpos,
154  Vector &smallNormal,
155  bool bigasteroid,
156  bool smallasteroid )
157 {
158  if (smaller->colTrees == NULL || this->colTrees == NULL)
159  return false;
160  if (hull < 0) return false;
161  if (smaller->colTrees->usingColTree() == false || this->colTrees->usingColTree() == false)
162  return false;
164  Unit *bigger = this;
165 
167  csReversibleTransform smalltransform( smaller->cumulative_transformation_matrix );
169  -bigger->cumulative_transformation_matrix.p ) );
170  bigtransform.SetO2TTranslation( csVector3( 0, 0, 0 ) );
171  //we're only gonna lerp the positions for speed here... gahh!
172 
173  // Check for shield collisions here prior to checking for mesh on mesh or ray collisions below.
174  csOPCODECollider *tmpCol = smaller->colTrees->colTree( smaller, bigger->GetWarpVelocity() );
175  if ( tmpCol
176  && ( tmpCol->Collide( *bigger->colTrees->colTree( bigger,
177  smaller->GetWarpVelocity() ), &smalltransform, &bigtransform ) ) ) {
179  unsigned int numHits = csOPCODECollider::GetCollisionPairCount();
180  if (numHits) {
181  smallpos.Set( (mycollide[0].a1.x+mycollide[0].b1.x+mycollide[0].c1.x)/3.0f,
182  (mycollide[0].a1.y+mycollide[0].b1.y+mycollide[0].c1.y)/3.0f,
183  (mycollide[0].a1.z+mycollide[0].b1.z+mycollide[0].c1.z)/3.0f );
184  smallpos = Transform( smaller->cumulative_transformation_matrix, smallpos );
185  bigpos.Set( (mycollide[0].a2.x+mycollide[0].b2.x+mycollide[0].c2.x)/3.0f,
186  (mycollide[0].a2.y+mycollide[0].b2.y+mycollide[0].c2.y)/3.0f,
187  (mycollide[0].a2.z+mycollide[0].b2.z+mycollide[0].c2.z)/3.0f );
188  bigpos = Transform( bigger->cumulative_transformation_matrix, bigpos );
189  csVector3 sn, bn;
190  sn.Cross( mycollide[0].b1-mycollide[0].a1, mycollide[0].c1-mycollide[0].a1 );
191  bn.Cross( mycollide[0].b2-mycollide[0].a2, mycollide[0].c2-mycollide[0].a2 );
192  sn.Normalize();
193  bn.Normalize();
194  smallNormal.Set( sn.x, sn.y, sn.z );
195  bigNormal.Set( bn.x, bn.y, bn.z );
196  smallNormal = TransformNormal( smaller->cumulative_transformation_matrix, smallNormal );
197  bigNormal = TransformNormal( bigger->cumulative_transformation_matrix, bigNormal );
198  return true;
199  }
200  }
201  un_iter i;
202  static float rsizelim = XMLSupport::parse_float( vs_config->getVariable( "physics", "smallest_subunit_to_collide", ".2" ) );
203  clsptr bigtype = bigasteroid ? ASTEROIDPTR : bigger->isUnit();
204  clsptr smalltype = smallasteroid ? ASTEROIDPTR : smaller->isUnit();
205  if ( bigger->SubUnits.empty() == false
206  && (bigger->graphicOptions.RecurseIntoSubUnitsOnCollision == true || bigtype == ASTEROIDPTR) ) {
207  i = bigger->getSubUnits();
208  float rad = smaller->rSize();
209  for (Unit *un; (un = *i); ++i) {
210  float subrad = un->rSize();
211  if ( (bigtype != ASTEROIDPTR) && (subrad/bigger->rSize() < rsizelim) ) {
212  break;
213  }
214  if ( ( un->Position()-smaller->Position() ).Magnitude() <= subrad+rad ) {
215  if ( ( un->InsideCollideTree( smaller, bigpos, bigNormal, smallpos, smallNormal, bigtype == ASTEROIDPTR,
216  smalltype == ASTEROIDPTR ) ) )
217  return true;
218  }
219  }
220  }
221  if ( smaller->SubUnits.empty() == false
222  && (smaller->graphicOptions.RecurseIntoSubUnitsOnCollision == true || smalltype == ASTEROIDPTR) ) {
223  i = smaller->getSubUnits();
224  float rad = bigger->rSize();
225  for (Unit *un; (un = *i); ++i) {
226  float subrad = un->rSize();
227  if ( (smalltype != ASTEROIDPTR) && (subrad/smaller->rSize() < rsizelim) )
228  break;
229  if ( ( un->Position()-bigger->Position() ).Magnitude() <= subrad+rad ) {
230  if ( ( bigger->InsideCollideTree( un, bigpos, bigNormal, smallpos, smallNormal, bigtype == ASTEROIDPTR,
231  smalltype == ASTEROIDPTR ) ) )
232  return true;
233  }
234  }
235  }
236  //FIXME
237  //doesn't check all i*j options of subunits vs subunits
238  return false;
239 }
240 
241 inline float mysqr( float a )
242 {
243  return a*a;
244 }
245 
246 bool Unit::Collide( Unit *target )
247 {
248  //now first OF ALL make sure they're within bubbles of each other...
249  if ( ( Position()-target->Position() ).MagnitudeSquared() > mysqr( radial_size+target->radial_size ) )
250  return false;
251  clsptr targetisUnit = target->isUnit();
252  clsptr thisisUnit = this->isUnit();
253  static float NEBULA_SPACE_DRAG = XMLSupport::parse_float( vs_config->getVariable( "physics", "nebula_space_drag", "0.01" ) );
254  if (targetisUnit == NEBULAPTR)
255  //why? why not?
256  this->Velocity *= (1-NEBULA_SPACE_DRAG);
257  if ( target == this
258  || ( (targetisUnit != NEBULAPTR
259  && thisisUnit != NEBULAPTR)
260  && ( owner == target || target->owner == this
261  || (owner != NULL
262  && target->owner == owner) ) )
263  || (Network != NULL && _Universe->isPlayerStarship( target ) == NULL && _Universe->isPlayerStarship( this ) == NULL) )
264  return false;
265  if (targetisUnit == ASTEROIDPTR && thisisUnit == ASTEROIDPTR)
266  return false;
267  std::multimap< Unit*, Unit* > *last_collisions = &_Universe->activeStarSystem()->last_collisions;
268  last_collisions->insert( std::pair< Unit*, Unit* > ( this, target ) );
269  //unit v unit? use point sampling?
270  if ( ( this->DockedOrDocking()&(DOCKED_INSIDE|DOCKED) ) || ( target->DockedOrDocking()&(DOCKED_INSIDE|DOCKED) ) )
271  return false;
272  //now do some serious checks
273  Unit *bigger;
274  Unit *smaller;
275  if (radial_size < target->radial_size) {
276  bigger = target;
277  smaller = this;
278  } else {
279  bigger = this;
280  smaller = target;
281  }
282  bool usecoltree = (this->colTrees && target->colTrees)
283  ? this->colTrees->colTree( this, Vector( 0, 0, 0 ) ) && target->colTrees->colTree( this, Vector( 0, 0, 0 ) )
284  : false;
285  if (usecoltree) {
286  QVector bigpos, smallpos;
287  Vector bigNormal, smallNormal;
288  if ( bigger->InsideCollideTree( smaller, bigpos, bigNormal, smallpos, smallNormal ) ) {
289  if ( !bigger->isDocked( smaller ) && !smaller->isDocked( bigger ) )
290  bigger->reactToCollision( smaller, bigpos, bigNormal, smallpos, smallNormal, 10 );
291  else return false;
292  } else {return false; }
293  } else {
294  Vector normal( -1, -1, -1 );
295  float dist = 0.0;
296  if ( bigger->Inside( smaller->Position(), smaller->rSize(), normal, dist ) ) {
297  if ( !bigger->isDocked( smaller ) && !smaller->isDocked( bigger ) )
298  bigger->reactToCollision( smaller, bigger->Position(), normal, smaller->Position(), -normal, dist );
299  else return false;
300  } else {
301  return(false);
302  }
303 
304  }
305  return true;
306 }
307 
308 float globQueryShell( QVector st, QVector dir, float radius )
309 {
310  float temp1 = radius;
311  float a, b, c;
312  c = st.Dot( st );
313  c = c-temp1*temp1;
314  b = 2.0f*( dir.Dot( st ) );
315  a = dir.Dot( dir );
316  //b^2-4ac
317  c = b*b-4.0f*a*c;
318  if (c < 0 || a == 0)
319  return 0.0f;
320  a *= 2.0f;
321 
322  float tmp = ( -b+sqrt( c ) )/a;
323  c = ( -b-sqrt( c ) )/a;
324  if (tmp > 0 && tmp <= 1)
325  return (c > 0 && c < tmp) ? c : tmp;
326  else if (c > 0 && c <= 1)
327  return c;
328  return 0.0f;
329 }
330 
331 float globQuerySphere( QVector start, QVector end, QVector pos, float radius )
332 {
333  QVector st = start-pos;
334  if (st.MagnitudeSquared() < radius*radius)
335  return 1.0e-6f;
336  return globQueryShell( st, end-start, radius );
337 }
338 
339 /*
340  * This is our ray / bolt collision routine for now.
341  * Basically, this is called on a ship unit to see if any ray or bolt given by some simple vectors collide with it
342  * We should probably first check against shields and then against the colTree to see if we hit the shields first
343  * Not sure yet if that would work though... more importantly, we might have to modify end in here in order
344  * to tell calling code that the bolt should stop at a given point.
345 */
346 Unit* Unit::rayCollide( const QVector &start, const QVector &end, Vector &norm, float &distance)
347 {
348  Unit *tmp;
349  float rad = this->rSize();
351  if ( ( tmp = *SubUnits.fastIterator() ) )
352  rad += tmp->rSize();
353  if ( !globQuerySphere( start, end, cumulative_transformation_matrix.p, rad ) )
354  return NULL;
356  if ( !SubUnits.empty() ) {
358  for (Unit *un; (un = *i); ++i)
359  if ( ( tmp = un->rayCollide( start, end, norm, distance) ) != 0 )
360  return tmp;
361  }
362  }
365  static bool sphere_test = XMLSupport::parse_bool( vs_config->getVariable( "physics", "sphere_collision", "true" ) );
366  distance = querySphereNoRecurse( start, end );
367  if (distance > 0.0f || (this->colTrees&&this->colTrees->colTree( this, this->GetWarpVelocity() )&&!sphere_test)) {
368  Vector coord;
369  /* Set up points and ray to send to ray collider. */
370  Opcode::Point rayOrigin(st.i,st.j,st.k);
371  Opcode::Point rayDirection(ed.i,ed.j,ed.k);
372  Opcode::Ray boltbeam(rayOrigin,rayDirection);
373  if(this->colTrees){
374  // Retrieve the correct scale'd collider from the unit's collide tree.
375  csOPCODECollider *tmpCol = this->colTrees->colTree( this, this->GetWarpVelocity() );
376  QVector del(end-start);
377  //Normalize(del);
378  norm = ((start+del*distance)-Position()).Cast();
379  Normalize(norm);
380  //RAY COLLIDE does not yet set normal, use that of the sphere center to current loc
381  if (tmpCol==NULL) {
382 
383  return this;
384  }
385  if(tmpCol->rayCollide(boltbeam,norm,distance)){
386  // compute real distance
387  distance = (end-start).Magnitude() * distance;
388 
389  // NOTE: Here is where we need to retrieve the point on the ray that we collided with the mesh, and set it to end, create the normal and set distance
390  VSFileSystem::vs_dprintf(3,"Beam collide with %p, distance %f\n",this,distance);
391  return(this);
392  }
393  } else {//no col trees = a sphere
394  // compute real distance
395  distance = (end-start).Magnitude() * distance;
396 
397  VSFileSystem::vs_dprintf(3,"Beam collide with %p, distance %f\n",this,distance);
398  return(this);
399  }
400  } else {
401  return (NULL);
402  }
403  return(NULL);
404 }
405 
406 bool Unit::querySphere( const QVector &pnt, float err ) const
407 {
408  int i;
410 
411  Vector TargetPoint( tmpo->getP() );
412 #ifdef VARIABLE_LENGTH_PQR
413  //adjust the ship radius by the scale of local coordinates
414  double SizeScaleFactor = sqrt( TargetPoint.Dot( TargetPoint ) );
415 #endif
416  if ( nummesh() < 1 && isPlanet() ) {
417  TargetPoint = (tmpo->p-pnt).Cast();
418  if (TargetPoint.Dot( TargetPoint )
419  < err*err
421 #ifdef VARIABLE_LENGTH_PQR
422  *SizeScaleFactor*SizeScaleFactor
423 #endif
424  +
425 #ifdef VARIABLE_LENGTH_PQR
426  SizeScaleFactor*
427 #endif
428  2.0f*err*radial_size
429  )
430  return true;
431  } else {
432  for (i = 0; i < nummesh(); i++) {
433  TargetPoint = (Transform( *tmpo, meshdata[i]->Position().Cast() )-pnt).Cast();
434  if (TargetPoint.Dot( TargetPoint )
435  < err*err
436  +meshdata[i]->rSize()*meshdata[i]->rSize()
437 #ifdef VARIABLE_LENGTH_PQR
438  *SizeScaleFactor*SizeScaleFactor
439 #endif
440  +
441 #ifdef VARIABLE_LENGTH_PQR
442  SizeScaleFactor*
443 #endif
444  2.0f*err*meshdata[i]->rSize()
445  )
446  return true;
447  }
448  }
450  if ( !SubUnits.empty() ) {
452  for (const Unit *un; (un = *i); ++i)
453  if ( (un)->querySphere( pnt, err ) )
454  return true;
455  }
456  }
457  return false;
458 }
459 
460 float Unit::querySphere( const QVector &start, const QVector &end, float min_radius ) const
461 {
462  if ( !SubUnits.empty() ) {
464  for (const Unit *un; (un = *i); ++i) {
465  float tmp;
466  if ( ( tmp = un->querySphere( start, end, min_radius ) ) != 0 )
467  return tmp;
468  }
469  }
470  return querySphereNoRecurse( start, end, min_radius );
471 }
472 
473 //does not check inside sphere
474 float Unit::querySphereNoRecurse( const QVector &start, const QVector &end, float min_radius ) const
475 {
476  int i;
477  double tmp;
478  for (i = 0; i < nummesh(); i++) {
479  if ( ( meshdata[i]->Position().Magnitude() > this->rSize() ) || ( meshdata[i]->rSize() > 30+this->rSize() ) )
480  continue;
481  if (isUnit() == PLANETPTR && i > 0)
482  break;
483  double a, b, c;
485 
486  QVector dir = end-start; //now start and end are based on mesh's position
487  c = st.Dot( st );
488  double temp1 = ( min_radius+meshdata[i]->rSize() );
489  //if (st.MagnitudeSquared()<temp1*temp1) //UNCOMMENT if you want inside sphere to count...otherwise...
490  //return 1.0e-6;
491  if (min_radius != -FLT_MAX)
492  c = c-temp1*temp1;
493  else
494  c = temp1;
495 #ifdef VARIABLE_LENGTH_PQR
496  c *= SizeScaleFactor*SizeScaleFactor;
497 #endif
498  b = 2.0f*( dir.Dot( st ) );
499  a = dir.Dot( dir );
500  //b^2-4ac
501  if (min_radius != -FLT_MAX)
502  c = b*b-4.0f*a*c;
503  else
504  c = FLT_MAX;
505  if (c < 0 || a == 0)
506  continue;
507  a *= 2.0f;
508 
509  tmp = ( -b+sqrt( c ) )/a;
510  c = ( -b-sqrt( c ) )/a;
511  if (tmp > 0 && tmp <= 1)
512  return (c > 0 && c < tmp) ? c : tmp;
513  else if (c > 0 && c <= 1)
514  return c;
515  }
516  return 0.0f;
517 }
518