Exemple #1
0
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;
}
Exemple #2
0
/**********************************************************
		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;
}
Exemple #4
0
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!
}
Exemple #5
0
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; 
}