// check whether a ship is in range of a stargate ----------------------------- // PRIVATE int ShipInStargateRange( Stargate *stargate, ShipObject *ship, geomv_t range ) { ASSERT( stargate != NULL ); ASSERT( ship != NULL ); //NOTE: // the ship is treated as a sphere for activation // range detection. Vector3 gatenormal; FetchZVector( stargate->ObjPosition, &gatenormal ); Vertex3 gatepos; FetchTVector( stargate->ObjPosition, &gatepos ); Vertex3 shippos; FetchTVector( ship->ObjPosition, &shippos ); geomv_t shipdot = -DOT_PRODUCT( &gatenormal, &shippos ); geomv_t gatedot = -DOT_PRODUCT( &gatenormal, &gatepos ); geomv_t distance = shipdot - gatedot; // bounding sphere touching in negative halfspace is still ok distance += ship->BoundingSphere; // not in range if ship in wrong halfspace if ( GEOMV_NEGATIVE( distance ) ) { return FALSE; } // in range if ship bounding sphere intersects gate range hemisphere range += ship->BoundingSphere * 2; return ( distance < range ); }
// check whether a ship is in jump range of a stargate ------------------------ // PRIVATE int ShipInStargateJumpRange( Stargate *stargate, ShipObject *ship, geomv_t range ) { ASSERT( stargate != NULL ); ASSERT( ship != NULL ); //NOTE: // the ship is treated as a point for jump // range detection. Vector3 gatenormal; FetchZVector( stargate->ObjPosition, &gatenormal ); Vertex3 gatepos; FetchTVector( stargate->ObjPosition, &gatepos ); Vertex3 shippos; FetchTVector( ship->ObjPosition, &shippos ); geomv_t shipdot = -DOT_PRODUCT( &gatenormal, &shippos ); geomv_t gatedot = -DOT_PRODUCT( &gatenormal, &gatepos ); geomv_t distance = shipdot - gatedot; // not in range if ship in wrong halfspace if ( GEOMV_NEGATIVE( distance ) ) { return FALSE; } // check whether inside of boundingsphere around stargate Vector3 stargate_ship; VECSUB( &stargate_ship, &shippos, &gatepos ); geomv_t stargate_ship_len = VctLenX( &stargate_ship ); if ( stargate_ship_len > stargate->BoundingSphere ) { return FALSE; } // inside the activation distance/range ? if ( distance < range ) { Vector3 shipnormal; FetchZVector( ship->ObjPosition, &shipnormal ); // ship must be flying approximately head-on into the gate //FIXME: cone_angle like for teleporter geomv_t dirdot = DOT_PRODUCT( &gatenormal, &shipnormal ); return ( dirdot > FLOAT_TO_GEOMV( 0.7f ) ); } return FALSE; }
// collision detection helper function ---------------------------------------- // int G_CollDet::_CheckShipProjectileDisjoint() { // extend sphere for point sampling geomv_t hugesphere = bd_sphere * CONSERVATIVE_POINTSAMPLE_EXPANSION; // vector to center Vector3 vectest; vectest.X = obj_pos.X - test_pos.X; vectest.Y = obj_pos.Y - test_pos.Y; vectest.Z = obj_pos.Z - test_pos.Z; // check enlarged vicinity for early-out if ( vectest.X >= hugesphere ) return TRUE; if ( vectest.Y >= hugesphere ) return TRUE; if ( vectest.Z >= hugesphere ) return TRUE; hugesphere = -hugesphere; if ( vectest.X <= hugesphere ) return TRUE; if ( vectest.Y <= hugesphere ) return TRUE; if ( vectest.Z <= hugesphere ) return TRUE; // test shield (bounding sphere) if ( test_shield ) { // squared distance to current position geomv_t vectestl2 = DOT_PRODUCT( &vectest, &vectest ); // trivial intersect if ( vectestl2 < bd_sphere2 ) return FALSE; // squared distance to previous position Vector3 vecprev; vecprev.X = obj_pos.X - prev_pos.X; vecprev.Y = obj_pos.Y - prev_pos.Y; vecprev.Z = obj_pos.Z - prev_pos.Z; geomv_t vecprevl2 = DOT_PRODUCT( &vecprev, &vecprev ); // trivial intersect if ( vecprevl2 < bd_sphere2 ) return FALSE; // calc nearest point on line to center Vector3 vecline; vecline.X = test_pos.X - prev_pos.X; vecline.Y = test_pos.Y - prev_pos.Y; vecline.Z = test_pos.Z - prev_pos.Z; geomv_t scaledt = DOT_PRODUCT( &vecline, &vecprev ); // ray pointing away from sphere? if ( GEOMV_NEGATIVE( scaledt ) ) return TRUE; // ray not yet at the sphere? geomv_t veclinel2 = DOT_PRODUCT( &vecline, &vecline ); if ( scaledt >= veclinel2 ) return TRUE; ASSERT( veclinel2 > GEOMV_VANISHING ); // calc squared distance of nearest point geomv_t dot2 = GEOMV_MUL( scaledt, scaledt ); geomv_t tlen2 = GEOMV_DIV( dot2, veclinel2 ); //NOTE: // the actual point of impact could now be // easily calculated without a sqrt(). // collision if nearest point inside return ( ( vecprevl2 - tlen2 ) >= bd_sphere2 ); } else { ASSERT( FALSE ); /* // shield is down: test actual object #ifdef CHECK_BOX_BEFORE_BSP_COLLISION // clip lineseg into box geomv_t isect; geomv_t seglen; geomv_t vtx0t = GEOMV_0; geomv_t vtx1t = GEOMV_1; int collside = -1; static geomv_t vanish = FLOAT_TO_GEOMV( 0.00001 ); // cull/clip against +Z geomv_t curax = obj_pos.Z + bd_sphere; geomv_t dist0 = prev_pos.Z - curax; geomv_t dist1 = test_pos.Z - curax; dword outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | ( DW32( dist1 ) & 0x80000000 ); // both outside? if ( outcode == 0x00000000 ) return TRUE; // at least one outside? if ( outcode != 0xc0000000 ) { if ( outcode == 0x40000000 ) { // v0 inside, v1 outside ABS_GEOMV( dist0 ); seglen = dist0 + dist1; if ( seglen <= vanish ) return TRUE; vtx1t = GEOMV_DIV( dist0, seglen ); } else { // v0 outside, v1 inside ABS_GEOMV( dist1 ); seglen = dist0 + dist1; if ( seglen <= vanish ) return TRUE; vtx0t = GEOMV_DIV( dist0, seglen ); collside = 0; } } #define AXIAL_CLIP_T(s) { \ \ outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | \ ( DW32( dist1 ) & 0x80000000 ); \ if ( outcode == 0x00000000 ) \ return TRUE; \ if ( outcode != 0xc0000000 ) { \ if ( outcode == 0x40000000 ) { \ ABS_GEOMV( dist0 ); \ seglen = dist0 + dist1; \ if ( seglen <= vanish ) \ return TRUE; \ isect = GEOMV_DIV( dist0, seglen ); \ if ( isect <= vtx0t ) \ return TRUE; \ if ( isect < vtx1t ) \ vtx1t = isect; \ } else { \ ABS_GEOMV( dist1 ); \ seglen = dist0 + dist1; \ if ( seglen <= vanish ) \ return TRUE; \ isect = GEOMV_DIV( dist0, seglen ); \ if ( isect >= vtx1t ) \ return TRUE; \ if ( isect > vtx0t ) { \ vtx0t = isect; \ collside = (s); \ } \ } \ } \ } // cull/clip against -Z curax = obj_pos.Z - bd_sphere; dist0 = curax - prev_pos.Z; dist1 = curax - test_pos.Z; AXIAL_CLIP_T( 1 ); // cull/clip against +X curax = obj_pos.X + bd_sphere; dist0 = prev_pos.X - curax; dist1 = test_pos.X - curax; AXIAL_CLIP_T( 2 ); // cull/clip against -X curax = obj_pos.X - bd_sphere; dist0 = curax - prev_pos.X; dist1 = curax - test_pos.X; AXIAL_CLIP_T( 3 ); // cull/clip against +Y curax = obj_pos.Y + bd_sphere; dist0 = prev_pos.Y - curax; dist1 = test_pos.Y - curax; AXIAL_CLIP_T( 4 ); // cull/clip against -Y curax = obj_pos.Y - bd_sphere; dist0 = curax - prev_pos.Y; dist1 = curax - test_pos.Y; AXIAL_CLIP_T( 5 ); //NOTE: // collside now contains the code of the collider side // (-1 means the lineseg starts in the bounding box) //TODO: // use already clipped segment in BSP test? #endif // CHECK_BOX_BEFORE_BSP_COLLISION // test actual object (polygon accurate) // transform line vertices into object-space CalcOrthoInverse( cur_ship->ObjPosition, DestXmatrx ); Vertex3 v0; MtxVctMUL( DestXmatrx, &prev_pos, &v0 ); Vertex3 v1; MtxVctMUL( DestXmatrx, &test_pos, &v1 ); // assume collision if no bsp tree CullBSPNode *node = cur_ship->AuxBSPTree; if ( node == NULL ) return FALSE; dword nodeid = 0; geomv_t impact_t; int collided = BSP_FindColliderLine( node, &v0, &v1, &nodeid, &impact_t ); // no collision? if ( !collided ) return TRUE; // react only if not started in solid leaf if ( nodeid > 0 ) { // fetch collider node = &cur_ship->AuxBSPTree[ nodeid ]; // calc collision position (object-space) v1.X -= v0.X; v1.Y -= v0.Y; v1.Z -= v0.Z; v1.X = v0.X + GEOMV_MUL( v1.X, impact_t ); v1.Y = v0.Y + GEOMV_MUL( v1.Y, impact_t ); v1.Z = v0.Z + GEOMV_MUL( v1.Z, impact_t ); // create hull impact particles SFX_HullImpact( cur_ship, &v1, &node->plane ); } // disable shield test_shield = FALSE; */ } return FALSE; }