CBaseEntity *CBasePlayer::FindUseEntity() { Vector forward, up; EyeVectors( &forward, NULL, &up ); trace_t tr; // Search for objects in a sphere (tests for entities that are not solid, yet still useable) Vector searchCenter = EyePosition(); // NOTE: Some debris objects are useable too, so hit those as well // A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too. int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP; #ifdef CSTRIKE_DLL useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_OPAQUE_AND_NPCS; #endif #ifdef HL1_DLL useableContents = MASK_SOLID; #endif #ifndef CLIENT_DLL CBaseEntity *pFoundByTrace = NULL; #endif // UNDONE: Might be faster to just fold this range into the sphere query CBaseEntity *pObject = NULL; float nearestDist = FLT_MAX; // try the hit entity if there is one, or the ground entity if there isn't. CBaseEntity *pNearest = NULL; const int NUM_TANGENTS = 8; // trace a box at successive angles down // forward, 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15 const float tangents[NUM_TANGENTS] = { 0, 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f }; for ( int i = 0; i < NUM_TANGENTS; i++ ) { if ( i == 0 ) { UTIL_TraceLine( searchCenter, searchCenter + forward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr ); } else { Vector down = forward - tangents[i]*up; VectorNormalize(down); UTIL_TraceHull( searchCenter, searchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr ); } pObject = tr.m_pEnt; #ifndef CLIENT_DLL pFoundByTrace = pObject; #endif bool bUsable = IsUseableEntity(pObject, 0); while ( pObject && !bUsable && pObject->GetMoveParent() ) { pObject = pObject->GetMoveParent(); bUsable = IsUseableEntity(pObject, 0); } if ( bUsable ) { Vector delta = tr.endpos - tr.startpos; float centerZ = CollisionProp()->WorldSpaceCenter().z; delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z ); float dist = delta.Length(); if ( dist < PLAYER_USE_RADIUS ) { #ifndef CLIENT_DLL if ( sv_debug_player_use.GetBool() ) { NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); } if ( pObject->MyNPCPointer() && pObject->MyNPCPointer()->IsPlayerAlly( this ) ) { // If about to select an NPC, do a more thorough check to ensure // that we're selecting the right one from a group. pObject = DoubleCheckUseNPC( pObject, searchCenter, forward ); } #endif if ( sv_debug_player_use.GetBool() ) { Msg( "Trace using: %s\n", pObject ? pObject->GetDebugName() : "no usable entity found" ); } pNearest = pObject; // if this is directly under the cursor just return it now if ( i == 0 ) return pObject; } } } // check ground entity first // if you've got a useable ground entity, then shrink the cone of this search to 45 degrees // otherwise, search out in a 90 degree cone (hemisphere) if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) ) { pNearest = GetGroundEntity(); } if ( pNearest ) { // estimate nearest object by distance from the view vector Vector point; pNearest->CollisionProp()->CalcNearestPoint( searchCenter, &point ); nearestDist = CalcDistanceToLine( point, searchCenter, forward ); if ( sv_debug_player_use.GetBool() ) { Msg("Trace found %s, dist %.2f\n", pNearest->GetClassname(), nearestDist ); } } for ( CEntitySphereQuery sphere( searchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { if ( !pObject ) continue; if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) continue; // see if it's more roughly in front of the player than previous guess Vector point; pObject->CollisionProp()->CalcNearestPoint( searchCenter, &point ); Vector dir = point - searchCenter; VectorNormalize(dir); float dot = DotProduct( dir, forward ); // Need to be looking at the object more or less if ( dot < 0.8 ) continue; float dist = CalcDistanceToLine( point, searchCenter, forward ); if ( sv_debug_player_use.GetBool() ) { Msg("Radius found %s, dist %.2f\n", pObject->GetClassname(), dist ); } if ( dist < nearestDist ) { // Since this has purely been a radius search to this point, we now // make sure the object isn't behind glass or a grate. trace_t trCheckOccluded; UTIL_TraceLine( searchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded ); if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject ) { pNearest = pObject; nearestDist = dist; } } } #ifndef CLIENT_DLL if ( !pNearest ) { // Haven't found anything near the player to use, nor any NPC's at distance. // Check to see if the player is trying to select an NPC through a rail, fence, or other 'see-though' volume. trace_t trAllies; UTIL_TraceLine( searchCenter, searchCenter + forward * PLAYER_USE_RADIUS, MASK_OPAQUE_AND_NPCS, this, COLLISION_GROUP_NONE, &trAllies ); if ( trAllies.m_pEnt && IsUseableEntity( trAllies.m_pEnt, 0 ) && trAllies.m_pEnt->MyNPCPointer() && trAllies.m_pEnt->MyNPCPointer()->IsPlayerAlly( this ) ) { // This is an NPC, take it! pNearest = trAllies.m_pEnt; } } if ( pNearest && pNearest->MyNPCPointer() && pNearest->MyNPCPointer()->IsPlayerAlly( this ) ) { pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); } if ( sv_debug_player_use.GetBool() ) { if ( !pNearest ) { NDebugOverlay::Line( searchCenter, tr.endpos, 255, 0, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 255, 0, 0, true, 30 ); } else if ( pNearest == pFoundByTrace ) { NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); } else { NDebugOverlay::Box( pNearest->WorldSpaceCenter(), Vector(-8, -8, -8), Vector(8, 8, 8), 0, 255, 0, true, 30 ); } } #endif if ( sv_debug_player_use.GetBool() ) { Msg( "Radial using: %s\n", pNearest ? pNearest->GetDebugName() : "no usable entity found" ); } return pNearest; }
/********************************************************** C# code from http://www.codeproject.com/Articles/15573/2D-Polyhedron-Collision-Detection */ bool Polyhedron::DoesCollide(Vector2* Velocity, Polyhedron* CheckWith) { bool Intersect = true; bool WillIntersect = true; int edgeCountA = Edges->count; int edgeCountB = CheckWith->Edges->count; // float minIntervalDistance = 99999999; // Vector2* translationAxis = new Vector2( 0, 0 ); Vector2* edge; // Loop through all the edges of both Polyhedrons for( int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++ ) { if( edgeIndex < edgeCountA ) { edge = Edges->ItemAt<Vector2*>(edgeIndex); } else { edge = CheckWith->Edges->ItemAt<Vector2*>(edgeIndex - edgeCountA); } // ===== 1. Find if the Polyhedrons are currently intersecting ===== // Find the axis perpendicular to the current edge Vector2* axis = new Vector2(-edge->Y, edge->X); axis->Normalise(); // Find the projection of the Polyhedron on the current axis float minA = 0; float minB = 0; float maxA = 0; float maxB = 0; Project(axis, &minA, &maxA); CheckWith->Project(axis, &minB, &maxB); // Check if the Polyhedron projections are currentlty intersecting if( IntervalDistance(minA, maxA, minB, maxB) > 0 ) { Intersect = false; } // ===== 2. Now find if the Polyhedrons *will* intersect ===== // Project the velocity on the current axis float velocityProjection = axis->DotProduct( Velocity ); // Get the projection of Polyhedron A during the movement if( velocityProjection < 0 ) { minA += velocityProjection; } else { maxA += velocityProjection; } // Do the same test as above for the new projection float intervalDistance = IntervalDistance( minA, maxA, minB, maxB ); if( intervalDistance > 0 ) { WillIntersect = false; } delete axis; // If the Polyhedrons are not intersecting and won't intersect, exit the loop if( !Intersect && !WillIntersect) { break; } /* // Check if the current interval distance is the minimum one. If so store // the interval distance and the current distance. // This will be used to calculate the minimum translation vector intervalDistance = Math.Abs(intervalDistance); if (intervalDistance < minIntervalDistance) { minIntervalDistance = intervalDistance; translationAxis = axis; Vector d = PolyhedronA.Center - PolyhedronB.Center; if (d.DotProduct(translationAxis) < 0) translationAxis = -translationAxis; } */ // int xcv = 1; } // The minimum translation vector can be used to push the Polyhedrons appart. // First moves the Polyhedrons by their velocity // then move PolyhedronA by MinimumTranslationVector. // if (result.WillIntersect) result.MinimumTranslationVector = translationAxis * minIntervalDistance; return (Intersect | WillIntersect); }
bool Collisions::IsPolygonIntersectsPolygon(Polygon2 & poly1, Polygon2 & poly2) { //#define DEBUG_DRAW_INTERSECTIONS Vector2 * points1 = poly1.GetPoints(); Vector2 * points2 = poly2.GetPoints(); separationAxes.clear(); for (int32 index1 = 0; index1 < poly1.pointCount; ++index1) { int32 index2 = (index1 + 1 != poly1.pointCount) ? (index1 + 1) : (0); Vector2 line = points1[index2] - points1[index1]; Vector2 normal = Vector2(line.y, -line.x); normal.Normalize(); AddSeparationAxis(normal); #if defined(DEBUG_DRAW_INTERSECTIONS) RenderManager::Instance()->SetColor(0.0f, 0.0f, 1.0f, 1.0f); RenderHelper::DrawLine(points1[index1] + (line / 2), points1[index1] + (line / 2) + normal * 10); #endif } for (int32 index1 = 0; index1 < poly2.pointCount; ++index1) { int32 index2 = (index1 + 1 != poly2.pointCount) ? (index1 + 1) : (0); Vector2 line = points2[index2] - points2[index1]; Vector2 normal = Vector2(line.y, -line.x); normal.Normalize(); AddSeparationAxis(normal); #if defined(DEBUG_DRAW_INTERSECTIONS) RenderManager::Instance()->SetColor(0.0f, 1.0f, 0.0f, 1.0f); RenderHelper::DrawLine(points2[index1] + (line / 3), points2[index1] + (line / 3) + normal * 10); #endif } size_t size = separationAxes.size(); #if defined(DEBUG_DRAW_INTERSECTIONS) for (size_t index = 0; index < size; ++index) { Vector2 axis = separationAxes[index]; RenderManager::Instance()->SetColor(1.0f, 0.0f, 0.0f, 1.0f); RenderHelper::DrawLine(Vector2(50.0f, 50.0f), Vector2(50.0f, 50.0f) + axis * 1000); } #endif for (size_t index = 0; index < size; ++index) { Vector2 axis = separationAxes[index]; float32 p1Min, p1Max; ProjectPolygon(axis, poly1, p1Min, p1Max); float32 p2Min, p2Max; ProjectPolygon(axis, poly2, p2Min, p2Max); #if defined(DEBUG_DRAW_INTERSECTIONS) RenderManager::Instance()->SetColor(0.0f, 1.0f, 1.0f, 1.0f); Vector2 norm = Vector2(axis.y, -axis.x); RenderHelper::DrawLine(Vector2(50.0f, 50.0f) + axis * p1Min + norm * 2.0f, Vector2(50.0f, 50.0f) + axis * p1Max + norm * 2.0f); RenderManager::Instance()->SetColor(1.0f, 1.0f, 0.0f, 1.0f); RenderHelper::DrawLine(Vector2(50.0f, 50.0f) + axis * p2Min - norm * 2.0f, Vector2(50.0f, 50.0f) + axis * p2Max - norm * 2.0f); #endif if (IntervalDistance(p1Min, p1Max, p2Min, p2Max) > 0) return false; } return true; }
bool Physics::DetectCollision( PhysicsBody* B1, PhysicsBody* B2 ) { float MinDistance = 10000.0f; //Initialize the length of the collision vector to a relatively large value for( int I = 0; I < B1->EdgeCount + B2->EdgeCount; I++ ) { //Just a fancy way of iterating through all of the edges of both bodies at once Edge* E; if( I < B1->EdgeCount ) E = B1->Edges[ I ]; else E = B2->Edges[ I - B1->EdgeCount ]; //This will skip edges that lie totally inside the bodies, as they don't matter. //The boundary flag has to be set manually and defaults to true if( !E->Boundary ) continue; Vec2 Axis( E->V1->Position.Y - E->V2->Position.Y, E->V2->Position.X - E->V1->Position.X ); //Calculate the perpendicular to this edge and normalize it Axis.Normalize(); float MinA, MinB, MaxA, MaxB; //Project both bodies onto the perpendicular B1->ProjectToAxis( Axis, MinA, MaxA ); B2->ProjectToAxis( Axis, MinB, MaxB ); float Distance = IntervalDistance( MinA, MaxA, MinB, MaxB ); //Calculate the distance between the two intervals if( Distance > 0.0f ) //If the intervals don't overlap, return, since there is no collision return false; else if( abs( Distance ) < MinDistance ) { MinDistance = abs( Distance ); CollisionInfo.Normal = Axis; //Save collision information for later CollisionInfo.E = E; //Store the edge, as it is the collision edge } } CollisionInfo.Depth = MinDistance; if( CollisionInfo.E->Parent != B2 ) { //Ensure that the body containing the collision edge lies in B2 and the one conatining the collision vertex in B1 PhysicsBody* Temp = B2; B2 = B1; B1 = Temp; } int Sign = SGN( CollisionInfo.Normal*( B1->Center - B2->Center ) ); //This is needed to make sure that the collision normal is pointing at B1 //Remember that the line equation is N*( R - R0 ). We choose B2->Center as R0; the normal N is given by the collision normal if( Sign != 1 ) CollisionInfo.Normal = -CollisionInfo.Normal; //Revert the collision normal if it points away from B1 Vec2 CollisionVector = CollisionInfo.Normal*CollisionInfo.Depth; float SmallestD = 10000.0f; //Initialize the smallest distance to a large value for( int I = 0; I < B1->VertexCount; I++ ) { float Distance = CollisionInfo.Normal*( B1->Vertices[ I ]->Position - B2->Center ); //Measure the distance of the vertex from the line using the line equation if( Distance < SmallestD ) { //If the measured distance is smaller than the smallest distance reported so far, set the smallest distance and the collision vertex SmallestD = Distance; CollisionInfo.V = B1->Vertices[ I ]; } } return true; //There is no separating axis. Report a collision! }
bool CW::PhysicsEngine::DetectCollision(PhysicsBody* b1, PhysicsBody* b2) { float MinDistance = 10000.0f; for (Edge* e : m_Edges) { if (!e->IsCollidable) continue; // Calculate the perpendicular to this edge and normalize it Vector2 Axis(e->P1->Position.y - e->P2->Position.y, e->P2->Position.x - e->P1->Position.x); Axis.Normalize(); // Project both bodies onto the perpendicular float MinA, MinB, MaxA, MaxB; b1->ProjectToAxis( Axis, MinA, MaxA ); b2->ProjectToAxis( Axis, MinB, MaxB ); // Calculate the distance between the two intervals float Distance = IntervalDistance(MinA, MaxA, MinB, MaxB); //If the intervals don't overlap, return, since there is no collision if(Distance > 0.0f) return false; else if(abs(Distance) < MinDistance) { MinDistance = abs(Distance); CollisionInfo.Normal = Axis; CollisionInfo.E = e; // We found our edge } } CollisionInfo.Depth = MinDistance; // Make sure that the body containing the collision edge lies in B2 and the one conatining the collision vertex in B1 if(CollisionInfo.E->Parent != b2) { PhysicsBody* Temp = b2; b2 = b1; b1 = Temp; } // Make sure collision normal is pointing at b1 int Sign = SGN(CollisionInfo.Normal*(b1->COM - b2->COM)); //Remember that the line equation is N*( R - R0 ). We choose B2->Center as R0; the normal N is given by the collision normal if(Sign != 1) CollisionInfo.Normal = -CollisionInfo.Normal; //Revert the collision normal if it points away from B1 Vector2 CollisionVector = CollisionInfo.Normal*CollisionInfo.Depth; //Initialize the smallest distance to a large value float SmallestD = 10000.0f; for (VerletPoint* p : b1->Vertices) { float Distance = CollisionInfo.Normal*(p->Position - b2->COM); if( Distance < SmallestD ) { SmallestD = Distance; CollisionInfo.V = p; } } //There is no separating axis. Report a collision! return true; }