Example #1
0
//------------------------------------------------------------------------------
//from[http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_7_Kd-Trees_and_More_Speed.shtml]
bool Triangle::Intersect( const Ray& _ray, Intersection& o_intersection ) const
{
	/*      v0
		/\
	 b /   \c
	v1/_____\ v2
		a
	*/
	Vector b = m_vertex[1] - m_vertex[0];
	Vector c = m_vertex[2] - m_vertex[0];
	Vector normal = b.Cross (c);
	//triangle is a line
	if( RealCompare( normal.Dot(normal), 0.0f, 0.0000000001 ) )
		return false;
	Normalise(normal);

	//ray-plane intersection
	float rayParameter = normal.Dot ( m_vertex[0] - _ray.Origin () ) / normal.Dot ( _ray.Direction () );
	//no intersection on the plane
	if ( rayParameter < 0.0f )
	{
		return false;
	}

	//if on the plane
	//solve the problem on 2d
	//get dominant axis of normal
    uint8_t axis = normal.DominantAxis();
	// p = p1*v1 + p2*v2 + p3*v3
	// p1+ p2+ p3 =1
	// p2 (  v2 - v1 ) + p3 (  v3 - v1 ) = intersection - v1
	Vector intersectionPos = _ray.Origin () + rayParameter * _ray.Direction ();
	Vector diff = intersectionPos - m_vertex[0];
	float bU, bV, cU, cV, diffU, diffV;
	uint8_t axisU = ( axis + 1 )%3;
	uint8_t axisV = ( axis + 2 )%3;
	diffU = diff[axisU];
	diffV = diff[axisV];
	bU = b[axisU];
	bV = b[axisV];
	cU = c[axisU];
	cV = c[axisV];
	float tmp =  bU * cV - bV * cU ;
	float p2 = (  cV * diffU - cU * diffV ) / tmp;
	if ( p2<0.0 )
	{
		return false;
	}
	float p3 = (  bU * diffV - bV * diffU ) / tmp;
	if (  p3<0.0 )
	{
		return false;
	}
	if (  p2+ p3> 1.0 )
	{
		return false;
	}
	float p1 = 1.0 - p2 - p3;
	Vector averageNormal = p1 * m_normal[0] + p2 * m_normal[1] + p3 * m_normal[2];
	Vector2D averageTexCoord = p1 * m_texture[0] + p2 * m_texture[1] + p3 * m_texture[2];
	Normalise(averageNormal);
	if( averageNormal.Dot( _ray.Direction() ) > 0 )
	{
		return false;
	}
	
	if( m_pMaterial->kf() > 0 && _ray.Direction().Dot( averageNormal ) > 0 )
	{
		//when calculating refraction for the ray inside object
		o_intersection = Intersection ( intersectionPos, averageNormal, averageTexCoord, rayParameter, g_air );
	}
	else if( m_pMaterial->kf()==0 && _ray.Direction().Dot(averageNormal ) > 0)
	{
		printf("back face\n");
		o_intersection = Intersection ( intersectionPos, averageNormal, averageTexCoord, rayParameter, m_pMaterial );
	}
	else
		o_intersection = Intersection ( intersectionPos, averageNormal, averageTexCoord, rayParameter, m_pMaterial );
	return true;
}
void C_HL2MP_Player::AvoidPlayers( CUserCmd *pCmd )
{
	// This is only used in team play.
	if ( !HL2MPRules()->IsTeamplay() )
		return;

	// Don't test if the player doesn't exist or is dead.
	if ( IsAlive() == false )
		return;

	C_Team *pTeam = ( C_Team * )GetTeam();
	if ( !pTeam )
		return;

	// Up vector.
	static Vector vecUp( 0.0f, 0.0f, 1.0f );

	Vector vecHL2MPPlayerCenter = GetAbsOrigin();
	Vector vecHL2MPPlayerMin = GetPlayerMins();
	Vector vecHL2MPPlayerMax = GetPlayerMaxs();
	float flZHeight = vecHL2MPPlayerMax.z - vecHL2MPPlayerMin.z;
	vecHL2MPPlayerCenter.z += 0.5f * flZHeight;
	VectorAdd( vecHL2MPPlayerMin, vecHL2MPPlayerCenter, vecHL2MPPlayerMin );
	VectorAdd( vecHL2MPPlayerMax, vecHL2MPPlayerCenter, vecHL2MPPlayerMax );

	// Find an intersecting player or object.
	int nAvoidPlayerCount = 0;
	C_HL2MP_Player *pAvoidPlayerList[MAX_PLAYERS];

	C_HL2MP_Player *pIntersectPlayer = NULL;
	float flAvoidRadius = 0.0f;

	Vector vecAvoidCenter, vecAvoidMin, vecAvoidMax;
	for ( int i = 0; i < pTeam->GetNumPlayers(); ++i )
	{
		C_HL2MP_Player *pAvoidPlayer = static_cast< C_HL2MP_Player * >( pTeam->GetPlayer( i ) );
		if ( pAvoidPlayer == NULL )
			continue;
		// Is the avoid player me?
		if ( pAvoidPlayer == this )
			continue;

		// Save as list to check against for objects.
		pAvoidPlayerList[nAvoidPlayerCount] = pAvoidPlayer;
		++nAvoidPlayerCount;

		// Check to see if the avoid player is dormant.
		if ( pAvoidPlayer->IsDormant() )
			continue;

		// Is the avoid player solid?
		if ( pAvoidPlayer->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
			continue;

		Vector t1, t2;

		vecAvoidCenter = pAvoidPlayer->GetAbsOrigin();
		vecAvoidMin = pAvoidPlayer->GetPlayerMins();
		vecAvoidMax = pAvoidPlayer->GetPlayerMaxs();
		flZHeight = vecAvoidMax.z - vecAvoidMin.z;
		vecAvoidCenter.z += 0.5f * flZHeight;
		VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
		VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );

		if ( IsBoxIntersectingBox( vecHL2MPPlayerMin, vecHL2MPPlayerMax, vecAvoidMin, vecAvoidMax ) )
		{
			// Need to avoid this player.
			if ( !pIntersectPlayer )
			{
				pIntersectPlayer = pAvoidPlayer;
				break;
			}
		}
	}

	// Anything to avoid?
	if ( !pIntersectPlayer )
		return;

	// Calculate the push strength and direction.
	Vector vecDelta;

	// Avoid a player - they have precedence.
	if ( pIntersectPlayer )
	{
		VectorSubtract( pIntersectPlayer->WorldSpaceCenter(), vecHL2MPPlayerCenter, vecDelta );

		Vector vRad = pIntersectPlayer->WorldAlignMaxs() - pIntersectPlayer->WorldAlignMins();
		vRad.z = 0;

		flAvoidRadius = vRad.Length();
	}

	float flPushStrength = RemapValClamped( vecDelta.Length(), flAvoidRadius, 0, 0, hl2mp_max_separation_force.GetInt() ); //flPushScale;

	//Msg( "PushScale = %f\n", flPushStrength );

	// Check to see if we have enough push strength to make a difference.
	if ( flPushStrength < 0.01f )
		return;

	Vector vecPush;
	if ( GetAbsVelocity().Length2DSqr() > 0.1f )
	{
		Vector vecVelocity = GetAbsVelocity();
		vecVelocity.z = 0.0f;
		CrossProduct( vecUp, vecVelocity, vecPush );
		VectorNormalize( vecPush );
	}
	else
	{
		// We are not moving, but we're still intersecting.
		QAngle angView = pCmd->viewangles;
		angView.x = 0.0f;
		AngleVectors( angView, NULL, &vecPush, NULL );
	}

	// Move away from the other player/object.
	Vector vecSeparationVelocity;
	if ( vecDelta.Dot( vecPush ) < 0 )
	{
		vecSeparationVelocity = vecPush * flPushStrength;
	}
	else
	{
		vecSeparationVelocity = vecPush * -flPushStrength;
	}

	// Don't allow the max push speed to be greater than the max player speed.
	float flMaxPlayerSpeed = MaxSpeed();
	float flCropFraction = 1.33333333f;

	if ( ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
	{	
		flMaxPlayerSpeed *= flCropFraction;
	}	

	float flMaxPlayerSpeedSqr = flMaxPlayerSpeed * flMaxPlayerSpeed;

	if ( vecSeparationVelocity.LengthSqr() > flMaxPlayerSpeedSqr )
	{
		vecSeparationVelocity.NormalizeInPlace();
		VectorScale( vecSeparationVelocity, flMaxPlayerSpeed, vecSeparationVelocity );
	}

	QAngle vAngles = pCmd->viewangles;
	vAngles.x = 0;
	Vector currentdir;
	Vector rightdir;

	AngleVectors( vAngles, &currentdir, &rightdir, NULL );

	Vector vDirection = vecSeparationVelocity;

	VectorNormalize( vDirection );

	float fwd = currentdir.Dot( vDirection );
	float rt = rightdir.Dot( vDirection );

	float forward = fwd * flPushStrength;
	float side = rt * flPushStrength;

	//Msg( "fwd: %f - rt: %f - forward: %f - side: %f\n", fwd, rt, forward, side );

	pCmd->forwardmove	+= forward;
	pCmd->sidemove		+= side;

	// Clamp the move to within legal limits, preserving direction. This is a little
	// complicated because we have different limits for forward, back, and side

	//Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );

	float flForwardScale = 1.0f;
	if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
	}
	else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
	}

	float flSideScale = 1.0f;
	if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
	{
		flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
	}

	float flScale = min( flForwardScale, flSideScale );
	pCmd->forwardmove *= flScale;
	pCmd->sidemove *= flScale;

	//Msg( "Pforwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
}
bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) {
    Vector d = eb.Minus(ea);
    double t_eps = LENGTH_EPS/d.Magnitude();

    double dist_a, dist_b;
    double t, tthis; 
    bool skew;
    Vector pi;
    bool inOrEdge0, inOrEdge1;

    Vector dthis = b.Minus(a);
    double tthis_eps = LENGTH_EPS/dthis.Magnitude();

    if((ea.Equals(a) && eb.Equals(b)) ||
       (eb.Equals(a) && ea.Equals(b)))
    {
        if(ppi) *ppi = a;
        if(spl) spl->Add(a);
        return true;
    }

    dist_a = a.DistanceToLine(ea, d),
    dist_b = b.DistanceToLine(ea, d);

    // Can't just test if dist_a equals dist_b; they could be on opposite
    // sides, since it's unsigned.
    double m = sqrt(d.Magnitude()*dthis.Magnitude());
    if(sqrt(fabs(d.Dot(dthis))) > (m - LENGTH_EPS)) {
        // The edges are parallel.
        if(fabs(dist_a) > LENGTH_EPS) {
            // and not coincident, so can't be interesecting
            return false;
        }
        // The edges are coincident. Make sure that neither endpoint lies
        // on the other
        bool inters = false;
        double t;
        t = a.Minus(ea).DivPivoting(d);
        if(t > t_eps && t < (1 - t_eps)) inters = true;
        t = b.Minus(ea).DivPivoting(d);
        if(t > t_eps && t < (1 - t_eps)) inters = true;
        t = ea.Minus(a).DivPivoting(dthis);
        if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
        t = eb.Minus(a).DivPivoting(dthis);
        if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;

        if(inters) {
            if(ppi) *ppi = a;
            if(spl) spl->Add(a);
            return true;
        } else {
            // So coincident but disjoint, okay.
            return false;
        }
    }

    // Lines are not parallel, so look for an intersection.
    pi = Vector::AtIntersectionOfLines(ea, eb, a, b,
                                       &skew,
                                       &t, &tthis);
    if(skew) return false;

    inOrEdge0 = (t     > -t_eps)     && (t     < (1 + t_eps));
    inOrEdge1 = (tthis > -tthis_eps) && (tthis < (1 + tthis_eps));

    if(inOrEdge0 && inOrEdge1) {
        if(a.Equals(ea) || b.Equals(ea) ||
           a.Equals(eb) || b.Equals(eb))
        {
            // Not an intersection if we share an endpoint with an edge
            return false;
        }
        // But it's an intersection if a vertex of one edge lies on the
        // inside of the other (or if they cross away from either's
        // vertex).
        if(ppi) *ppi = pi;
        if(spl) spl->Add(pi);
        return true;
    }
    return false;
}
Example #4
0
Plane::Plane(const Vector& Normal, const Vector& Point)
    : m_Normal(Normal), m_Distance(-Normal.Dot(Point)) {}
//-----------------------------------------------------------------------------
// Client-side obstacle avoidance
//-----------------------------------------------------------------------------
void C_BaseHLPlayer::PerformClientSideObstacleAvoidance( float flFrameTime, CUserCmd *pCmd )
{
	// Don't avoid if noclipping or in movetype none
	switch ( GetMoveType() )
	{
	case MOVETYPE_NOCLIP:
	case MOVETYPE_NONE:
	case MOVETYPE_OBSERVER:
		return;
	default:
		break;
	}

	// Try to steer away from any objects/players we might interpenetrate
	Vector size = WorldAlignSize();

	float radius = 0.7f * sqrt( size.x * size.x + size.y * size.y );
	float curspeed = GetLocalVelocity().Length2D();

	//int slot = 1;
	//engine->Con_NPrintf( slot++, "speed %f\n", curspeed );
	//engine->Con_NPrintf( slot++, "radius %f\n", radius );

	// If running, use a larger radius
	float factor = 1.0f;

	if ( curspeed > 150.0f )
	{
		curspeed = MIN( 2048.0f, curspeed );
		factor = ( 1.0f + ( curspeed - 150.0f ) / 150.0f );

		//engine->Con_NPrintf( slot++, "scaleup (%f) to radius %f\n", factor, radius * factor );

		radius = radius * factor;
	}

	Vector currentdir;
	Vector rightdir;

	QAngle vAngles = pCmd->viewangles;
	vAngles.x = 0;

	AngleVectors( vAngles, &currentdir, &rightdir, NULL );
		
	bool istryingtomove = false;
	bool ismovingforward = false;
	if ( fabs( pCmd->forwardmove ) > 0.0f || 
		fabs( pCmd->sidemove ) > 0.0f )
	{
		istryingtomove = true;
		if ( pCmd->forwardmove > 1.0f )
		{
			ismovingforward = true;
		}
	}

	if ( istryingtomove == true )
		 radius *= 1.3f;

	CPlayerAndObjectEnumerator avoid( radius );
	partition->EnumerateElementsInSphere( PARTITION_CLIENT_SOLID_EDICTS, GetAbsOrigin(), radius, false, &avoid );

	// Okay, decide how to avoid if there's anything close by
	int c = avoid.GetObjectCount();
	if ( c <= 0 )
		return;

	//engine->Con_NPrintf( slot++, "moving %s forward %s\n", istryingtomove ? "true" : "false", ismovingforward ? "true" : "false"  );

	float adjustforwardmove = 0.0f;
	float adjustsidemove	= 0.0f;

	for ( int i = 0; i < c; i++ )
	{
		C_AI_BaseNPC *obj = dynamic_cast< C_AI_BaseNPC *>(avoid.GetObject( i ));

		if( !obj )
			continue;

		Vector vecToObject = obj->GetAbsOrigin() - GetAbsOrigin();

		float flDist = vecToObject.Length2D();
		
		// Figure out a 2D radius for the object
		Vector vecWorldMins, vecWorldMaxs;
		obj->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs );
		Vector objSize = vecWorldMaxs - vecWorldMins;

		float objectradius = 0.5f * sqrt( objSize.x * objSize.x + objSize.y * objSize.y );

		//Don't run this code if the NPC is not moving UNLESS we are in stuck inside of them.
		if ( !obj->IsMoving() && flDist > objectradius )
			  continue;

		if ( flDist > objectradius && obj->IsEffectActive( EF_NODRAW ) )
		{
			obj->RemoveEffects( EF_NODRAW );
		}

		Vector vecNPCVelocity;
		obj->EstimateAbsVelocity( vecNPCVelocity );
		float flNPCSpeed = VectorNormalize( vecNPCVelocity );

		Vector vPlayerVel = GetAbsVelocity();
		VectorNormalize( vPlayerVel );

		float flHit1, flHit2;
		Vector vRayDir = vecToObject;
		VectorNormalize( vRayDir );

		float flVelProduct = DotProduct( vecNPCVelocity, vPlayerVel );
		float flDirProduct = DotProduct( vRayDir, vPlayerVel );

		if ( !IntersectInfiniteRayWithSphere(
				GetAbsOrigin(),
				vRayDir,
				obj->GetAbsOrigin(),
				radius,
				&flHit1,
				&flHit2 ) )
			continue;

        Vector dirToObject = -vecToObject;
		VectorNormalize( dirToObject );

		float fwd = 0;
		float rt = 0;

		float sidescale = 2.0f;
		float forwardscale = 1.0f;
		bool foundResult = false;

		Vector vMoveDir = vecNPCVelocity;
		if ( flNPCSpeed > 0.001f )
		{
			// This NPC is moving. First try deflecting the player left or right relative to the NPC's velocity.
			// Start with whatever side they're on relative to the NPC's velocity.
			Vector vecNPCTrajectoryRight = CrossProduct( vecNPCVelocity, Vector( 0, 0, 1) );
			int iDirection = ( vecNPCTrajectoryRight.Dot( dirToObject ) > 0 ) ? 1 : -1;
			for ( int nTries = 0; nTries < 2; nTries++ )
			{
				Vector vecTryMove = vecNPCTrajectoryRight * iDirection;
				VectorNormalize( vecTryMove );
				
				Vector vTestPosition = GetAbsOrigin() + vecTryMove * radius * 2;

				if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
				{
					fwd = currentdir.Dot( vecTryMove );
					rt = rightdir.Dot( vecTryMove );
					
					//Msg( "PUSH DEFLECT fwd=%f, rt=%f\n", fwd, rt );
					
					foundResult = true;
					break;
				}
				else
				{
					// Try the other direction.
					iDirection *= -1;
				}
			}
		}
		else
		{
			// the object isn't moving, so try moving opposite the way it's facing
			Vector vecNPCForward;
			obj->GetVectors( &vecNPCForward, NULL, NULL );
			
			Vector vTestPosition = GetAbsOrigin() - vecNPCForward * radius * 2;
			if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
			{
				fwd = currentdir.Dot( -vecNPCForward );
				rt = rightdir.Dot( -vecNPCForward );

				if ( flDist < objectradius )
				{
					obj->AddEffects( EF_NODRAW );
				}

				//Msg( "PUSH AWAY FACE fwd=%f, rt=%f\n", fwd, rt );

				foundResult = true;
			}
		}

		if ( !foundResult )
		{
			// test if we can move in the direction the object is moving
			Vector vTestPosition = GetAbsOrigin() + vMoveDir * radius * 2;
			if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
			{
				fwd = currentdir.Dot( vMoveDir );
				rt = rightdir.Dot( vMoveDir );

				if ( flDist < objectradius )
				{
					obj->AddEffects( EF_NODRAW );
				}

				//Msg( "PUSH ALONG fwd=%f, rt=%f\n", fwd, rt );

				foundResult = true;
			}
			else
			{
				// try moving directly away from the object
				Vector vTestPosition = GetAbsOrigin() - dirToObject * radius * 2;
				if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
				{
					fwd = currentdir.Dot( -dirToObject );
					rt = rightdir.Dot( -dirToObject );
					foundResult = true;

					//Msg( "PUSH AWAY fwd=%f, rt=%f\n", fwd, rt );
				}
			}
		}

		if ( !foundResult )
		{
			// test if we can move through the object
			Vector vTestPosition = GetAbsOrigin() - vMoveDir * radius * 2;
			fwd = currentdir.Dot( -vMoveDir );
			rt = rightdir.Dot( -vMoveDir );

			if ( flDist < objectradius )
			{
				obj->AddEffects( EF_NODRAW );
			}

			//Msg( "PUSH THROUGH fwd=%f, rt=%f\n", fwd, rt );

			foundResult = true;
		}

		// If running, then do a lot more sideways veer since we're not going to do anything to
		//  forward velocity
		if ( istryingtomove )
		{
			sidescale = 6.0f;
		}

		if ( flVelProduct > 0.0f && flDirProduct > 0.0f )
		{
			sidescale = 0.1f;
		}

		float force = 1.0f;
		float forward = forwardscale * fwd * force * AVOID_SPEED;
		float side = sidescale * rt * force * AVOID_SPEED;

		adjustforwardmove	+= forward;
		adjustsidemove		+= side;
	}

	pCmd->forwardmove	+= adjustforwardmove;
	pCmd->sidemove		+= adjustsidemove;
	
	// Clamp the move to within legal limits, preserving direction. This is a little
	// complicated because we have different limits for forward, back, and side

	//Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );

	float flForwardScale = 1.0f;
	if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
	}
	else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
	}
	
	float flSideScale = 1.0f;
	if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
	{
		flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
	}
	
	float flScale = MIN( flForwardScale, flSideScale );
	pCmd->forwardmove *= flScale;
	pCmd->sidemove *= flScale;

	//Msg( "POSTCLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
}
void Constraint::MenuConstrain(int id) {
    Constraint c;
    ZERO(&c);
    c.group = SS.GW.activeGroup;
    c.workplane = SS.GW.ActiveWorkplane();

    SS.GW.GroupSelection();
#define gs (SS.GW.gs)

    switch(id) {
        case GraphicsWindow::MNU_DISTANCE_DIA: {
            if(gs.points == 2 && gs.n == 2) {
                c.type = PT_PT_DISTANCE;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 && gs.n == 1) {
                c.type = PT_PT_DISTANCE;
                Entity *e = SK.GetEntity(gs.entity[0]);
                c.ptA = e->point[0];
                c.ptB = e->point[1];
            } else if(gs.vectors == 1 && gs.points == 2 && gs.n == 3) {
                c.type = PROJ_PT_DISTANCE;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
                c.entityA = gs.vector[0];
            } else if(gs.workplanes == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_PLANE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_LINE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_FACE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.face[0];
            } else if(gs.circlesOrArcs == 1 && gs.n == 1) {
                c.type = DIAMETER;
                c.entityA = gs.entity[0];
            } else {
                Error(
"Bad selection for distance / diameter constraint. This "
"constraint can apply to:\n\n"
"    * two points (distance between points)\n"
"    * a line segment (length)\n"
"    * two points and a line segment or normal (projected distance)\n"
"    * a workplane and a point (minimum distance)\n"
"    * a line segment and a point (minimum distance)\n"
"    * a plane face and a point (minimum distance)\n"
"    * a circle or an arc (diameter)\n");
                return;
            }
            if(c.type == PT_PT_DISTANCE || c.type == PROJ_PT_DISTANCE) {
                Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
                Vector a = SK.GetEntity(c.ptA)->PointGetNum();
                Vector b = SK.GetEntity(c.ptB)->PointGetNum();
                c.disp.offset = n.Cross(a.Minus(b));
                c.disp.offset = (c.disp.offset).WithMagnitude(50/SS.GW.scale);
            } else {
                c.disp.offset = Vector::From(0, 0, 0);
            }

            c.valA = 0;
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_ON_ENTITY:
            if(gs.points == 2 && gs.n == 2) {
                c.type = POINTS_COINCIDENT;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.points == 1 && gs.workplanes == 1 && gs.n == 2) {
                c.type = PT_IN_PLANE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
                c.type = PT_ON_LINE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) {
                c.type = PT_ON_CIRCLE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.faces == 1 && gs.n == 2) {
                c.type = PT_ON_FACE;
                c.ptA = gs.point[0];
                c.entityA = gs.face[0];
            } else {
                Error("Bad selection for on point / curve / plane constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two points (points coincident)\n"
                      "    * a point and a workplane (point in plane)\n"
                      "    * a point and a line segment (point on line)\n"
                      "    * a point and a circle or arc (point on curve)\n"
                      "    * a point and a plane face (point on face)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_EQUAL:
            if(gs.lineSegments == 2 && gs.n == 2) {
                c.type = EQUAL_LENGTH_LINES;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
                c.type = EQ_PT_LN_DISTANCES;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.entityB = gs.entity[1];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
                // The same line segment for the distances, but different
                // points.
                c.type = EQ_PT_LN_DISTANCES;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.entityB = gs.entity[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
                c.type = EQ_LEN_PT_LINE_D;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
                c.ptA = gs.point[0];
            } else if(gs.vectors == 4 && gs.n == 4) {
                c.type = EQUAL_ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.entityC = gs.vector[2];
                c.entityD = gs.vector[3];
            } else if(gs.vectors == 3 && gs.n == 3) {
                c.type = EQUAL_ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.entityC = gs.vector[1];
                c.entityD = gs.vector[2];
            } else if(gs.circlesOrArcs == 2 && gs.n == 2) {
                c.type = EQUAL_RADIUS;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else if(gs.arcs == 1 && gs.lineSegments == 1 && gs.n == 2) {
                c.type = EQUAL_LINE_ARC_LEN;
                if(SK.GetEntity(gs.entity[0])->type == Entity::ARC_OF_CIRCLE) {
                    c.entityA = gs.entity[1];
                    c.entityB = gs.entity[0];
                } else {
                    c.entityA = gs.entity[0];
                    c.entityB = gs.entity[1];
                }
            } else {
                Error("Bad selection for equal length / radius constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two line segments (equal length)\n"
                      "    * two line segments and two points "
                              "(equal point-line distances)\n"
                      "    * a line segment and two points "
                              "(equal point-line distances)\n"
                      "    * a line segment, and a point and line segment "
                              "(point-line distance equals length)\n"
                      "    * four line segments or normals "
                              "(equal angle between A,B and C,D)\n"
                      "    * three line segments or normals "
                              "(equal angle between A,B and B,C)\n"
                      "    * two circles or arcs (equal radius)\n"
                      "    * a line segment and an arc "
                              "(line segment length equals arc length)\n");
                return;
            }
            if(c.type == EQUAL_ANGLE) {
                // Infer the nearest supplementary angle from the sketch.
                Vector a1 = SK.GetEntity(c.entityA)->VectorGetNum(),
                       b1 = SK.GetEntity(c.entityB)->VectorGetNum(),
                       a2 = SK.GetEntity(c.entityC)->VectorGetNum(),
                       b2 = SK.GetEntity(c.entityD)->VectorGetNum();
                double d1 = a1.Dot(b1), d2 = a2.Dot(b2);

                if(d1*d2 < 0) {
                    c.other = true;
                }
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_RATIO:
            if(gs.lineSegments == 2 && gs.n == 2) {
                c.type = LENGTH_RATIO;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else {
                Error("Bad selection for length ratio constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments\n");
                return;
            }

            c.valA = 0;
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_AT_MIDPOINT:
            if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
                c.type = AT_MIDPOINT;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];

                // If a point is at-midpoint, then no reason to also constrain
                // it on-line; so auto-remove that.
                DeleteAllConstraintsFor(PT_ON_LINE, c.entityA, c.ptA);
            } else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
                c.type = AT_MIDPOINT;
                int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
                c.entityA = gs.entity[i];
                c.entityB = gs.entity[1-i];
            } else {
                Error("Bad selection for at midpoint constraint. This "
                      "constraint can apply to:\n\n"
                      "    * a line segment and a point "
                            "(point at midpoint)\n"
                      "    * a line segment and a workplane "
                            "(line's midpoint on plane)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_SYMMETRIC:
            if(gs.points == 2 &&
                                ((gs.workplanes == 1 && gs.n == 3) ||
                                 (gs.n == 2)))
            {
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 && 
                                ((gs.workplanes == 1 && gs.n == 2) ||
                                 (gs.n == 1)))
            {
                int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
                Entity *line = SK.GetEntity(gs.entity[i]);
                c.entityA = gs.entity[1-i];
                c.ptA = line->point[0];
                c.ptB = line->point[1];
            } else if(SS.GW.LockedInWorkplane()
                        && gs.lineSegments == 2 && gs.n == 2)
            {
                Entity *l0 = SK.GetEntity(gs.entity[0]),
                       *l1 = SK.GetEntity(gs.entity[1]);

                if((l1->group.v != SS.GW.activeGroup.v) ||
                   (l1->construction && !(l0->construction)))
                {
                    SWAP(Entity *, l0, l1);
                }
                c.ptA = l1->point[0];
                c.ptB = l1->point[1];
                c.entityA = l0->h;
                c.type = SYMMETRIC_LINE;
            } else if(SS.GW.LockedInWorkplane()
                        && gs.lineSegments == 1 && gs.points == 2 && gs.n == 3)
            {
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
                c.entityA = gs.entity[0];
                c.type = SYMMETRIC_LINE;
            } else {
                Error("Bad selection for symmetric constraint. This constraint "
                      "can apply to:\n\n"
                      "    * two points or a line segment "
                          "(symmetric about workplane's coordinate axis)\n"
                      "    * line segment, and two points or a line segment "
                          "(symmetric about line segment)\n"
                      "    * workplane, and two points or a line segment "
                          "(symmetric about workplane)\n");
                return;
            }
            if(c.type != 0) {
                // Already done, symmetry about a line segment in a workplane
            } else if(c.entityA.v == Entity::NO_ENTITY.v) {
                // Horizontal / vertical symmetry, implicit symmetry plane
                // normal to the workplane
                if(c.workplane.v == Entity::FREE_IN_3D.v) {
                    Error("Must be locked in to workplane when constraining "
                          "symmetric without an explicit symmetry plane.");
                    return;
                }
                Vector pa = SK.GetEntity(c.ptA)->PointGetNum();
                Vector pb = SK.GetEntity(c.ptB)->PointGetNum();
                Vector dp = pa.Minus(pb);
                EntityBase *norm = SK.GetEntity(c.workplane)->Normal();;
                Vector u = norm->NormalU(), v = norm->NormalV();
                if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
                    c.type = SYMMETRIC_HORIZ;
                } else {
                    c.type = SYMMETRIC_VERT;
                }
                if(gs.lineSegments == 1) {
                    // If this line segment is already constrained horiz or
                    // vert, then auto-remove that redundant constraint.
                    DeleteAllConstraintsFor(HORIZONTAL, (gs.entity[0]),
                        Entity::NO_ENTITY);
                    DeleteAllConstraintsFor(VERTICAL, (gs.entity[0]),
                        Entity::NO_ENTITY);

                }
            } else {
                // Symmetry with a symmetry plane specified explicitly.
                c.type = SYMMETRIC;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_VERTICAL:
        case GraphicsWindow::MNU_HORIZONTAL: {
            hEntity ha, hb;
            if(c.workplane.v == Entity::FREE_IN_3D.v) {
                Error("Select workplane before constraining horiz/vert.");
                return;
            }
            if(gs.lineSegments == 1 && gs.n == 1) {
                c.entityA = gs.entity[0];
                Entity *e = SK.GetEntity(c.entityA);
                ha = e->point[0];
                hb = e->point[1];
            } else if(gs.points == 2 && gs.n == 2) {
                ha = c.ptA = gs.point[0];
                hb = c.ptB = gs.point[1];
            } else {
                Error("Bad selection for horizontal / vertical constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two points\n"
                      "    * a line segment\n");
                return;
            }
            if(id == GraphicsWindow::MNU_HORIZONTAL) {
                c.type = HORIZONTAL;
            } else {
                c.type = VERTICAL;
            }
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_ORIENTED_SAME: {
            if(gs.anyNormals == 2 && gs.n == 2) {
                c.type = SAME_ORIENTATION;
                c.entityA = gs.anyNormal[0];
                c.entityB = gs.anyNormal[1];
            } else {
                Error("Bad selection for same orientation constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two normals\n");
                return;
            }
            SS.UndoRemember();

            Entity *nfree = SK.GetEntity(c.entityA);
            Entity *nref  = SK.GetEntity(c.entityB);
            if(nref->group.v == SS.GW.activeGroup.v) {
                SWAP(Entity *, nref, nfree);
            }
            if(nfree->group.v == SS.GW.activeGroup.v &&
               nref ->group.v != SS.GW.activeGroup.v)
            {
                // nfree is free, and nref is locked (since it came from a
                // previous group); so let's force nfree aligned to nref,
                // and make convergence easy
                Vector ru = nref ->NormalU(), rv = nref ->NormalV();
                Vector fu = nfree->NormalU(), fv = nfree->NormalV();

                if(fabs(fu.Dot(ru)) < fabs(fu.Dot(rv))) {
                    // There might be an odd*90 degree rotation about the
                    // normal vector; allow that, since the numerical
                    // constraint does
                    SWAP(Vector, ru, rv);
                } 
                fu = fu.Dot(ru) > 0 ? ru : ru.ScaledBy(-1);
                fv = fv.Dot(rv) > 0 ? rv : rv.ScaledBy(-1);

                nfree->NormalForceTo(Quaternion::From(fu, fv));
            }
            AddConstraint(&c, false);
            break;
        }

        case GraphicsWindow::MNU_OTHER_ANGLE:
            if(gs.constraints == 1 && gs.n == 0) {
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
                if(c->type == ANGLE) {
                    SS.UndoRemember();
                    c->other = !(c->other);
                    c->ModifyToSatisfy();
                    break;
                }
                if(c->type == EQUAL_ANGLE) {
                    SS.UndoRemember();
                    c->other = !(c->other);
                    SS.MarkGroupDirty(c->group);
                    SS.later.generateAll = true;
                    break;
                }
            }
            Error("Must select an angle constraint.");
            return;

        case GraphicsWindow::MNU_REFERENCE:
            if(gs.constraints == 1 && gs.n == 0) {
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
                if(c->HasLabel() && c->type != COMMENT) {
                    (c->reference) = !(c->reference);
                    SK.GetGroup(c->group)->clean = false;
                    SS.GenerateAll();
                    break;
                }
            }
            Error("Must select a constraint with associated label.");
            return;

        case GraphicsWindow::MNU_ANGLE: {
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.valA = 0;
            } else {
                Error("Bad selection for angle constraint. This constraint "
                      "can apply to:\n\n"
                      "    * two line segments\n"
                      "    * a line segment and a normal\n"
                      "    * two normals\n");
                return;
            }

            Entity *ea = SK.GetEntity(c.entityA), 
                   *eb = SK.GetEntity(c.entityB);
            if(ea->type == Entity::LINE_SEGMENT &&
               eb->type == Entity::LINE_SEGMENT)
            {
                Vector a0 = SK.GetEntity(ea->point[0])->PointGetNum(),
                       a1 = SK.GetEntity(ea->point[1])->PointGetNum(),
                       b0 = SK.GetEntity(eb->point[0])->PointGetNum(),
                       b1 = SK.GetEntity(eb->point[1])->PointGetNum();
                if(a0.Equals(b0) || a1.Equals(b1)) {
                    // okay, vectors should be drawn in same sense
                } else if(a0.Equals(b1) || a1.Equals(b0)) {
                    // vectors are in opposite sense
                    c.other = true;
                } else {
                    // no shared point; not clear which intersection to draw
                }
            }
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_PARALLEL:
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = PARALLEL;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
            } else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
                Entity *line = SK.GetEntity(gs.entity[0]);
                Entity *arc  = SK.GetEntity(gs.entity[1]);
                if(line->type == Entity::ARC_OF_CIRCLE) {
                    SWAP(Entity *, line, arc);
                }
                Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
                       l1 = SK.GetEntity(line->point[1])->PointGetNum();
                Vector a1 = SK.GetEntity(arc->point[1])->PointGetNum(),
                       a2 = SK.GetEntity(arc->point[2])->PointGetNum();

                if(l0.Equals(a1) || l1.Equals(a1)) {
                    c.other = false;
                } else if(l0.Equals(a2) || l1.Equals(a2)) {
                    c.other = true;
                } else {
                    Error("The tangent arc and line segment must share an "
                          "endpoint. Constrain them with Constrain -> "
                          "On Point before constraining tangent.");
                    return;
                }
                c.type = ARC_LINE_TANGENT;
                c.entityA = arc->h;
                c.entityB = line->h;
            } else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
                Entity *line  = SK.GetEntity(gs.entity[0]);
                Entity *cubic = SK.GetEntity(gs.entity[1]);
                if(line->type == Entity::CUBIC) {
                    SWAP(Entity *, line, cubic);
                }
                Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
                       l1 = SK.GetEntity(line->point[1])->PointGetNum();
                Vector as = cubic->CubicGetStartNum(),
                       af = cubic->CubicGetFinishNum();

                if(l0.Equals(as) || l1.Equals(as)) {
                    c.other = false;
                } else if(l0.Equals(af) || l1.Equals(af)) {
                    c.other = true;
                } else {
                    Error("The tangent cubic and line segment must share an "
                          "endpoint. Constrain them with Constrain -> "
                          "On Point before constraining tangent.");
                    return;
                }
                c.type = CUBIC_LINE_TANGENT;
                c.entityA = cubic->h;
                c.entityB = line->h;
            } else if(gs.cubics + gs.arcs == 2 && gs.n == 2) {
                if(!SS.GW.LockedInWorkplane()) {
                    Error("Curve-curve tangency must apply in workplane.");
                    return;
                }
                Entity *eA = SK.GetEntity(gs.entity[0]),
                       *eB = SK.GetEntity(gs.entity[1]);
                Vector as = eA->EndpointStart(),
                       af = eA->EndpointFinish(),
                       bs = eB->EndpointStart(),
                       bf = eB->EndpointFinish();
                if(as.Equals(bs)) {
                    c.other = false; c.other2 = false;
                } else if(as.Equals(bf)) {
                    c.other = false; c.other2 = true;
                } else if(af.Equals(bs)) {
                    c.other = true; c.other2 = false;
                } else if(af.Equals(bf)) {
                    c.other = true; c.other2 = true;
                } else {
                    Error("The curves must share an endpoint. Constrain them "
                          "with Constrain -> On Point before constraining "
                          "tangent.");
                    return;
                }
                c.type = CURVE_CURVE_TANGENT;
                c.entityA = eA->h;
                c.entityB = eB->h;
            } else {
                Error("Bad selection for parallel / tangent constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments (parallel)\n"
                      "    * a line segment and a normal (parallel)\n"
                      "    * two normals (parallel)\n"
                      "    * two line segments, arcs, or beziers, that share "
                            "an endpoint (tangent)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_PERPENDICULAR:
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = PERPENDICULAR;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
            } else {
                Error("Bad selection for perpendicular constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments\n"
                      "    * a line segment and a normal\n"
                      "    * two normals\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_WHERE_DRAGGED:
            if(gs.points == 1 && gs.n == 1) {
                c.type = WHERE_DRAGGED;
                c.ptA = gs.point[0];
            } else {
                Error("Bad selection for lock point where dragged constraint. "
                      "This constraint can apply to:\n\n"
                      "    * a point\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_COMMENT:
            SS.GW.pending.operation = GraphicsWindow::MNU_COMMENT;
            SS.GW.pending.description = "click center of comment text";
            SS.later.showTW = true;
            break;

        default: oops();
    }

    SS.GW.ClearSelection();
    InvalidateGraphics();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_PropForklift::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
{
	// vecVehicleEyePos = real eye position this frame

	// m_vecLastEyePos = eye position last frame
	// m_vecEyeSpeed = eye speed last frame
	// vecPredEyePos = predicted eye position this frame (assuming no acceleration - it will get that from the pd controller).
	// vecPredEyeSpeed = predicted eye speed
	Vector vecPredEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
	Vector vecPredEyeSpeed = m_vecEyeSpeed;

	// m_vecLastEyeTarget = real eye position last frame (used for speed calculation).
	// Calculate the approximate speed based on the current vehicle eye position and the eye position last frame.
	Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
	m_vecLastEyeTarget = vecVehicleEyePos;
	if (vecVehicleEyeSpeed.Length() == 0.0)
		return;

	// Calculate the delta between the predicted eye position and speed and the current eye position and speed.
	Vector vecDeltaSpeed = vecVehicleEyeSpeed - vecPredEyeSpeed;
	Vector vecDeltaPos = vecVehicleEyePos - vecPredEyePos;

	// Forward vector.
	Vector vecForward;
	AngleVectors( vecVehicleEyeAngles, &vecForward );

	float flDeltaLength = vecDeltaPos.Length();
	if ( flDeltaLength > Forklift_DELTA_LENGTH_MAX )
	{
		// Clamp.
		float flDelta = flDeltaLength - Forklift_DELTA_LENGTH_MAX;
		if ( flDelta > 40.0f )
		{
			// This part is a bit of a hack to get rid of large deltas (at level load, etc.).
			m_vecLastEyePos = vecVehicleEyePos;
			m_vecEyeSpeed = vecVehicleEyeSpeed;
		}
		else
		{
			// Position clamp.
			float flRatio = Forklift_DELTA_LENGTH_MAX / flDeltaLength;
			vecDeltaPos *= flRatio;
			Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
			vecVehicleEyePos -= vecForwardOffset;
			m_vecLastEyePos = vecVehicleEyePos;

			// Speed clamp.
			vecDeltaSpeed *= flRatio;
			float flCoefficients[2];
			ComputePDControllerCoefficients( flCoefficients, r_JeepViewDampenFreq.GetFloat(), r_JeepViewDampenDamp.GetFloat(), flFrameTime );
			m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
		}
	}
	else
	{
		// Generate an updated (dampening) speed for use in next frames position prediction.
		float flCoefficients[2];
		ComputePDControllerCoefficients( flCoefficients, r_JeepViewDampenFreq.GetFloat(), r_JeepViewDampenDamp.GetFloat(), flFrameTime );
		m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
		
		// Save off data for next frame.
		m_vecLastEyePos = vecPredEyePos;
		
		// Move eye forward/backward.
		Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
		vecVehicleEyePos -= vecForwardOffset;
	}
}
Example #8
0
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group)
{
    SBezierLoop *sbl;

    int i0 = surface.n, i;

    // Normalize the axis direction so that the direction of revolution
    // ends up parallel to the normal of the sketch, on the side of the
    // axis where the sketch is.
    Vector pto;
    double md = VERY_NEGATIVE;
    for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
        SBezier *sb;
        for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
            // Choose the point farthest from the axis; we'll get garbage
            // if we choose a point that lies on the axis, for example.
            // (And our surface will be self-intersecting if the sketch
            // spans the axis, so don't worry about that.)
            Vector p = sb->Start();
            double d = p.DistanceToLine(pt, axis);
            if(d > md) {
                md = d;
                pto = p;
            }
        }
    }
    Vector ptc = pto.ClosestPointOnLine(pt, axis),
           up  = (pto.Minus(ptc)).WithMagnitude(1),
           vp  = (sbls->normal).Cross(up);
    if(vp.Dot(axis) < 0) {
        axis = axis.ScaledBy(-1);
    }

    // Now we actually build and trim the surfaces.
    for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
        int i, j;
        SBezier *sb;
        List<Revolved> hsl = {};

        for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
            Revolved revs;
            for(j = 0; j < 4; j++) {
                if(sb->deg == 1 &&
                    (sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
                    (sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS)
                {
                    // This is a line on the axis of revolution; it does
                    // not contribute a surface.
                    revs.d[j].v = 0;
                } else {
                    SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis,
                                                             (PI/2)*j,
                                                             (PI/2)*(j+1));
                    ss.color = color;
                    if(sb->entity != 0) {
                        hEntity he;
                        he.v = sb->entity;
                        hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
                        if(SK.entity.FindByIdNoOops(hface) != NULL) {
                            ss.face = hface.v;
                        }
                    }
                    revs.d[j] = surface.AddAndAssignId(&ss);
                }
            }
            hsl.Add(&revs);
        }

        for(i = 0; i < sbl->l.n; i++) {
            Revolved revs  = hsl.elem[i],
                     revsp = hsl.elem[WRAP(i-1, sbl->l.n)];

            sb   = &(sbl->l.elem[i]);

            for(j = 0; j < 4; j++) {
                SCurve sc;
                Quaternion qs = Quaternion::From(axis, (PI/2)*j);
                // we want Q*(x - p) + p = Q*x + (p - Q*p)
                Vector ts = pt.Minus(qs.Rotate(pt));

                // If this input curve generate a surface, then trim that
                // surface with the rotated version of the input curve.
                if(revs.d[j].v) {
                    sc = {};
                    sc.isExact = true;
                    sc.exact = sb->TransformedBy(ts, qs, 1.0);
                    (sc.exact).MakePwlInto(&(sc.pts));
                    sc.surfA = revs.d[j];
                    sc.surfB = revs.d[WRAP(j-1, 4)];

                    hSCurve hcb = curve.AddAndAssignId(&sc);

                    STrimBy stb;
                    stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
                    (surface.FindById(sc.surfA))->trim.Add(&stb);
                    stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
                    (surface.FindById(sc.surfB))->trim.Add(&stb);
                }

                // And if this input curve and the one after it both generated
                // surfaces, then trim both of those by the appropriate
                // circle.
                if(revs.d[j].v && revsp.d[j].v) {
                    SSurface *ss = surface.FindById(revs.d[j]);

                    sc = {};
                    sc.isExact = true;
                    sc.exact = SBezier::From(ss->ctrl[0][0],
                                             ss->ctrl[0][1],
                                             ss->ctrl[0][2]);
                    sc.exact.weight[1] = ss->weight[0][1];
                    (sc.exact).MakePwlInto(&(sc.pts));
                    sc.surfA = revs.d[j];
                    sc.surfB = revsp.d[j];

                    hSCurve hcc = curve.AddAndAssignId(&sc);

                    STrimBy stb;
                    stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/false);
                    (surface.FindById(sc.surfA))->trim.Add(&stb);
                    stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/true);
                    (surface.FindById(sc.surfB))->trim.Add(&stb);
                }
            }
        }

        hsl.Clear();
    }

    for(i = i0; i < surface.n; i++) {
        SSurface *srf = &(surface.elem[i]);

        // Revolution of a line; this is potentially a plane, which we can
        // rewrite to have degree (1, 1).
        if(srf->degm == 1 && srf->degn == 2) {
            // close start, far start, far finish
            Vector cs, fs, ff;
            double d0, d1;
            d0 = (srf->ctrl[0][0]).DistanceToLine(pt, axis);
            d1 = (srf->ctrl[1][0]).DistanceToLine(pt, axis);

            if(d0 > d1) {
                cs = srf->ctrl[1][0];
                fs = srf->ctrl[0][0];
                ff = srf->ctrl[0][2];
            } else {
                cs = srf->ctrl[0][0];
                fs = srf->ctrl[1][0];
                ff = srf->ctrl[1][2];
            }

            // origin close, origin far
            Vector oc = cs.ClosestPointOnLine(pt, axis),
                   of = fs.ClosestPointOnLine(pt, axis);

            if(oc.Equals(of)) {
                // This is a plane, not a (non-degenerate) cone.
                Vector oldn = srf->NormalAt(0.5, 0.5);

                Vector u = fs.Minus(of), v;

                v = (axis.Cross(u)).WithMagnitude(1);

                double vm = (ff.Minus(of)).Dot(v);
                v = v.ScaledBy(vm);

                srf->degm = 1;
                srf->degn = 1;
                srf->ctrl[0][0] = of;
                srf->ctrl[0][1] = of.Plus(u);
                srf->ctrl[1][0] = of.Plus(v);
                srf->ctrl[1][1] = of.Plus(u).Plus(v);
                srf->weight[0][0] = 1;
                srf->weight[0][1] = 1;
                srf->weight[1][0] = 1;
                srf->weight[1][1] = 1;

                if(oldn.Dot(srf->NormalAt(0.5, 0.5)) < 0) {
                    swap(srf->ctrl[0][0], srf->ctrl[1][0]);
                    swap(srf->ctrl[0][1], srf->ctrl[1][1]);
                }
                continue;
            }

            if(fabs(d0 - d1) < LENGTH_EPS) {
                // This is a cylinder; so transpose it so that we'll recognize
                // it as a surface of extrusion.
                SSurface sn = *srf;

                // Transposing u and v flips the normal, so reverse u to
                // flip it again and put it back where we started.
                sn.degm = 2;
                sn.degn = 1;
                int dm, dn;
                for(dm = 0; dm <= 1; dm++) {
                    for(dn = 0; dn <= 2; dn++) {
                        sn.ctrl  [dn][dm] = srf->ctrl  [1-dm][dn];
                        sn.weight[dn][dm] = srf->weight[1-dm][dn];
                    }
                }

                *srf = sn;
                continue;
            }
        }

    }

}
Example #9
0
void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
                                SShell *into)
{
    Vector amax, amin, bmax, bmin;
    GetAxisAlignedBounding(&amax, &amin);
    b->GetAxisAlignedBounding(&bmax, &bmin);

    if(Vector::BoundingBoxesDisjoint(amax, amin, bmax, bmin)) {
        // They cannot possibly intersect, no curves to generate
        return;
    }

    Vector alongt, alongb;
    SBezier oft, ofb;
    bool isExtdt = this->IsExtrusion(&oft, &alongt),
         isExtdb =    b->IsExtrusion(&ofb, &alongb);

    if(degm == 1 && degn == 1 && b->degm == 1 && b->degn == 1) {
        // Line-line intersection; it's a plane or nothing.
        Vector na = NormalAt(0, 0).WithMagnitude(1),
               nb = b->NormalAt(0, 0).WithMagnitude(1);
        double da = na.Dot(PointAt(0, 0)),
               db = nb.Dot(b->PointAt(0, 0));

        Vector dl = na.Cross(nb);
        if(dl.Magnitude() < LENGTH_EPS) return; // parallel planes
        dl = dl.WithMagnitude(1);
        Vector p = Vector::AtIntersectionOfPlanes(na, da, nb, db);

        // Trim it to the region 0 <= {u,v} <= 1 for each plane; not strictly
        // necessary, since line will be split and excess edges culled, but
        // this improves speed and robustness.
        int i;
        double tmax = VERY_POSITIVE, tmin = VERY_NEGATIVE;
        for(i = 0; i < 2; i++) {
            SSurface *s = (i == 0) ? this : b;
            Vector tu, tv;
            s->TangentsAt(0, 0, &tu, &tv);

            double up, vp, ud, vd;
            s->ClosestPointTo(p, &up, &vp);
            ud = (dl.Dot(tu)) / tu.MagSquared();
            vd = (dl.Dot(tv)) / tv.MagSquared();

            // so u = up + t*ud
            //    v = vp + t*vd
            if(ud > LENGTH_EPS) {
                tmin = max(tmin, -up/ud);
                tmax = min(tmax, (1 - up)/ud);
            } else if(ud < -LENGTH_EPS) {
                tmax = min(tmax, -up/ud);
                tmin = max(tmin, (1 - up)/ud);
            } else {
                if(up < -LENGTH_EPS || up > 1 + LENGTH_EPS) {
                    // u is constant, and outside [0, 1]
                    tmax = VERY_NEGATIVE;
                }
            }
            if(vd > LENGTH_EPS) {
                tmin = max(tmin, -vp/vd);
                tmax = min(tmax, (1 - vp)/vd);
            } else if(vd < -LENGTH_EPS) {
                tmax = min(tmax, -vp/vd);
                tmin = max(tmin, (1 - vp)/vd);
            } else {
                if(vp < -LENGTH_EPS || vp > 1 + LENGTH_EPS) {
                    // v is constant, and outside [0, 1]
                    tmax = VERY_NEGATIVE;
                }
            }
        }

        if(tmax > tmin + LENGTH_EPS) {
            SBezier bezier = SBezier::From(p.Plus(dl.ScaledBy(tmin)),
                                           p.Plus(dl.ScaledBy(tmax)));
            AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
        }
    } else if((degm == 1 && degn == 1 && isExtdb) ||
              (b->degm == 1 && b->degn == 1 && isExtdt))
    {
        // The intersection between a plane and a surface of extrusion
        SSurface *splane, *sext;
        if(degm == 1 && degn == 1) {
            splane = this;
            sext = b;
        } else {
            splane = b;
            sext = this;
        }

        Vector n = splane->NormalAt(0, 0).WithMagnitude(1), along;
        double d = n.Dot(splane->PointAt(0, 0));
        SBezier bezier;
        (void)sext->IsExtrusion(&bezier, &along);

        if(fabs(n.Dot(along)) < LENGTH_EPS) {
            // Direction of extrusion is parallel to plane; so intersection
            // is zero or more lines. Build a line within the plane, and
            // normal to the direction of extrusion, and intersect that line
            // against the surface; each intersection point corresponds to
            // a line.
            Vector pm, alu, p0, dp;
            // a point halfway along the extrusion
            pm = ((sext->ctrl[0][0]).Plus(sext->ctrl[0][1])).ScaledBy(0.5);
            alu = along.WithMagnitude(1);
            dp = (n.Cross(along)).WithMagnitude(1);
            // n, alu, and dp form an orthogonal csys; set n component to
            // place it on the plane, alu component to lie halfway along
            // extrusion, and dp component doesn't matter so zero
            p0 = n.ScaledBy(d).Plus(alu.ScaledBy(pm.Dot(alu)));

            List<SInter> inters = {};
            sext->AllPointsIntersecting(p0, p0.Plus(dp), &inters,
                /*asSegment=*/false, /*trimmed=*/false, /*inclTangent=*/true);

            SInter *si;
            for(si = inters.First(); si; si = inters.NextAfter(si)) {
                Vector al = along.ScaledBy(0.5);
                SBezier bezier;
                bezier = SBezier::From((si->p).Minus(al), (si->p).Plus(al));
                AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
            }

            inters.Clear();
        } else {
            // Direction of extrusion is not parallel to plane; so
            // intersection is projection of extruded curve into our plane.
            int i;
            for(i = 0; i <= bezier.deg; i++) {
                Vector p0 = bezier.ctrl[i],
                       p1 = p0.Plus(along);

                bezier.ctrl[i] =
                    Vector::AtIntersectionOfPlaneAndLine(n, d, p0, p1, NULL);
            }

            AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
        }
    } else if(isExtdt && isExtdb &&
                sqrt(fabs(alongt.Dot(alongb))) >
                sqrt(alongt.Magnitude() * alongb.Magnitude()) - LENGTH_EPS)
    {
        // Two surfaces of extrusion along the same axis. So they might
        // intersect along some number of lines parallel to the axis.
        Vector axis = alongt.WithMagnitude(1);

        List<SInter> inters = {};
        List<Vector> lv = {};

        double a_axis0 = (   ctrl[0][0]).Dot(axis),
               a_axis1 = (   ctrl[0][1]).Dot(axis),
               b_axis0 = (b->ctrl[0][0]).Dot(axis),
               b_axis1 = (b->ctrl[0][1]).Dot(axis);

        if(a_axis0 > a_axis1) swap(a_axis0, a_axis1);
        if(b_axis0 > b_axis1) swap(b_axis0, b_axis1);

        double ab_axis0 = max(a_axis0, b_axis0),
               ab_axis1 = min(a_axis1, b_axis1);

        if(fabs(ab_axis0 - ab_axis1) < LENGTH_EPS) {
            // The line would be zero-length
            return;
        }

        Vector axis0 = axis.ScaledBy(ab_axis0),
               axis1 = axis.ScaledBy(ab_axis1),
               axisc = (axis0.Plus(axis1)).ScaledBy(0.5);

        oft.MakePwlInto(&lv);

        int i;
        for(i = 0; i < lv.n - 1; i++) {
            Vector pa = lv.elem[i], pb = lv.elem[i+1];
            pa = pa.Minus(axis.ScaledBy(pa.Dot(axis)));
            pb = pb.Minus(axis.ScaledBy(pb.Dot(axis)));
            pa = pa.Plus(axisc);
            pb = pb.Plus(axisc);

            b->AllPointsIntersecting(pa, pb, &inters,
                /*asSegment=*/true,/*trimmed=*/false, /*inclTangent=*/false);
        }

        SInter *si;
        for(si = inters.First(); si; si = inters.NextAfter(si)) {
            Vector p = (si->p).Minus(axis.ScaledBy((si->p).Dot(axis)));
            double ub, vb;
            b->ClosestPointTo(p, &ub, &vb, /*mustConverge=*/true);
            SSurface plane;
            plane = SSurface::FromPlane(p, axis.Normal(0), axis.Normal(1));

            b->PointOnSurfaces(this, &plane, &ub, &vb);

            p = b->PointAt(ub, vb);

            SBezier bezier;
            bezier = SBezier::From(p.Plus(axis0), p.Plus(axis1));
            AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
        }

        inters.Clear();
        lv.Clear();
    } else {
        // Try intersecting the surfaces numerically, by a marching algorithm.
        // First, we find all the intersections between a surface and the
        // boundary of the other surface.
        SPointList spl = {};
        int a;
        for(a = 0; a < 2; a++) {
            SShell   *shA  = (a == 0) ? agnstA : agnstB;
            SSurface *srfA = (a == 0) ? this : b,
                     *srfB = (a == 0) ? b : this;

            SEdgeList el = {};
            srfA->MakeEdgesInto(shA, &el, MakeAs::XYZ, NULL);

            SEdge *se;
            for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
                List<SInter> lsi = {};

                srfB->AllPointsIntersecting(se->a, se->b, &lsi,
                    /*asSegment=*/true, /*trimmed=*/true, /*inclTangent=*/false);
                if(lsi.n == 0) continue;

                // Find the other surface that this curve trims.
                hSCurve hsc = { (uint32_t)se->auxA };
                SCurve *sc = shA->curve.FindById(hsc);
                hSSurface hother = (sc->surfA.v == srfA->h.v) ?
                                                    sc->surfB : sc->surfA;
                SSurface *other = shA->surface.FindById(hother);

                SInter *si;
                for(si = lsi.First(); si; si = lsi.NextAfter(si)) {
                    Vector p = si->p;
                    double u, v;
                    srfB->ClosestPointTo(p, &u, &v);
                    srfB->PointOnSurfaces(srfA, other, &u, &v);
                    p = srfB->PointAt(u, v);
                    if(!spl.ContainsPoint(p)) {
                        SPoint sp;
                        sp.p = p;
                        // We also need the edge normal, so that we know in
                        // which direction to march.
                        srfA->ClosestPointTo(p, &u, &v);
                        Vector n = srfA->NormalAt(u, v);
                        sp.auxv = n.Cross((se->b).Minus(se->a));
                        sp.auxv = (sp.auxv).WithMagnitude(1);

                        spl.l.Add(&sp);
                    }
                }
                lsi.Clear();
            }

            el.Clear();
        }

        while(spl.l.n >= 2) {
            SCurve sc = {};
            sc.surfA = h;
            sc.surfB = b->h;
            sc.isExact = false;
            sc.source = SCurve::Source::INTERSECTION;

            Vector start  = spl.l.elem[0].p,
                   startv = spl.l.elem[0].auxv;
            spl.l.ClearTags();
            spl.l.elem[0].tag = 1;
            spl.l.RemoveTagged();

            // Our chord tolerance is whatever the user specified
            double maxtol = SS.ChordTolMm();
            int maxsteps = max(300, SS.GetMaxSegments()*3);

            // The curve starts at our starting point.
            SCurvePt padd = {};
            padd.vertex = true;
            padd.p = start;
            sc.pts.Add(&padd);

            Point2d pa, pb;
            Vector np, npc = Vector::From(0, 0, 0);
            bool fwd = false;
            // Better to start with a too-small step, so that we don't miss
            // features of the curve entirely.
            double tol, step = maxtol;
            for(a = 0; a < maxsteps; a++) {
                ClosestPointTo(start, &pa);
                b->ClosestPointTo(start, &pb);

                Vector na =    NormalAt(pa).WithMagnitude(1),
                       nb = b->NormalAt(pb).WithMagnitude(1);

                if(a == 0) {
                    Vector dp = nb.Cross(na);
                    if(dp.Dot(startv) < 0) {
                        // We want to march in the more inward direction.
                        fwd = true;
                    } else {
                        fwd = false;
                    }
                }

                int i;
                for(i = 0; i < 20; i++) {
                    Vector dp = nb.Cross(na);
                    if(!fwd) dp = dp.ScaledBy(-1);
                    dp = dp.WithMagnitude(step);

                    np = start.Plus(dp);
                    npc = ClosestPointOnThisAndSurface(b, np);
                    tol = (npc.Minus(np)).Magnitude();

                    if(tol > maxtol*0.8) {
                        step *= 0.90;
                    } else {
                        step /= 0.90;
                    }

                    if((tol < maxtol) && (tol > maxtol/2)) {
                        // If we meet the chord tolerance test, and we're
                        // not too fine, then we break out.
                        break;
                    }
                }

                SPoint *sp;
                for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) {
                    if((sp->p).OnLineSegment(start, npc, 2*SS.ChordTolMm())) {
                        sp->tag = 1;
                        a = maxsteps;
                        npc = sp->p;
                    }
                }

                padd.p = npc;
                padd.vertex = (a == maxsteps);
                sc.pts.Add(&padd);

                start = npc;
            }

            spl.l.RemoveTagged();

            // And now we split and insert the curve
            SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b);
            sc.Clear();
            into->curve.AddAndAssignId(&split);
        }
        spl.Clear();
    }
}
Example #10
0
//-----------------------------------------------------------------------------
// Does the given point lie on our shell? There are many cases; inside and
// outside are obvious, but then there's all the edge-on-edge and edge-on-face
// possibilities.
//
// To calculate, we intersect a ray through p with our shell, and classify
// using the closest intersection point. If the ray hits a surface on edge,
// then just reattempt in a different random direction.
//-----------------------------------------------------------------------------
bool SShell::ClassifyEdge(int *indir, int *outdir,
                          Vector ea, Vector eb,
                          Vector p,
                          Vector edge_n_in, Vector edge_n_out, Vector surf_n)
{
    List<SInter> l;
    ZERO(&l);

    srand(0);

    // First, check for edge-on-edge
    int edge_inters = 0;
    Vector inter_surf_n[2], inter_edge_n[2];
    SSurface *srf;
    for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
        if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;

        SEdgeList *sel = &(srf->edges);
        SEdge *se;
        for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
            if((ea.Equals(se->a) && eb.Equals(se->b)) ||
               (eb.Equals(se->a) && ea.Equals(se->b)) ||
                p.OnLineSegment(se->a, se->b))
            {
                if(edge_inters < 2) {
                    // Edge-on-edge case
                    Point2d pm;
                    srf->ClosestPointTo(p,  &pm, false);
                    // A vector normal to the surface, at the intersection point
                    inter_surf_n[edge_inters] = srf->NormalAt(pm);
                    // A vector normal to the intersecting edge (but within the
                    // intersecting surface) at the intersection point, pointing
                    // out.
                    inter_edge_n[edge_inters] =
                      (inter_surf_n[edge_inters]).Cross((se->b).Minus((se->a)));
                }

                edge_inters++;
            }
        }
    }

    if(edge_inters == 2) {
        // TODO, make this use the appropriate curved normals
        double dotp[2];
        for(int i = 0; i < 2; i++) {
            dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]);
        }

        if(fabs(dotp[1]) < DOTP_TOL) {
            SWAP(double, dotp[0],         dotp[1]);
            SWAP(Vector, inter_surf_n[0], inter_surf_n[1]);
            SWAP(Vector, inter_edge_n[0], inter_edge_n[1]);
        }

        int coinc = (surf_n.Dot(inter_surf_n[0])) > 0 ? COINC_SAME : COINC_OPP;

        if(fabs(dotp[0]) < DOTP_TOL && fabs(dotp[1]) < DOTP_TOL) {
            // This is actually an edge on face case, just that the face
            // is split into two pieces joining at our edge.
            *indir  = coinc;
            *outdir = coinc;
        } else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] > DOTP_TOL) {
            if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
                *indir  = coinc;
                *outdir = OUTSIDE;
            } else {
                *indir  = INSIDE;
                *outdir = coinc;
            }
        } else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] < -DOTP_TOL) {
            if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
                *indir  = coinc;
                *outdir = INSIDE;
            } else {
                *indir  = OUTSIDE;
                *outdir = coinc;
            }
        } else if(dotp[0] > DOTP_TOL && dotp[1] > DOTP_TOL) {
            *indir  = INSIDE;
            *outdir = OUTSIDE;
        } else if(dotp[0] < -DOTP_TOL && dotp[1] < -DOTP_TOL) {
            *indir  = OUTSIDE;
            *outdir = INSIDE;
        } else {
            // Edge is tangent to the shell at shell's edge, so can't be
            // a boundary of the surface.
            return false;
        }
        return true;
    }

    if(edge_inters != 0) dbp("bad, edge_inters=%d", edge_inters);

    // Next, check for edge-on-surface. The ray-casting for edge-inside-shell
    // would catch this too, but test separately, for speed (since many edges
    // are on surface) and for numerical stability, so we don't pick up
    // the additional error from the line intersection.

    for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
        if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;

        Point2d puv;
        srf->ClosestPointTo(p, &(puv.x), &(puv.y), false);
        Vector pp = srf->PointAt(puv);

        if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
        Point2d dummy = { 0, 0 };
        int c = srf->bsp->ClassifyPoint(puv, dummy, srf);
        if(c == SBspUv::OUTSIDE) continue;

        // Edge-on-face (unless edge-on-edge above superceded)
        Point2d pin, pout;
        srf->ClosestPointTo(p.Plus(edge_n_in),  &pin,  false);
        srf->ClosestPointTo(p.Plus(edge_n_out), &pout, false);

        Vector surf_n_in  = srf->NormalAt(pin),
               surf_n_out = srf->NormalAt(pout);

        *indir  = ClassifyRegion(edge_n_in,  surf_n_in,  surf_n);
        *outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
        return true;
    }

    // Edge is not on face or on edge; so it's either inside or outside
    // the shell, and we'll determine which by raycasting.
    int cnt = 0;
    for(;;) {
        // Cast a ray in a random direction (two-sided so that we test if
        // the point lies on a surface, but use only one side for in/out
        // testing)
        Vector ray = Vector::From(Random(1), Random(1), Random(1));

        AllPointsIntersecting(
            p.Minus(ray), p.Plus(ray), &l, false, true, false);

        // no intersections means it's outside
        *indir  = OUTSIDE;
        *outdir = OUTSIDE;
        double dmin = VERY_POSITIVE;
        bool onEdge = false;
        edge_inters = 0;

        SInter *si;
        for(si = l.First(); si; si = l.NextAfter(si)) {
            double t = ((si->p).Minus(p)).DivPivoting(ray);
            if(t*ray.Magnitude() < -LENGTH_EPS) {
                // wrong side, doesn't count
                continue;
            }

            double d = ((si->p).Minus(p)).Magnitude();

            // We actually should never hit this case; it should have been
            // handled above.
            if(d < LENGTH_EPS && si->onEdge) {
                edge_inters++;
            }

            if(d < dmin) {
                dmin = d;
                // Edge does not lie on surface; either strictly inside
                // or strictly outside
                if((si->surfNormal).Dot(ray) > 0) {
                    *indir  = INSIDE;
                    *outdir = INSIDE;
                } else {
                    *indir  = OUTSIDE;
                    *outdir = OUTSIDE;
                }
                onEdge = si->onEdge;
            }
        }
        l.Clear();

        // If the point being tested lies exactly on an edge of the shell,
        // then our ray always lies on edge, and that's okay. Otherwise
        // try again in a different random direction.
        if(!onEdge) break;
        if(cnt++ > 5) {
            dbp("can't find a ray that doesn't hit on edge!");
            dbp("on edge = %d, edge_inters = %d", onEdge, edge_inters);
            SS.nakedEdges.AddEdge(ea, eb);
            break;
        }
    }

    return true;
}
Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe )
{
	trace_t	tr;
	float	fraction = 1.0f;
	bool	collided = false;
	Vector	normal	 = Vector( 0, 0, -1 );

	float	waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 );

	waterLevel -= GetAbsOrigin().z;
	waterLevel /= 150;

	if ( waterLevel < 1.0f )
	{
		collided = true;
		fraction = waterLevel;
	}

	AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
	
	if ( ( collided == false ) || ( tr.fraction < fraction ) )
	{
		fraction	= tr.fraction;
		normal		= tr.plane.normal;
	}

	if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) )
	{
#if FEELER_COLLISION_VISUALIZE
		NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f );
#endif
		
		Vector	probeDir = probe - GetLocalOrigin();

		Vector	normalToProbeAndWallNormal = probeDir.Cross( normal );
		Vector	steeringVector = normalToProbeAndWallNormal.Cross( probeDir );

		Vector	velDir = GetAbsVelocity();
		VectorNormalize( velDir );

		float	steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir );

		if ( steeringForce < 0.0f )
		{
			steeringForce = -steeringForce;
		}

		velDir = steeringVector;
		VectorNormalize( velDir );

		steeringVector = steeringForce * velDir;
		
		return steeringVector;
	}

#if FEELER_COLLISION_VISUALIZE
	NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f );
#endif

	return Vector( 0.0f, 0.0f, 0.0f );
}
Example #12
0
//-----------------------------------------------------------------------------
// Find all points where a line through a and b intersects our surface, and
// add them to the list. If seg is true then report only intersections that
// lie within the finite line segment (not including the endpoints); otherwise
// we work along the infinite line. And we report either just intersections
// inside the trim curve, or any intersection with u, v in [0, 1]. And we
// either disregard or report tangent points.
//-----------------------------------------------------------------------------
void SSurface::AllPointsIntersecting(Vector a, Vector b,
                                     List<SInter> *l,
                                     bool seg, bool trimmed, bool inclTangent)
{
    if(LineEntirelyOutsideBbox(a, b, seg)) return;

    Vector ba = b.Minus(a);
    double bam = ba.Magnitude();

    List<Inter> inters;
    ZERO(&inters);

    // All the intersections between the line and the surface; either special
    // cases that we can quickly solve in closed form, or general numerical.
    Vector center, axis, start, finish;
    double radius;
    if(degm == 1 && degn == 1) {
        // Against a plane, easy.
        Vector n = NormalAt(0, 0).WithMagnitude(1);
        double d = n.Dot(PointAt(0, 0));
        // Trim to line segment now if requested, don't generate points that
        // would just get discarded later.
        if(!seg ||
           (n.Dot(a) > d + LENGTH_EPS && n.Dot(b) < d - LENGTH_EPS) ||
           (n.Dot(b) > d + LENGTH_EPS && n.Dot(a) < d - LENGTH_EPS))
        {
            Vector p = Vector::AtIntersectionOfPlaneAndLine(n, d, a, b, NULL);
            Inter inter;
            ClosestPointTo(p, &(inter.p.x), &(inter.p.y));
            inters.Add(&inter);
        }
    } else if(IsCylinder(&axis, &center, &radius, &start, &finish)) {
        // This one can be solved in closed form too.
        Vector ab = b.Minus(a);
        if(axis.Cross(ab).Magnitude() < LENGTH_EPS) {
            // edge is parallel to axis of cylinder, no intersection points
            return;
        }
        // A coordinate system centered at the center of the circle, with
        // the edge under test horizontal
        Vector u, v, n = axis.WithMagnitude(1);
        u = (ab.Minus(n.ScaledBy(ab.Dot(n)))).WithMagnitude(1);
        v = n.Cross(u);
        Point2d ap = (a.Minus(center)).DotInToCsys(u, v, n).ProjectXy(),
                bp = (b.Minus(center)).DotInToCsys(u, v, n).ProjectXy(),
                sp = (start. Minus(center)).DotInToCsys(u, v, n).ProjectXy(),
                fp = (finish.Minus(center)).DotInToCsys(u, v, n).ProjectXy();

        double thetas = atan2(sp.y, sp.x), thetaf = atan2(fp.y, fp.x);

        Point2d ip[2];
        int ip_n = 0;
        if(fabs(fabs(ap.y) - radius) < LENGTH_EPS) {
            // tangent
            if(inclTangent) {
                ip[0] = Point2d::From(0, ap.y);
                ip_n = 1;
            }
        } else if(fabs(ap.y) < radius) {
            // two intersections
            double xint = sqrt(radius*radius - ap.y*ap.y);
            ip[0] = Point2d::From(-xint, ap.y);
            ip[1] = Point2d::From( xint, ap.y);
            ip_n = 2;
        }
        int i;
        for(i = 0; i < ip_n; i++) {
            double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap));
            // This is a point on the circle; but is it on the arc?
            Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t));
            double theta = atan2(pp.y, pp.x);
            double dp = WRAP_SYMMETRIC(theta  - thetas, 2*PI),
                   df = WRAP_SYMMETRIC(thetaf - thetas, 2*PI);
            double tol = LENGTH_EPS/radius;

            if((df > 0 && ((dp < -tol) || (dp > df + tol))) ||
               (df < 0 && ((dp >  tol) || (dp < df - tol))))
            {
                continue;
            }

            Vector p = a.Plus((b.Minus(a)).ScaledBy(t));

            Inter inter;
            ClosestPointTo(p, &(inter.p.x), &(inter.p.y));
            inters.Add(&inter);
        }
    } else {
        // General numerical solution by subdivision, fallback
        int cnt = 0, level = 0;
        AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, seg, this);
    }

    // Remove duplicate intersection points
    inters.ClearTags();
    int i, j;
    for(i = 0; i < inters.n; i++) {
        for(j = i + 1; j < inters.n; j++) {
            if(inters.elem[i].p.Equals(inters.elem[j].p)) {
                inters.elem[j].tag = 1;
            }
        }
    }
    inters.RemoveTagged();

    for(i = 0; i < inters.n; i++) {
        Point2d puv = inters.elem[i].p;

        // Make sure the point lies within the finite line segment
        Vector pxyz = PointAt(puv.x, puv.y);
        double t = (pxyz.Minus(a)).DivPivoting(ba);
        if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
            continue;
        }

        // And that it lies inside our trim region
        Point2d dummy = { 0, 0 };
        int c = bsp->ClassifyPoint(puv, dummy, this);
        if(trimmed && c == SBspUv::OUTSIDE) {
            continue;
        }

        // It does, so generate the intersection
        SInter si;
        si.p = pxyz;
        si.surfNormal = NormalAt(puv.x, puv.y);
        si.pinter = puv;
        si.srf = this;
        si.onEdge = (c != SBspUv::INSIDE);
        l->Add(&si);
    }

    inters.Clear();
}
Example #13
0
//-----------------------------------------------------------------------------
// Generates a single suggestion
//-----------------------------------------------------------------------------
bool CAI_PlaneSolver::GenerateCircleObstacleSuggestion( const CircleObstacles_t &obstacle, 
	const AILocalMoveGoal_t &moveGoal, float probeDist, const Vector& npcLoc, float radiusNpc )
{
	CBaseEntity *pObstacleEntity = NULL;

	float zDistTooFar;
	if ( obstacle.hEntity && obstacle.hEntity->CollisionProp() )
	{
		pObstacleEntity = obstacle.hEntity.Get();

		// HEY! I'm trying to avoid the very thing I'm trying to get to. This will make we wobble like a drunk as I approach. Don't do it.
		if( pObstacleEntity == moveGoal.pMoveTarget && (pObstacleEntity->IsNPC() || pObstacleEntity->IsPlayer()) )
			return false;

		Vector mins, maxs;
		pObstacleEntity->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
		zDistTooFar = ( maxs.z - mins.z ) * 0.5 + GetNpc()->GetHullHeight() * 0.5;
	}
	else
	{
		zDistTooFar = GetNpc()->GetHullHeight();
	}

	if ( fabs( obstacle.center.z - npcLoc.z ) > zDistTooFar )
		return false;

	Vector vecToNpc = npcLoc - obstacle.center;
	vecToNpc.z = 0;
	float distToObstacleSq = sq(vecToNpc.x) + sq(vecToNpc.y);
	float radius = obstacle.radius + radiusNpc;

	if ( distToObstacleSq <= 0.001 || distToObstacleSq >= sq( radius + probeDist ) )
		return false;

	Vector vecToObstacle = vecToNpc * -1;
	float distToObstacle = VectorNormalize( vecToObstacle );
	float weight;
	float arc;
	float radiusSq = sq(radius);

	float flDot = DotProduct( vecToObstacle, moveGoal.dir );

	// Don't steer around to avoid obstacles we've already passed, unless we're right up against them.
	// That is, do this computation without the probeDist added in.
	if( flDot < 0.0f && distToObstacleSq > radiusSq )
		return false;

	if ( radiusSq < distToObstacleSq )
	{
		Vector vecTangent;
		float distToTangent = FastSqrt( distToObstacleSq - radiusSq );

		float oneOverDistToObstacleSq = 1 / distToObstacleSq;

		vecTangent.x = ( -distToTangent * vecToNpc.x + radius * vecToNpc.y ) * oneOverDistToObstacleSq;
		vecTangent.y = ( -distToTangent * vecToNpc.y - radius * vecToNpc.x ) * oneOverDistToObstacleSq;
		vecTangent.z = 0;

		float cosHalfArc = vecToObstacle.Dot( vecTangent );
		arc = RAD2DEG(acosf( cosHalfArc )) * 2.0;
		weight = 1.0 - (distToObstacle - radius) / probeDist;
		if ( weight > 0.75 )
		{
			arc += (arc * 0.5) * (weight - 0.75) / 0.25;
		}

		Assert( weight >= 0.0 && weight <= 1.0 );

#if DEBUG_OBSTACLES
		// -------------------------
		Msg( "Adding arc %f, w %f\n", arc, weight );

		Vector pointTangent = npcLoc + ( vecTangent * distToTangent );

		NDebugOverlay::Line( npcLoc - Vector( 0, 0, 64 ), npcLoc + Vector(0,0,64), 0,255,0, false, 0.1 );
		NDebugOverlay::Line( center - Vector( 0, 0, 64 ), center + Vector(0,0,64), 0,255,0, false, 0.1 );
		NDebugOverlay::Line( pointTangent - Vector( 0, 0, 64 ), pointTangent + Vector(0,0,64), 0,255,0, false, 0.1 );

		NDebugOverlay::Line( npcLoc + Vector(0,0,64), center + Vector(0,0,64), 0,0,255, false, 0.1 );
		NDebugOverlay::Line( center + Vector(0,0,64), pointTangent + Vector(0,0,64), 0,0,255, false, 0.1 );
		NDebugOverlay::Line( pointTangent + Vector(0,0,64), npcLoc + Vector(0,0,64), 0,0,255, false, 0.1 );
#endif
	}
	else
	{
		arc = 210;
		weight = 1.0;
	}

	if ( obstacle.hEntity != NULL )
	{
		weight = AdjustRegulationWeight( obstacle.hEntity, weight );
	}

	AI_MoveSuggestion_t suggestion( obstacle.type, weight, UTIL_VecToYaw(vecToObstacle), arc );
	m_Solver.AddRegulation( suggestion );
	return true;
}
Example #14
0
    static unsigned int BoxBoxContacts(
      Vector&  p_a, ChMatrix33<>& R_a, Vector const & ext_a,
      Vector&  p_b, ChMatrix33<>& R_b, Vector const & ext_b,
      double const & envelope,
      Vector * p,
	  Vector & n,
	  double * distances
      )
    {
      assert(p);
      assert(distances);

	  unsigned int cnt = 0;

      //--- Sign lookup table, could be precomputed!!!
      Vector sign[8];
      for(unsigned int mask=0;mask<8;++mask)
      {
        sign[mask](0) = (mask&0x0001)?1:-1;
        sign[mask](1) = ((mask>>1)&0x0001)?1:-1;
        sign[mask](2) = ((mask>>2)&0x0001)?1:-1;
      }
      //--- extract axis of boxes in WCS
      Vector A[3];
      A[0].x = R_a(0,0);   A[0].y = R_a(1,0);   A[0].z = R_a(2,0);
      A[1].x = R_a(0,1);   A[1].y = R_a(1,1);   A[1].z = R_a(2,1);
      A[2].x = R_a(0,2);   A[2].y = R_a(1,2);   A[2].z = R_a(2,2);
      Vector B[3];
      B[0].x = R_b(0,0);   B[0].y = R_b(1,0);   B[0].z = R_b(2,0);
      B[1].x = R_b(0,1);   B[1].y = R_b(1,1);   B[1].z = R_b(2,1);
      B[2].x = R_b(0,2);   B[2].y = R_b(1,2);   B[2].z = R_b(2,2);

      //--- To compat numerical round-offs, these tend to favor edge-edge
      //--- cases, when one really rather wants a face-case. Truncating
      //--- seems to let the algorithm pick face cases over edge-edge
      //--- cases.
      unsigned int i;

      for( i=0;i<3;++i)
        for(unsigned int j=0;j<3;++j)
        {
          if(fabs(A[i](j))<10e-7)
            A[i](j) = 0.;
          if(fabs(B[i](j))<10e-7)
            B[i](j) = 0.;
        }

      Vector a[8];
      Vector b[8];
      //--- corner points of boxes in WCS
      for( i=0;i<8;++i)
      {
        a[i] = A[2]*(sign[i](2)*ext_a(2)) + A[1]*(sign[i](1)*ext_a(1)) + A[0]*(sign[i](0)*ext_a(0)) + p_a;
        b[i] = B[2]*(sign[i](2)*ext_b(2)) + B[1]*(sign[i](1)*ext_b(1)) + B[0]*(sign[i](0)*ext_b(0)) + p_b;
	  }
      //--- Potential separating axes in WCS
      Vector axis[15];
      axis[0] = A[0];
      axis[1] = A[1];
      axis[2] = A[2];
      axis[3] = B[0];
      axis[4] = B[1];
      axis[5] = B[2];
      axis[6].Cross(A[0],B[0]);
      if(axis[6](0)==0 && axis[6](1)==0 && axis[6](2)==0)
        axis[6] = A[0];
      else
        axis[6] /= sqrt(axis[6].Dot(axis[6]));
      axis[7].Cross(A[0],B[1]);
      if(axis[7](0)==0 && axis[7](1)==0 && axis[7](2)==0)
        axis[7] = A[0];
      else
        axis[7] /= sqrt(axis[7].Dot(axis[7]));
      axis[8].Cross(A[0],B[2]);
      if(axis[8](0)==0 && axis[8](1)==0 && axis[8](2)==0)
        axis[8] = A[0];
      else
        axis[8] /= sqrt(axis[8].Dot(axis[8]));
      axis[9].Cross(A[1],B[0]);
      if(axis[9](0)==0 && axis[9](1)==0 && axis[9](2)==0)
        axis[9] = A[1];
      else
        axis[9] /= sqrt(axis[9].Dot(axis[9]));
      axis[10].Cross(A[1],B[1]);
      if(axis[10](0)==0 && axis[10](1)==0 && axis[10](2)==0)
        axis[10] = A[1];
      else
        axis[10] /= sqrt(axis[10].Dot(axis[10]));
      axis[11].Cross(A[1],B[2]);
      if(axis[11](0)==0 && axis[11](1)==0 && axis[11](2)==0)
        axis[11] = A[1];
      else
        axis[11] /= sqrt(axis[11].Dot(axis[11]));
      axis[12].Cross(A[2],B[0]);
      if(axis[12](0)==0 && axis[12](1)==0 && axis[12](2)==0)
        axis[12] = A[2];
      else
        axis[12] /= sqrt(axis[12].Dot(axis[12]));
      axis[13].Cross(A[2],B[1]);
      if(axis[13](0)==0 && axis[13](1)==0 && axis[13](2)==0)
        axis[13] = A[2];
      else
        axis[13] /= sqrt(axis[13].Dot(axis[13]));
      axis[14].Cross(A[2],B[2]);
      if(axis[14](0)==0 && axis[14](1)==0 && axis[14](2)==0)
        axis[14] = A[2];
      else
        axis[14] /= sqrt(axis[14].Dot(axis[14]));
      //--- project vertices of boxes onto separating axis
      double min_proj_a[15];
      double min_proj_b[15];
      double max_proj_a[15];
      double max_proj_b[15];
      for(i=0;i<15;++i)
      {
        min_proj_a[i] = min_proj_b[i] = 10e30;
        max_proj_a[i] = max_proj_b[i] = -10e30;
      }
      for(i=0;i<15;++i)
      {
        for(unsigned int j=0;j<8;++j)
        {
          double proj_a = a[j].Dot(axis[i]);
          double proj_b = b[j].Dot(axis[i]);
          min_proj_a[i] = ChMin(min_proj_a[i],proj_a);
          max_proj_a[i] = ChMax(max_proj_a[i],proj_a);
          min_proj_b[i] = ChMin(min_proj_b[i],proj_b);
          max_proj_b[i] = ChMax(max_proj_b[i],proj_b);
        }
        //--- test for valid separation axis if so return
        if (min_proj_a[i] > (max_proj_b[i]+envelope) ||   min_proj_b[i] > (max_proj_a[i]+envelope))
          return 0;
      }
      //--- Compute box overlaps along all 15 separating axes, and determine
      //--- minimum overlap
      double overlap[15];
      double minimum_overlap = -10e30;
      unsigned int minimum_axis = 15;
      bool flip_axis[15];
      //--- Notice that edge-edge cases are testet last, so face cases
      //--- are favored over edge-edge cases
      for(i=0;i<15;++i)
      {
        flip_axis[i] = false;
        overlap[i] = 10e30;
        if(max_proj_a[i] <= min_proj_b[i])
        {
          overlap[i] = ChMin( overlap[i], min_proj_b[i] - max_proj_a[i] );
          if(overlap[i]>minimum_overlap)
          {
            minimum_overlap = overlap[i];
            minimum_axis = i;
            flip_axis[i] = false;
          }
        }
        if(max_proj_b[i] <= min_proj_a[i])
        {
          overlap[i] = ChMin( overlap[i], min_proj_a[i] - max_proj_b[i] );
          if(overlap[i]>minimum_overlap)
          {
            minimum_overlap = overlap[i];
            minimum_axis = i;
            flip_axis[i] = true;
          }
        }
        if(min_proj_a[i] <= min_proj_b[i] &&  min_proj_b[i] <= max_proj_a[i])
        {
          overlap[i] = ChMin( overlap[i], -(max_proj_a[i] - min_proj_b[i]) );
          if(overlap[i]>minimum_overlap)
          {
            minimum_overlap = overlap[i];
            minimum_axis = i;
            flip_axis[i] = false;
          }
        }
        if(min_proj_b[i] <= min_proj_a[i] &&  min_proj_a[i] <= max_proj_b[i])
        {
          overlap[i] = ChMin(overlap[i], -(max_proj_b[i] - min_proj_a[i]) );
          if(overlap[i]>minimum_overlap)
          {
            minimum_overlap = overlap[i];
            minimum_axis = i;
            flip_axis[i] = true;
          }
        }
      }
      if(minimum_overlap>envelope)
        return 0;
      //--- Take care of normals, so they point in the correct direction.
      for(i=0;i<15;++i)
      {
        if(flip_axis[i])
          axis[i] = - axis[i];
      }
      //--- At this point we know that a projection along axis[minimum_axis] with
      //--- value minimum_overlap will lead to non-penetration of the two boxes. We
      //--- just need to generate the contact points!!!
      unsigned int corners_inside = 0;
      unsigned int corners_B_in_A = 0;
      unsigned int corners_A_in_B = 0;
      bool AinB[8];
      bool BinA[8];
      Coordsys WCStoA(p_a, R_a.Get_A_quaternion());
	  Coordsys WCStoB(p_b, R_b.Get_A_quaternion());
      Vector eps_a = ext_a + Vector(envelope,envelope,envelope);
      Vector eps_b = ext_b + Vector(envelope,envelope,envelope);
      for(i=0;i<8;++i)
      {
		Vector a_in_B = WCStoB.TransformParentToLocal(a[i]);//ChTransform<>::TransformParentToLocal(a[i], p_a, R_a);
		// = WCStoB.TransformParentToLocal(a[i]);
        Vector abs_a(fabs(a_in_B.x),fabs(a_in_B.y),fabs(a_in_B.z) ) ;
        if(abs_a <= eps_b)
        {
          ++corners_inside;
          ++corners_A_in_B;
          AinB[i] = true;
        }
        else
          AinB[i] = false;
        Vector b_in_A = WCStoA.TransformParentToLocal(b[i]);//= ChTransform<>::TransformParentToLocal(b[i], p_b, R_b);
		// = WCStoA.TransformParentToLocal(b[i]);
        Vector abs_b(fabs(b_in_A.x),fabs(b_in_A.y),fabs(b_in_A.z) );
        if(abs_b <= eps_a)
        {
          ++corners_inside;
          ++corners_B_in_A;
          BinA[i] = true;
        }
        else
          BinA[i] = false;
      }
      //--- This may indicate an edge-edge case
      if(minimum_axis >= 6)
      {
        //--- However the edge-edge case may not be the best choice,
        //--- so if we find a corner point of one box being inside
        //--- the other, we fall back to use the face case with
        //--- minimum overlap.
        if(corners_inside)//--- Actually we only need to test end-points of edge for inclusion (4 points instead of 16!!!).
        {
          minimum_overlap = -10e30;
          minimum_axis = 15;
          for(unsigned int i=0;i<6;++i)
          {
            if(overlap[i]>minimum_overlap)
            {
              minimum_overlap = overlap[i];
              minimum_axis = i;
            }
          }
        }
      }

      //--- now we can safely pick the contact normal, since we
      //--- know wheter we have a face-case or edge-edge case.
      n = axis[minimum_axis];

      //--- This is definitely an edge-edge case
      if(minimum_axis>=6)
      {
        //--- Find a point p_a on the edge from box A.
        for(unsigned int i=0;i<3;++i)
          if(n.Dot(A[i]) > 0.)
            p_a += ext_a(i)*A[i];
          else
            p_a -= ext_a(i)*A[i];
        //--- Find a point p_b on the edge from box B.
        for(int ci=0;ci<3;++ci)
          if(n.Dot(B[ci]) < 0.)
            p_b += ext_b(ci)*B[ci];
          else
            p_b -= ext_b(ci)*B[ci];

        //--- Determine the indices of two unit edge direction vectors (columns
        //--- of rotation matrices in WCS).
        int columnA = ((minimum_axis)-6)/3;
        int columnB = ((minimum_axis)-6)%3;
        double s,t;
        //--- Compute the edge-paramter values s and t corresponding to the closest
        //--- points between the two infinite lines parallel to the two edges.
        ClosestPointsBetweenLines()(p_a,A[columnA],p_b,B[columnB],s,t);
        //--- Use the edge parameter values to compute the closest
        //--- points between the two edges.
        p_a += A[columnA]*s;
        p_b += B[columnB]*t;
        //--- Let the contact point be given by the mean of the closest points.
        p[0] = (p_a + p_b)*.5;
        distances[0] = overlap[minimum_axis];
        return 1;
      }
      //--- This is a face-``something else'' case, we actually already have taken
      //--- care of all corner points, but there might be some edge-edge crossings
      //--- generating contact points



      //--- Make sure that we work in the frame of the box that defines the contact
      //--- normal. This coordinate frame is nice, because the contact-face is a axis
      //--- aligned rectangle. We will refer to this frame as the reference frame, and
      //--- use the letter 'r' or 'R' for it. The other box is named the incident box,
      //--- its closest face towards the reference face is called the incidient face, and
      //--- is denoted by the letter 'i' or 'I'.
      Vector * R_r,* R_i;  //--- Box direction vectors in WCS
      Vector ext_r,ext_i;          //--- Box extents
      Vector p_r,p_i;              //--- Box centers in WCS
      bool * incident_inside;    //--- corner inside state of incident box.
      if (minimum_axis  < 3)
      {
        //--- This means that box A is defining the reference frame
        R_r = A;
        R_i = B;
        p_r = p_a;
        p_i = p_b;
        ext_r = ext_a;
        ext_i = ext_b;
        incident_inside = BinA;
      }
      else
      {
        //--- This means that box B is defining the reference frame
        R_r = B;
        R_i = A;
        p_r = p_b;
        p_i = p_a;
        ext_r = ext_b;
        ext_i = ext_a;
        incident_inside = AinB;
      }
      //--- Following vectors are used for computing the corner points of the incident
      //--- face. At first they are used to determine the axis of the incidient box
      //--- pointing towards the reference box.
      //---
      //--- n_r_wcs = normal pointing away from reference frame in WCS coordinates.
      //--- n_r = normal vector of reference face dotted with axes of incident box.
      //--- abs_n_r = absolute values of n_r.
      Vector n_r_wcs,n_r,abs_n_r;
      if (minimum_axis < 3)
      {
        n_r_wcs = n;
      }
      else
      {
        n_r_wcs = -n;
      }

      //--- Each of these is a measure for how much the axis' of the incident box
      //--- points in the direction of n_r_wcs. The largest absolute value give
      //--- us the axis along which we will find the closest face towards the reference
      //--- box. The sign will tell us if we should take the positive or negative
      //--- face to get the closest incident face.
      n_r(0) = R_i[0].Dot(n_r_wcs);
      n_r(1) = R_i[1].Dot(n_r_wcs);
      n_r(2) = R_i[2].Dot(n_r_wcs);
      abs_n_r(0) = fabs (n_r(0));
      abs_n_r(1) = fabs (n_r(1));
      abs_n_r(2) = fabs (n_r(2));
      //--- Find the largest compontent of abs_n_r: This corresponds to the normal
      //--- for the indident face. The axis number is stored in a3. the other
      //--- axis numbers of the indicent face are stored in a1,a2.
      int a1,a2,a3;
      if (abs_n_r(1) > abs_n_r(0))
      {
        if (abs_n_r(1) > abs_n_r(2))
        {
          a1 = 2; a2 = 0; a3 = 1;
        }
        else
        {
          a1 = 0; a2 = 1; a3 = 2;
        }
      }
      else
      {
        if (abs_n_r(0) > abs_n_r(2))
        {
          a1 = 1; a2 = 2; a3 = 0;
        }
        else
        {
          a1 = 0; a2 = 1; a3 = 2;
        }
      }
      //--- Now we have information enough to determine the incidient face, that means we can
      //--- compute the center point of incident face in WCS coordinates.

      int plus_sign[3];
      Vector center_i_wcs;
      if (n_r(a3) < 0)
      {
        center_i_wcs = p_i + ext_i(a3) * R_i[a3];
        plus_sign[a3] = 1;
      }
      else
      {
        center_i_wcs = p_i - ext_i(a3) * R_i[a3];
        plus_sign[a3] = 0;
      }
      //--- Compute difference of center point of incident face with center of reference coordinates.
      Vector center_ir = center_i_wcs - p_r;
      //--- Find the normal and non-normal axis numbers of the reference box
      int code1,code2,code3;
      if (minimum_axis < 3)
        code3 = minimum_axis;  //012
      else
        code3 = minimum_axis-3;  //345
      if (code3==0)
      {
        code1 = 1;
        code2 = 2;
      }
      else if (code3==1)
      {
        code1 = 2;
        code2 = 0;
      }
      else
      {
        code1 = 0;
        code2 = 1;
      }
      //--- Find the four corners of the incident face, in reference-face coordinates
      double quad[8]; //--- 2D coordinate of incident face (stored as x,y pairs).
      bool inside[4];     //--- inside state of the four coners of the quad
      //--- Project center_ri onto reference-face coordinate system (has origo
      //--- at the center of the reference face, and the two orthogonal unit vectors
      //--- denoted by R_r[code1] and R_r[code2] spaning the face-plane).
      double c1 = R_r[code1].Dot( center_ir);
      double c2 = R_r[code2].Dot( center_ir);
      //--- Compute the projections of the axis spanning the incidient
      //--- face, onto the axis spanning the reference face.
      //---
      //--- This will allow us to determine the coordinates in the reference-face
      //--- when we step along a direction of the incident face given by either
      //--- a1 or a2.
      double m11 = R_r[code1].Dot( R_i[a1]);
      double m12 = R_r[code1].Dot( R_i[a2]);
      double m21 = R_r[code2].Dot( R_i[a1]);
      double m22 = R_r[code2].Dot( R_i[a2]);
      {
        double k1 = m11 * ext_i(a1);
        double k2 = m21 * ext_i(a1);
        double k3 = m12 * ext_i(a2);
        double k4 = m22 * ext_i(a2);

        plus_sign[a1] = 0;
        plus_sign[a2] = 0;
        unsigned int mask = ( (plus_sign[a1]<<a1) |  (plus_sign[a2]<<a2) |  (plus_sign[a3]<<a3));
        inside[0] = incident_inside[ mask ];

        quad[0] = c1 - k1 - k3;
        quad[1] = c2 - k2 - k4;

        plus_sign[a1] = 0;
        plus_sign[a2] = 1;
        mask = (plus_sign[a1]<<a1 |  plus_sign[a2]<<a2 |  plus_sign[a3]<<a3);
        inside[1] = incident_inside[ mask ];

        quad[2] = c1 - k1 + k3;
        quad[3] = c2 - k2 + k4;

        plus_sign[a1] = 1;
        plus_sign[a2] = 1;
        mask = (plus_sign[a1]<<a1 |  plus_sign[a2]<<a2 |  plus_sign[a3]<<a3);
        inside[2] = incident_inside[ mask ];

        quad[4] = c1 + k1 + k3;
        quad[5] = c2 + k2 + k4;

        plus_sign[a1] = 1;
        plus_sign[a2] = 0;
        mask = (plus_sign[a1]<<a1 |  plus_sign[a2]<<a2 |  plus_sign[a3]<<a3);
        inside[3] = incident_inside[ mask ];

        quad[6] = c1 + k1 - k3;
        quad[7] = c2 + k2 - k4;
      }
      //--- find the size of the reference face
      double rect[2];
      rect[0] = ext_r(code1);
      rect[1] = ext_r(code2);

      //--- Intersect the edges of the incident and the reference face
      double crossings[16];
      unsigned int edge_crossings = RectQuadEdgeIntersectionTest()(envelope,rect,quad,inside,crossings);
      assert(edge_crossings<=8);

      if(!corners_inside && !edge_crossings)
        return 0;

      //--- Convert the intersection points into reference-face coordinates,
      //--- and compute the contact position and depth for each point.
      double det1 = 1./(m11*m22 - m12*m21);
      m11 *= det1;
      m12 *= det1;
      m21 *= det1;
      m22 *= det1;

      for (unsigned int j=0; j < edge_crossings; ++j)
      {
        //--- Get coordinates of edge-edge crossing point in reference face coordinate system.
        double p0 = crossings[j*2] - c1;
        double p1 = crossings[j*2+1] - c2;
        //--- Compute intersection point in (almost) WCS. Actually we have
        //--- displaced origin to center of reference frame box
        double k1 =  m22*p0 - m12*p1;
        double k2 = -m21*p0 + m11*p1;
        Vector point = center_ir + k1*R_i[a1] + k2*R_i[a2];
        //--- Depth of intersection point
        double depth = n_r_wcs.Dot(point) - ext_r(code3);
        if(depth<envelope)
        {
          p[cnt] = point + p_r;//--- Move origin from center of reference frame box to WCS
          distances[cnt] = depth;
          ++cnt;
        }
      }
      //      assert((corners_inside + cnt)<=8);//--- If not we are in serious trouble!!!
      //--- I think there is a special case, if corners_inside = 8 and
      //--- corners_in_A = 4 and corners_in_B = 4, then there really
      //--- can only be 4 contacts???

      if(corners_inside)
      {
        unsigned int start_corner_A = cnt;
        unsigned int end_corner_A = cnt;

        //--- Compute Displacement of contact plane from origin of WCS, the
        //--- contact plane is equal to the face plane of the reference box
        double w =   ext_r(code3) +  n_r_wcs.Dot(p_r);

        if(corners_A_in_B)
        {
          for (unsigned int i=0; i < 8; ++i)
          {
            if(AinB[i])
            {
              Vector point = a[i];
              double depth = n_r_wcs.Dot(point) - w;
              if(depth<envelope)
              {
                p[cnt] = point;
                distances[cnt] = depth;
                ++cnt;
              }
            }
          }
          end_corner_A = cnt;
        }
        if(corners_B_in_A)
        {
          for (unsigned int i=0; i < 8; ++i)
          {
            if(BinA[i])
            {
              Vector point = b[i];
              bool redundant = false;
              for(unsigned int j=start_corner_A;j<end_corner_A;++j)
              {
                if( p[j].Equals(point,envelope) )
                {
                  redundant = true;
                  break;
                }
              }
              if(redundant)
                continue;
              double depth = n_r_wcs.Dot(point) - w;
              if(depth<envelope)
              {
                p[cnt] = point;
                distances[cnt] = depth;
                ++cnt;
              }
            }
          }
        }
      }
      //      assert(cnt<=8);//--- If not we are in serious trouble!!!
      return cnt;
    };
Example #15
0
void AvoidPushawayProps( CBaseCombatCharacter *pPlayer, CUserCmd *pCmd )
{
	// Figure out what direction we're moving and the extents of the box we're going to sweep 
	// against physics objects.
	Vector currentdir;
	Vector rightdir;
	AngleVectors( pCmd->viewangles, &currentdir, &rightdir, NULL );

	CBaseEntity *props[512];
#ifdef CLIENT_DLL
	int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_CLIENT_SOLID_EDICTS, NULL );
#else
	int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_ENGINE_SOLID_EDICTS, NULL );
#endif

	const Vector & ourCenter = pPlayer->WorldSpaceCenter();
	Vector nearestPropPoint;
	Vector nearestPlayerPoint;

	for ( int i=0; i < nEnts; i++ )
	{
		// Don't respond to this entity on the client unless it has PHYSICS_MULTIPLAYER_FULL set.
		IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
		if ( pInterface && pInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
			continue;

		const float minMass = 10.0f; // minimum mass that can push a player back
		const float maxMass = 30.0f; // cap at a decently large value
		float mass = maxMass;
		if ( pInterface )
		{
			mass = pInterface->GetMass();
		}
		mass = clamp( mass, minMass, maxMass );
		
		mass = max( mass, 0 );
		mass /= maxMass; // bring into a 0..1 range

		// Push away from the collision point. The closer our center is to the collision point,
		// the harder we push away.
		props[i]->CollisionProp()->CalcNearestPoint( ourCenter, &nearestPropPoint );
		pPlayer->CollisionProp()->CalcNearestPoint( nearestPropPoint, &nearestPlayerPoint );
		Vector vPushAway = (nearestPlayerPoint - nearestPropPoint);
		float flDist = VectorNormalize( vPushAway );

		const float MaxPushawayDistance = 5.0f;
		if ( flDist > MaxPushawayDistance && !pPlayer->CollisionProp()->IsPointInBounds( nearestPropPoint ) )
		{
			continue;
		}

		// If we're not pushing, try from our center to the nearest edge of the prop
		if ( vPushAway.IsZero() )
		{
			vPushAway = (ourCenter - nearestPropPoint);
			flDist = VectorNormalize( vPushAway );
		}

		// If we're still not pushing, try from our center to the center of the prop
		if ( vPushAway.IsZero() )
		{
			vPushAway = (ourCenter - props[i]->WorldSpaceCenter());
			flDist = VectorNormalize( vPushAway );
		}

		flDist = max( flDist, 1 );

		float flForce = sv_pushaway_player_force.GetFloat() / flDist * mass;
		flForce = min( flForce, sv_pushaway_max_player_force.GetFloat() );

#ifndef CLIENT_DLL
		pPlayer->PushawayTouch( props[i] );

		// We can get right up next to rotating doors before they start to move, so scale back our force so we don't go flying
		if ( FClassnameIs( props[i], "func_door_rotating" ) || FClassnameIs( props[i], "prop_door_rotating" ) )
#endif
		{
			flForce *= 0.25f;
		}

		vPushAway *= flForce;

		pCmd->forwardmove += vPushAway.Dot( currentdir );
		pCmd->sidemove    += vPushAway.Dot( rightdir );
	}
}
void TextWindow::DescribeSelection(void) {
    Entity *e;
    Vector p;
    int i;
    Printf(false, "");

    if(gs.n == 1 && (gs.points == 1 || gs.entities == 1)) {
        e = SK.GetEntity(gs.points == 1 ? gs.point[0] : gs.entity[0]);

#define COSTR(p) \
    SS.MmToString((p).x), SS.MmToString((p).y), SS.MmToString((p).z)
#define PT_AS_STR "(%Fi%s%E, %Fi%s%E, %Fi%s%E)"
#define PT_AS_NUM "(%Fi%3%E, %Fi%3%E, %Fi%3%E)"
        switch(e->type) {
            case Entity::POINT_IN_3D:
            case Entity::POINT_IN_2D:
            case Entity::POINT_N_TRANS:
            case Entity::POINT_N_ROT_TRANS:
            case Entity::POINT_N_COPY:
            case Entity::POINT_N_ROT_AA:
                p = e->PointGetNum();
                Printf(false, "%FtPOINT%E at " PT_AS_STR, COSTR(p));
                break;

            case Entity::NORMAL_IN_3D:
            case Entity::NORMAL_IN_2D:
            case Entity::NORMAL_N_COPY:
            case Entity::NORMAL_N_ROT:
            case Entity::NORMAL_N_ROT_AA: {
                Quaternion q = e->NormalGetNum();
                p = q.RotationN();
                Printf(false, "%FtNORMAL / COORDINATE SYSTEM%E");
                Printf(true,  "  basis n = " PT_AS_NUM, CO(p));
                p = q.RotationU();
                Printf(false, "        u = " PT_AS_NUM, CO(p));
                p = q.RotationV();
                Printf(false, "        v = " PT_AS_NUM, CO(p));
                break;
            }
            case Entity::WORKPLANE: {
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(false, "%FtWORKPLANE%E");
                Printf(true, "   origin = " PT_AS_STR, COSTR(p));
                Quaternion q = e->Normal()->NormalGetNum();
                p = q.RotationN();
                Printf(true, "   normal = " PT_AS_NUM, CO(p));
                break;
            }
            case Entity::LINE_SEGMENT: {
                Vector p0 = SK.GetEntity(e->point[0])->PointGetNum();
                p = p0;
                Printf(false, "%FtLINE SEGMENT%E");
                Printf(true,  "   thru " PT_AS_STR, COSTR(p));
                Vector p1 = SK.GetEntity(e->point[1])->PointGetNum();
                p = p1;
                Printf(false, "        " PT_AS_STR, COSTR(p));
                Printf(true,  "   len = %Fi%s%E",
                    SS.MmToString((p1.Minus(p0).Magnitude())));
                break;
            }
            case Entity::CUBIC_PERIODIC:
            case Entity::CUBIC:
                int pts;
                if(e->type == Entity::CUBIC_PERIODIC) {
                    Printf(false, "%FtPERIODIC C2 CUBIC SPLINE%E");
                    pts = (3 + e->extraPoints);
                } else if(e->extraPoints > 0) {
                    Printf(false, "%FtINTERPOLATING C2 CUBIC SPLINE%E");
                    pts = (4 + e->extraPoints);
                } else {
                    Printf(false, "%FtCUBIC BEZIER CURVE%E");
                    pts = 4;
                }
                for(i = 0; i < pts; i++) {
                    p = SK.GetEntity(e->point[i])->PointGetNum();
                    Printf((i==0), "   p%d = " PT_AS_STR, i, COSTR(p));
                }
                break;

            case Entity::ARC_OF_CIRCLE: {
                Printf(false, "%FtARC OF A CIRCLE%E");
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(true,  "     center = " PT_AS_STR, COSTR(p));
                p = SK.GetEntity(e->point[1])->PointGetNum();
                Printf(true,  "  endpoints = " PT_AS_STR, COSTR(p));
                p = SK.GetEntity(e->point[2])->PointGetNum();
                Printf(false, "              " PT_AS_STR, COSTR(p));
                double r = e->CircleGetRadiusNum();
                Printf(true, "   diameter =  %Fi%s", SS.MmToString(r*2));
                Printf(false, "     radius =  %Fi%s", SS.MmToString(r));
                double thetas, thetaf, dtheta;
                e->ArcGetAngles(&thetas, &thetaf, &dtheta);
                Printf(false, "    arc len =  %Fi%s", SS.MmToString(dtheta*r));
                break;
            }
            case Entity::CIRCLE: {
                Printf(false, "%FtCIRCLE%E");
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(true,  "     center = " PT_AS_STR, COSTR(p));
                double r = e->CircleGetRadiusNum();
                Printf(true,  "   diameter =  %Fi%s", SS.MmToString(r*2));
                Printf(false, "     radius =  %Fi%s", SS.MmToString(r));
                break;
            }
            case Entity::FACE_NORMAL_PT:
            case Entity::FACE_XPROD:
            case Entity::FACE_N_ROT_TRANS:
            case Entity::FACE_N_ROT_AA:
            case Entity::FACE_N_TRANS:
                Printf(false, "%FtPLANE FACE%E");
                p = e->FaceGetNormalNum();
                Printf(true,  "   normal = " PT_AS_NUM, CO(p));
                p = e->FaceGetPointNum();
                Printf(false, "     thru = " PT_AS_STR, COSTR(p));
                break;

            case Entity::TTF_TEXT: {
                Printf(false, "%FtTRUETYPE FONT TEXT%E");
                Printf(true, "  font = '%Fi%s%E'", e->font.str);
                if(e->h.isFromRequest()) {
                    Printf(false, "  text = '%Fi%s%E' %Fl%Ll%f%D[change]%E",
                        e->str.str, &ScreenEditTtfText, e->h.request());
                    Printf(true, "  select new font");
                    SS.fonts.LoadAll();
                    int i;
                    for(i = 0; i < SS.fonts.l.n; i++) {
                        TtfFont *tf = &(SS.fonts.l.elem[i]);
                        if(strcmp(e->font.str, tf->FontFileBaseName())==0) {
                            Printf(false, "%Bp    %s",
                                (i & 1) ? 'd' : 'a',
                                tf->name.str);
                        } else {
                            Printf(false, "%Bp    %f%D%Fl%Ll%s%E%Bp",
                                (i & 1) ? 'd' : 'a',
                                &ScreenSetTtfFont, i,
                                tf->name.str,
                                (i & 1) ? 'd' : 'a');
                        }
                    }
                } else {
                    Printf(false, "  text = '%Fi%s%E'", e->str.str);
                }
                break;
            }

            default:
                Printf(true, "%Ft?? ENTITY%E");
                break;
        }

        Group *g = SK.GetGroup(e->group);
        Printf(false, "");
        Printf(false, "%FtIN GROUP%E      %s", g->DescriptionString());
        if(e->workplane.v == Entity::FREE_IN_3D.v) {
            Printf(false, "%FtNOT LOCKED IN WORKPLANE%E"); 
        } else {
            Entity *w = SK.GetEntity(e->workplane);
            Printf(false, "%FtIN WORKPLANE%E  %s", w->DescriptionString());
        }
        if(e->style.v) {
            Style *s = Style::Get(e->style);
            Printf(false, "%FtIN STYLE%E      %s", s->DescriptionString());
        } else {
            Printf(false, "%FtIN STYLE%E      none");
        }
        if(e->construction) {
            Printf(false, "%FtCONSTRUCTION");
        }
    } else if(gs.n == 2 && gs.points == 2) {
        Printf(false, "%FtTWO POINTS");
        Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "   at " PT_AS_STR, COSTR(p0));
        Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
        Printf(false, "      " PT_AS_STR, COSTR(p1));
        double d = (p1.Minus(p0)).Magnitude();
        Printf(true, "  d = %Fi%s", SS.MmToString(d));
    } else if(gs.n == 2 && gs.faces == 1 && gs.points == 1) {
        Printf(false, "%FtA POINT AND A PLANE FACE");
        Vector pt = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "        point = " PT_AS_STR, COSTR(pt));
        Vector n = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
        Printf(true,  " plane normal = " PT_AS_NUM, CO(n));
        Vector pl = SK.GetEntity(gs.face[0])->FaceGetPointNum();
        Printf(false, "   plane thru = " PT_AS_STR, COSTR(pl));
        double dd = n.Dot(pl) - n.Dot(pt);
        Printf(true,  "     distance = %Fi%s", SS.MmToString(dd));
    } else if(gs.n == 3 && gs.points == 2 && gs.vectors == 1) {
        Printf(false, "%FtTWO POINTS AND A VECTOR");
        Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "  pointA = " PT_AS_STR, COSTR(p0));
        Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
        Printf(false, "  pointB = " PT_AS_STR, COSTR(p1));
        Vector v  = SK.GetEntity(gs.vector[0])->VectorGetNum();
        v = v.WithMagnitude(1);
        Printf(true,  "  vector = " PT_AS_NUM, CO(v));
        double d = (p1.Minus(p0)).Dot(v);
        Printf(true,  "  proj_d = %Fi%s", SS.MmToString(d));
    } else if(gs.n == 2 && gs.lineSegments == 1 && gs.points == 1) {
        Entity *ln = SK.GetEntity(gs.entity[0]);
        Vector lp0 = SK.GetEntity(ln->point[0])->PointGetNum(),
               lp1 = SK.GetEntity(ln->point[1])->PointGetNum();
        Printf(false, "%FtLINE SEGMENT AND POINT%E");
        Printf(true,  "   ln thru " PT_AS_STR, COSTR(lp0));
        Printf(false, "           " PT_AS_STR, COSTR(lp1));
        Vector pp = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "     point " PT_AS_STR, COSTR(pp));
        Printf(true,  " pt-ln distance = %Fi%s%E",
            SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))));
    } else if(gs.n == 2 && gs.vectors == 2) {
        Printf(false, "%FtTWO VECTORS");

        Vector v0 = SK.GetEntity(gs.entity[0])->VectorGetNum(),
               v1 = SK.GetEntity(gs.entity[1])->VectorGetNum();
        v0 = v0.WithMagnitude(1);
        v1 = v1.WithMagnitude(1);

        Printf(true,  "  vectorA = " PT_AS_NUM, CO(v0));
        Printf(false, "  vectorB = " PT_AS_NUM, CO(v1));

        double theta = acos(v0.Dot(v1));
        Printf(true,  "    angle = %Fi%2%E degrees", theta*180/PI);
        while(theta < PI/2) theta += PI;
        while(theta > PI/2) theta -= PI; 
        Printf(false, " or angle = %Fi%2%E (mod 180)", theta*180/PI);
    } else if(gs.n == 2 && gs.faces == 2) {
        Printf(false, "%FtTWO PLANE FACES");

        Vector n0 = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
        Printf(true,  " planeA normal = " PT_AS_NUM, CO(n0));
        Vector p0 = SK.GetEntity(gs.face[0])->FaceGetPointNum();
        Printf(false, "   planeA thru = " PT_AS_STR, COSTR(p0));

        Vector n1 = SK.GetEntity(gs.face[1])->FaceGetNormalNum();
        Printf(true,  " planeB normal = " PT_AS_NUM, CO(n1));
        Vector p1 = SK.GetEntity(gs.face[1])->FaceGetPointNum();
        Printf(false, "   planeB thru = " PT_AS_STR, COSTR(p1));

        double theta = acos(n0.Dot(n1));
        Printf(true,  "         angle = %Fi%2%E degrees", theta*180/PI);
        while(theta < PI/2) theta += PI;
        while(theta > PI/2) theta -= PI; 
        Printf(false, "      or angle = %Fi%2%E (mod 180)", theta*180/PI);

        if(fabs(theta) < 0.01) {
            double d = (p1.Minus(p0)).Dot(n0);
            Printf(true,  "      distance = %Fi%s", SS.MmToString(d));
        }
    } else if(gs.n == 0 && gs.stylables > 0) {
        Printf(false, "%FtSELECTED:%E comment text");
    } else if(gs.n == 0 && gs.constraints == 1) {
        Printf(false, "%FtSELECTED:%E %s",
            SK.GetConstraint(gs.constraint[0])->DescriptionString());
    } else {
        int n = SS.GW.selection.n;
        Printf(false, "%FtSELECTED:%E %d item%s", n, n == 1 ? "" : "s");
    }

    if(shown.screen == SCREEN_STYLE_INFO && 
       shown.style.v >= Style::FIRST_CUSTOM && gs.stylables > 0)
    {
        // If we are showing a screen for a particular style, then offer the
        // option to assign our selected entities to that style.
        Style *s = Style::Get(shown.style);
        Printf(true, "%Fl%D%f%Ll(assign to style %s)%E",
            shown.style.v,
            &ScreenAssignSelectionToStyle,
            s->DescriptionString());
    }
    // If any of the selected entities have an assigned style, then offer
    // the option to remove that style.
    bool styleAssigned = false;
    for(i = 0; i < gs.entities; i++) {
        Entity *e = SK.GetEntity(gs.entity[i]);
        if(e->style.v != 0) {
            styleAssigned = true;
        }
    }
    for(i = 0; i < gs.constraints; i++) {
        Constraint *c = SK.GetConstraint(gs.constraint[i]);
        if(c->type == Constraint::COMMENT && c->disp.style.v != 0) {
            styleAssigned = true;
        }
    }
    if(styleAssigned) {
        Printf(true, "%Fl%D%f%Ll(remove assigned style)%E",
            0,
            &ScreenAssignSelectionToStyle);
    }

    Printf(true, "%Fl%f%Ll(unselect all)%E", &TextWindow::ScreenUnselectAll);
}
	void CBaseGrenadeProjectile::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
	{
		//Assume all surfaces have the same elasticity
		float flSurfaceElasticity = 1.0;

		//Don't bounce off of players with perfect elasticity
		if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
		{
			flSurfaceElasticity = 0.3;
		}

		// if its breakable glass and we kill it, don't bounce.
		// give some damage to the glass, and if it breaks, pass 
		// through it.
		bool breakthrough = false;

		if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable" ) )
		{
			breakthrough = true;
		}

		if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable_surf" ) )
		{
			breakthrough = true;
		}

		if (breakthrough)
		{
			CTakeDamageInfo info( this, this, 10, DMG_CLUB );
			trace.m_pEnt->DispatchTraceAttack( info, GetAbsVelocity(), &trace );

			ApplyMultiDamage();

			if( trace.m_pEnt->m_iHealth <= 0 )
			{
				// slow our flight a little bit
				Vector vel = GetAbsVelocity();

				vel *= 0.4;

				SetAbsVelocity( vel );
				return;
			}
		}
		
		float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
		flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );

		// NOTE: A backoff of 2.0f is a reflection
		Vector vecAbsVelocity;
		PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
		vecAbsVelocity *= flTotalElasticity;

		// Get the total velocity (player + conveyors, etc.)
		VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
		float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );

		// Stop if on ground.
		if ( trace.plane.normal.z > 0.7f )			// Floor
		{
			// Verify that we have an entity.
			CBaseEntity *pEntity = trace.m_pEnt;
			Assert( pEntity );

			SetAbsVelocity( vecAbsVelocity );

			if ( flSpeedSqr < ( 30 * 30 ) )
			{
				if ( pEntity->IsStandable() )
				{
					SetGroundEntity( pEntity );
				}

				// Reset velocities.
				SetAbsVelocity( vec3_origin );
				SetLocalAngularVelocity( vec3_angle );

				//align to the ground so we're not standing on end
				QAngle angle;
				VectorAngles( trace.plane.normal, angle );

				// rotate randomly in yaw
				angle[1] = random->RandomFloat( 0, 360 );

				// TODO: rotate around trace.plane.normal
				
				SetAbsAngles( angle );			
			}
			else
			{
				Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;	
				Vector vecBaseDir = GetBaseVelocity();
				VectorNormalize( vecBaseDir );
				float flScale = vecDelta.Dot( vecBaseDir );

				VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity ); 
				VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
				PhysicsPushEntity( vecVelocity, &trace );
			}
		}
		else
		{
			// If we get *too* slow, we'll stick without ever coming to rest because
			// we'll get pushed down by gravity faster than we can escape from the wall.
			if ( flSpeedSqr < ( 30 * 30 ) )
			{
				// Reset velocities.
				SetAbsVelocity( vec3_origin );
				SetLocalAngularVelocity( vec3_angle );
			}
			else
			{
				SetAbsVelocity( vecAbsVelocity );
			}
		}
		
		BounceSound();
	}
Example #18
0
Plane Triangle::GetPlane() const
{
    Vector Normal = ( ( m_Vec2 - m_Vec1 ).Cross( m_Vec3 - m_Vec1 ) ).GetNormalized();
    float Distance = -Normal.Dot( m_Vec1 );
    return Plane( Normal, Distance );
}
void GLWall::SetupLights()
{
	// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
	switch (type)
	{
	case RENDERWALL_FOGBOUNDARY:
	case RENDERWALL_MIRRORSURFACE:
	case RENDERWALL_COLOR:
	case RENDERWALL_COLORLAYER:
		return;
	}

	float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2};
	Plane p;

	lightdata.Clear();
	p.Init(vtx,4);

	if (!p.ValidNormal()) 
	{
		return;
	}
	FLightNode *node;
	if (seg->sidedef == NULL)
	{
		node = NULL;
	}
	else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
	{
		node = seg->sidedef->lighthead;
	}
	else if (sub)
	{
		// Polobject segs cannot be checked per sidedef so use the subsector instead.
		node = sub->lighthead;
	}
	else node = NULL;

	// Iterate through all dynamic lights which touch this wall and render them
	while (node)
	{
		if (!(node->lightsource->flags2&MF2_DORMANT))
		{
			iter_dlight++;

			Vector fn, pos;

			float x = FIXED2FLOAT(node->lightsource->X());
			float y = FIXED2FLOAT(node->lightsource->Y());
			float z = FIXED2FLOAT(node->lightsource->Z());
			float dist = fabsf(p.DistToPoint(x, z, y));
			float radius = (node->lightsource->GetRadius() * gl_lights_size);
			float scale = 1.0f / ((2.f * radius) - dist);

			if (radius > 0.f && dist < radius)
			{
				Vector nearPt, up, right;

				pos.Set(x,z,y);
				fn=p.Normal();
				fn.GetRightUp(right, up);

				Vector tmpVec = fn * dist;
				nearPt = pos + tmpVec;

				Vector t1;
				int outcnt[4]={0,0,0,0};
				texcoord tcs[4];

				// do a quick check whether the light touches this polygon
				for(int i=0;i<4;i++)
				{
					t1.Set(&vtx[i*3]);
					Vector nearToVert = t1 - nearPt;
					tcs[i].u = (nearToVert.Dot(right) * scale) + 0.5f;
					tcs[i].v = (nearToVert.Dot(up) * scale) + 0.5f;

					if (tcs[i].u<0) outcnt[0]++;
					if (tcs[i].u>1) outcnt[1]++;
					if (tcs[i].v<0) outcnt[2]++;
					if (tcs[i].v>1) outcnt[3]++;

				}
				if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) 
				{
					gl_GetLight(p, node->lightsource, true, false, lightdata);
				}
			}
		}
		node = node->nextLight;
	}

	dynlightindex = GLRenderer->mLights->UploadLights(lightdata);
}
Example #20
0
// Real-Time Collision Detection p.139
Vector Triangle::GetClosestPoint( const Vector& v, Vector* OutBarycentricCoords ) const
{
    Vector ab = m_Vec2 - m_Vec1;
    Vector ac = m_Vec3 - m_Vec1;
    Vector bc = m_Vec3 - m_Vec2;
    Vector av = v - m_Vec1;
    Vector bv = v - m_Vec2;
    Vector cv = v - m_Vec3;
    Vector va = -av;
    Vector vb = -bv;
    Vector vc = -cv;

    float snom = av.Dot( ab );
    float sdenom = bv.Dot( m_Vec1 - m_Vec2 );

    float tnom = av.Dot( ac );
    float tdenom = cv.Dot( m_Vec1 - m_Vec3 );

    if( snom <= 0.0f && tnom <= 0.0f )
    {
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 1.0f, 0.0f, 0.0f );
        }
        return m_Vec1;
    }

    float unom = bv.Dot( bc );
    float udenom = cv.Dot( m_Vec2 - m_Vec3 );

    if( sdenom <= 0.0f && unom <= 0.0f )
    {
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 0.0f, 1.0f, 0.0f );
        }
        return m_Vec2;
    }

    if( tdenom <= 0.0f && udenom <= 0.0f )
    {
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 0.0f, 0.0f, 1.0f );
        }
        return m_Vec3;
    }

    Vector n = ab.Cross( ac );

    float c = n.Dot( va.Cross( vb ) );

    if( c < 0.0f && snom >= 0.0f && sdenom >= 0.0f )
    {
        const float baryV = snom / ( snom + sdenom );
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 1.0f - baryV, baryV, 0.0f );
        }
        return m_Vec1 + ab * baryV;
    }

    float a = n.Dot( vb.Cross( vc ) );

    if( a <= 0.0f && unom >= 0.0f && udenom >= 0.0f )
    {
        const float baryW = unom / ( unom + udenom );
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 0.0f, 1.0f - baryW, baryW );
        }
        return m_Vec2 + bc * baryW;
    }

    float b = n.Dot( vc.Cross( va ) );

    if( b <= 0.0f && tnom >= 0.0f && tdenom >= 0.0f )
    {
        const float baryW = tnom / ( tnom + tdenom );
        if( OutBarycentricCoords )
        {
            *OutBarycentricCoords = Vector( 1.0f - baryW, 0.0f, baryW );
        }
        return m_Vec1 + ac * baryW;
    }

    float baryU = a / ( a + b + c );
    float baryV = b / ( a + b + c );
    float baryW = 1.0f - baryU - baryV;

    if( OutBarycentricCoords )
    {
        *OutBarycentricCoords = Vector( baryU, baryV, baryW );
    }

    return baryU * m_Vec1 + baryV * m_Vec2 + baryW * m_Vec3;
}
Example #21
0
//-----------------------------------------------------------------------------
// Purpose: Special draw for the warped overlay
//-----------------------------------------------------------------------------
void CWarpOverlay::Draw( bool bCacheFullSceneState )
{
	// Get the vector to the sun.
	Vector vToGlow;
	
	if( m_bDirectional )
		vToGlow = m_vDirection;
	else
		vToGlow = m_vPos - CurrentViewOrigin();

	VectorNormalize( vToGlow );

	float flDot = vToGlow.Dot( CurrentViewForward() );

	if( flDot <= g_flOverlayRange )
		return;

	UpdateGlowObstruction( vToGlow, bCacheFullSceneState );
	if( m_flGlowObstructionScale == 0 )
		return;
	
	CMatRenderContextPtr pRenderContext( materials );
	
	//FIXME: Allow multiple?
	for( int iSprite=0; iSprite < m_nSprites; iSprite++ )
	{
		CGlowSprite *pSprite = &m_Sprites[iSprite];

		// Figure out the color and size to draw it.
		float flHorzSize, flVertSize;
		Vector vColor;
		CalcSpriteColorAndSize( flDot, pSprite, &flHorzSize, &flVertSize, &vColor );
	
		// Setup the basis to draw the sprite.
		Vector vBasePt, vUp, vRight;
		CalcBasis( vToGlow, flHorzSize, flVertSize, vBasePt, vUp, vRight );

		// Draw the sprite.
		IMaterial *pMaterial = materials->FindMaterial( "sun/overlay", TEXTURE_GROUP_CLIENT_EFFECTS );
		IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, pMaterial );

		CMeshBuilder builder;
		builder.Begin( pMesh, MATERIAL_QUADS, 1 );
		
		Vector vPt;

		vPt = vBasePt - vRight + vUp;
		builder.Position3fv( vPt.Base() );
		builder.Color4f( VectorExpand(vColor), 1 );
		builder.TexCoord2f( 0, 0, 1 );
		builder.AdvanceVertex();

		vPt = vBasePt + vRight + vUp;
		builder.Position3fv( vPt.Base() );
		builder.Color4f( VectorExpand(vColor), 1 );
		builder.TexCoord2f( 0, 1, 1 );
		builder.AdvanceVertex();

		vPt = vBasePt + vRight - vUp;
		builder.Position3fv( vPt.Base() );
		builder.Color4f( VectorExpand(vColor), 1 );
		builder.TexCoord2f( 0, 1, 0 );
		builder.AdvanceVertex();

		vPt = vBasePt - vRight - vUp;
		builder.Position3fv( vPt.Base() );
		builder.Color4f( VectorExpand(vColor), 1 );
		builder.TexCoord2f( 0, 0, 0 );
		builder.AdvanceVertex();
		
		builder.End( false, true );
	}
}
//-----------------------------------------------------------------------------
// Copies the texture coordinate system from pFrom into pTo. Then it rotates 
// the texture around the edge until it's as close to pTo's normal as possible.
//-----------------------------------------------------------------------------
void CFaceEditMaterialPage::CopyTCoordSystem( const CMapFace *pFrom, CMapFace *pTo )
{
	Vector		axis[2], vEdge, vEdgePt, vOrigin;
	Vector		vFromPt, vNextFromPt;
	Vector		vToPt, vPrevToPt;
	Vector		vTestTextureNormal, vTextureNormal;
	VMatrix		mEdgeRotation, mOriginRotation, mTranslation;
	float		fAngle, fDot;
	bool		bRotate;
	float		fShift[2];
	Vector		vProjTexNormal;
	Vector		vProjPolyNormal;

	// The edge vector lies on both planes.
	vEdge = pFrom->plane.normal.Cross(pTo->plane.normal);
	VectorNormalize( vEdge );

	// To find a point on the plane, we make a plane from the edge vector and find the intersection
	// between the three planes (without the third plane, there are an infinite number of solutions).
	if( PlaneIntersection( VPlane(pFrom->plane.normal, pFrom->plane.dist),
		                   VPlane(pTo->plane.normal, pTo->plane.dist),
		                   VPlane(vEdge, 0.0f), vEdgePt ) )
	{
		bRotate = true;
	}
	else
	{
		// Ok, in this case, the planes are parallel so we don't need to rotate around the edge anyway!
		bRotate = false;
	}

	// Copy the texture coordinate system.
	axis[0] = pFrom->texture.UAxis.AsVector3D();
	axis[1] = pFrom->texture.VAxis.AsVector3D();
	fShift[0] = pFrom->texture.UAxis[3];
	fShift[1] = pFrom->texture.VAxis[3];
	vOrigin = axis[0]*fShift[0]*pFrom->texture.scale[0] + axis[1]*fShift[1]*pFrom->texture.scale[1];
	
	vTextureNormal = axis[0].Cross(axis[1]);
	VectorNormalize(vTextureNormal);
	if(bRotate)
	{
		// Project texture normal and poly normal into the plane of rotation
		// to get the angle between them.
		vProjTexNormal = vTextureNormal - vEdge * vEdge.Dot(vTextureNormal);
		vProjPolyNormal = pTo->plane.normal - vEdge * vEdge.Dot(pTo->plane.normal);

		VectorNormalize( vProjTexNormal );
		VectorNormalize( vProjPolyNormal );

		fDot = vProjTexNormal.Dot(vProjPolyNormal);
		fAngle = (float)(acos(fDot) * (180.0f / M_PI));
		if(fDot < 0.0f)
			fAngle = 180.0f - fAngle;

		// Ok, rotate them for the final values.
		mEdgeRotation = SetupMatrixAxisRot(vEdge, fAngle);
		axis[0] = mEdgeRotation.ApplyRotation(axis[0]);
		axis[1] = mEdgeRotation.ApplyRotation(axis[1]);

		// Origin needs translation and rotation to rotate around the edge.
		mTranslation = SetupMatrixTranslation(vEdgePt);
		mOriginRotation = ~mTranslation * mEdgeRotation * mTranslation;
		vOrigin = mOriginRotation * vOrigin;
	}

	pTo->texture.UAxis.AsVector3D() = axis[0];
	pTo->texture.VAxis.AsVector3D() = axis[1];

	pTo->texture.UAxis[3] = axis[0].Dot(vOrigin) / pFrom->texture.scale[0];
	pTo->texture.VAxis[3] = axis[1].Dot(vOrigin) / pFrom->texture.scale[1];
	pTo->NormalizeTextureShifts();

	pTo->texture.scale[0] = pFrom->texture.scale[0];
	pTo->texture.scale[1] = pFrom->texture.scale[1];

	// rotate is only for UI purposes, it doesn't actually do anything.
	pTo->texture.rotate = 0.0f;

	pTo->CalcTextureCoords();
}
Example #23
0
bool Quake3BSP_GL::PointInTriangle(int leafIndex, Vector vPos, Vector planeNorm, float distance)
{
	BSPLeaf *pLeaf = &m_pLeafs[leafIndex];
	int numFaces = pLeaf->numOfLeafFaces;

	// We have the position of the camera which is the origin of our ray.  Get the forward
	// vector of where the camera is looking and scale that out pretty far to get the end
	// point of the ray
	Vector tempPos = tempPos.scalar(-10000.0f, Camera::GetInstance().GetCamForward());

	Vector rayEndpoint = (tempPos + vPos);

	for(int i=0; i < numFaces; ++i){
		int faceIndex = m_pLeafFaces[pLeaf->leafface + i];

		BSPFace *pFace = &m_pFaces[faceIndex];

		// Check if this is a valid face first
		if((pFace->type != FACE_POLYGON))
				continue;

		for(int j=0; j<(pFace->numOfIndices); j+=3){
			// Get the 3 points from the vertex list for this triangle to test
			BSPVertex *pVertexOne = &m_pVerts[pFace->startVertIndex+m_pIndices[pFace->startIndex+j]];
			BSPVertex *pVertexTwo = &m_pVerts[pFace->startVertIndex+m_pIndices[pFace->startIndex+j+1]];
			BSPVertex *pVertexThree = &m_pVerts[pFace->startVertIndex+m_pIndices[pFace->startIndex+j+2]];

			// Now compute the edge vectors of the triangle
			Vector tempVect1 = pVertexTwo->vPosition - pVertexOne->vPosition;
			Vector tempVect2 = pVertexThree->vPosition - pVertexTwo->vPosition;
			Vector tempVect3 = pVertexOne->vPosition - pVertexThree->vPosition;

			// Compute the normal to the plane
			Vector triangleNormal = tempVect1.Cross(tempVect2);
			triangleNormal.normalize();

			// Get the length of the ray
			Vector rayLength = rayEndpoint - vPos;
			float length = rayLength.Length();

			// Compute the D portion of the plane equation Ax + By + Cz + D = 0
			float trianglePlaneD = (triangleNormal.Dot(pVertexOne->vPosition));
			Vector tempNorm = rayEndpoint;
			tempNorm.normalize();

			// Check if the ray is parallel to the plane and would thus never intersect
			float denominator = tempNorm.Dot(triangleNormal);
			if( denominator < 0.0f){
				// Ray parallel to the plane, no chance for intersection
				continue;
			}

			// Compute the possible point of intersection
			float t = ((trianglePlaneD -(vPos.Dot(triangleNormal)))/denominator);
			
			// Test if the point lies on the ray, if so, then test if the point is within the triangle
			if(t>0.0f && t<length){
				Vector testPoint = (vPos + tempNorm.scalar(t,tempNorm));

				Vector firstInner  = testPoint - pVertexOne->vPosition;
				Vector secondInner = testPoint - pVertexTwo->vPosition;
				Vector thirdInner  = testPoint - pVertexThree->vPosition;

				float signOne = triangleNormal.Dot(tempVect1.Cross(firstInner));
				float signTwo = triangleNormal.Dot(tempVect2.Cross(secondInner));
				float signThree = triangleNormal.Dot(tempVect3.Cross(thirdInner));

				if((signOne < 0.0f && signTwo < 0.0f && signThree < 0.0f ) || ( signOne > 0.0f && signTwo > 0.0f && signThree > 0.0f )){
					// if all the signs match, either all positive or all negative, then the point is in the triangle
					// pass back the vertices to the renderer to draw.

					Renderer::GetInstance().SetTriangleVertices(pVertexOne->vPosition - triangleNormal, pVertexTwo->vPosition - triangleNormal, pVertexThree->vPosition - triangleNormal);
					return true;
				} 
			}
		}
	}

	// If we went through all the triangles in all the faces, this leaf was not intersected with, keep searching.
	return false;
}
Example #24
0
inline void VectorRotate(const Vector &i, const matrix3x4 &matrix, Vector &o)
{
	o.x = i.Dot(Vector(matrix[0][0], matrix[0][1], matrix[0][2]));
	o.y = i.Dot(Vector(matrix[1][0], matrix[1][1], matrix[1][2]));
	o.z = i.Dot(Vector(matrix[2][0], matrix[2][1], matrix[2][2]));
}
//------------------------------------------------------------------------------
// Purpose : Starts the swing of the weapon and determines the animation
// Input   : bIsSecondary - is this a secondary attack?
//------------------------------------------------------------------------------
void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary )
{
	trace_t traceHit;

	// Try a ray
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( !pOwner )
		return;

	Vector swingStart = pOwner->Weapon_ShootPosition( );
	Vector forward;

	pOwner->EyeVectors( &forward, NULL, NULL );

	Vector swingEnd = swingStart + forward * GetRange();
	UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit );
	Activity nHitActivity = ACT_VM_HITCENTER;

#ifndef CLIENT_DLL
	// Like bullets, bludgeon traces have to trace against triggers.
	CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
	TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin );
#endif

	if ( traceHit.fraction == 1.0 )
	{
		float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM;  // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point

		// Back off by hull "radius"
		swingEnd -= forward * bludgeonHullRadius;

		UTIL_TraceHull( swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit );
		if ( traceHit.fraction < 1.0 && traceHit.m_pEnt )
		{
			Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart;
			VectorNormalize( vecToTarget );

			float dot = vecToTarget.Dot( forward );

			// YWB:  Make sure they are sort of facing the guy at least...
			if ( dot < 0.70721f )
			{
				// Force amiss
				traceHit.fraction = 1.0f;
			}
			else
			{
				nHitActivity = ChooseIntersectionPointAndActivity( traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner );
			}
		}
	}

	WeaponSound( SINGLE );

	// -------------------------
	//	Miss
	// -------------------------
	if ( traceHit.fraction == 1.0f )
	{
		nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER;

		// We want to test the first swing again
		Vector testEnd = swingStart + forward * GetRange();
		
		// See if we happened to hit water
		ImpactWater( swingStart, testEnd );
	}
	else
	{
		Hit( traceHit, nHitActivity );
	}

	// Send the anim
	SendWeaponAnim( nHitActivity );

	pOwner->SetAnimation( PLAYER_ATTACK1 );


	//Setup our next attack times
	m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
	m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
}
Example #26
0
inline void VectorTransform(const Vector &v, const matrix3x4 &matrix, Vector &o)
{
	o.x = v.Dot(Vector(matrix[0][0], matrix[0][1], matrix[0][2])) + matrix[0][3];
	o.y = v.Dot(Vector(matrix[1][0], matrix[1][1], matrix[1][2])) + matrix[1][3];
	o.z = v.Dot(Vector(matrix[2][0], matrix[2][1], matrix[2][2])) + matrix[2][3];
}
Example #27
0
void CTexelDiffuseMethod::GenerateTexel(size_t iTexel, CConversionMeshInstance* pMeshInstance, CConversionFace* pFace, CConversionVertex* pV1, CConversionVertex* pV2, CConversionVertex* pV3, raytrace::CTraceResult* tr, const Vector& vecUVPosition, raytrace::CRaytracer* pTracer)
{
	CConversionFace* pHitFace = tr->m_pMeshInstance->GetMesh()->GetFace(tr->m_iFace);
	CTexture& oTexture = m_aTextures[tr->m_pMeshInstance->GetMappedMaterial(pHitFace->m)->m_iMaterial];

	if (!oTexture.m_pclrData)
		return;

	CConversionMesh* pHitMesh = tr->m_pMeshInstance->GetMesh();

	// TODO: Use the nearest triangle in this face.
	CConversionVertex* pHitV1 = pHitFace->GetVertex(0);
	CConversionVertex* pHitV2 = pHitFace->GetVertex(1);
	CConversionVertex* pHitV3 = pHitFace->GetVertex(2);

	Vector2D vu1 = pHitMesh->GetUV(pHitV1->vu);
	Vector2D vu2 = pHitMesh->GetUV(pHitV2->vu);
	Vector2D vu3 = pHitMesh->GetUV(pHitV3->vu);

	Vector v1 = tr->m_pMeshInstance->GetVertex(pHitV1->v);
	Vector v2 = tr->m_pMeshInstance->GetVertex(pHitV2->v);
	Vector v3 = tr->m_pMeshInstance->GetVertex(pHitV3->v);

	// Find where the world point is in UV space.

	// First convert to barycentric coordinates.
	Vector u = v2 - v1;
	Vector v = v3 - v1;
	float uu = u.Dot(u);
	float uv = u.Dot(v);
	float vv = v.Dot(v);
	Vector w = tr->m_vecHit - v1;
	float wu = w.Dot(u);
	float wv = w.Dot(v);

	float D = uv * uv - uu * vv;

	float b1, b2, b3;

	b1 = (uv * wu - uu * wv) / D;
	b2 = (uv * wv - vv * wu) / D;
	b3 = 1 - b1 - b2;

	// The position of the traceline's hit in (u, v) texture space
	Vector2D vecWorldPosition = vu1 * b1 + vu2 * b2 + vu3 * b3;

	// Mutex may be dead, try to bail before.
	if (m_pGenerator->IsStopped())
		return;

	size_t iU = (size_t)((vecWorldPosition.x * oTexture.m_iWidth) - 0.5f);
	size_t iV = (size_t)((vecWorldPosition.y * oTexture.m_iHeight) - 0.5f);

	iU %= oTexture.m_iWidth;
	iV %= oTexture.m_iHeight;

	size_t iColorTexel;
	m_pGenerator->Texel(iU, iV, iColorTexel, oTexture.m_iWidth, oTexture.m_iHeight);

	Color clrData = oTexture.m_pclrData[iColorTexel];

	m_pGenerator->GetParallelizer()->LockData();

	m_avecDiffuseValues[iTexel] += Vector(clrData);
	m_aiDiffuseReads[iTexel]++;
	m_pGenerator->MarkTexelUsed(iTexel);

	m_pGenerator->GetParallelizer()->UnlockData();
}
bool CASW_Shieldbug::BlockedDamage( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
// 	if (IsMeleeAttacking())
// 	{
// 		if (asw_debug_shieldbug.GetBool())
// 			Msg("Not blocking as I'm meleeing\n");
// 		return false;
// 	}

	if ( ptr->hitgroup == HITGROUP_BONE_SHIELD )
		return true;

#if 0
	Vector sparkNormal;
	Vector vecDamagePos = info.GetDamagePosition();
	if ( info.GetAttacker() && vecDamagePos != vec3_origin )
	{
		vecDamagePos = info.GetAttacker()->GetAbsOrigin();	// use the attacker's position when determining block, to stop marines shooting through gaps and hurting the sbug from any angle
	}

	if ( vecDamagePos == vec3_origin )		// don't block non-locational damage (i.e. fire burning)
		return false;

	// if we're in the middle of meleeing, don't block damage
	if ( IsMeleeAttacking() )
	{
#ifdef GAME_DLL
		if ( asw_debug_shieldbug.GetBool() )
		{
			Msg( "Not blocking as I'm meleeing\n" );
		}
#endif
		return false;
	}

	sparkNormal = vecDamagePos - GetAbsOrigin();	// should be head pos
	VectorNormalize( sparkNormal );
	Vector vecFacing;
	AngleVectors( GetAbsAngles(), &vecFacing );
	vecFacing.z = 0;
	VectorNormalize( vecFacing );
	sparkNormal.z = 0;
	VectorNormalize( sparkNormal );
	float dot = vecFacing.Dot( sparkNormal );

#ifdef GAME_DLL
	if ( asw_debug_shieldbug.GetBool() )
	{
		Msg("Defending dot %f\n", dot);
	}
	if (dot > 0)
	{
		if ( asw_debug_shieldbug.GetBool() )
		{
			Msg( "  blocked damage\n" );
		}
		return true;
	}
	if ( asw_debug_shieldbug.GetBool() )
	{
		Msg( "  Not blocked dmg\n" );
	}
#else
	if ( dot > 0 )
	{
		return true;
	}
#endif

#endif
	return false;
}
Example #29
0
bool Segment::Intersects( const Triangle& t, CollisionInfo* const pInfo /*= NULL*/ ) const
{
	// Could be optimized
	// See Real-Time Collision Detection p. 191
	Vector v12 = t.m_Vec2 - t.m_Vec1;
	Vector v13 = t.m_Vec3 - t.m_Vec1;
	Vector Direction = m_Point1 - m_Point2;

	Vector TriNormal = v12.Cross( v13 );

	float d = Direction.Dot( TriNormal );

	// If denominator is zero, segment is parallel to triangle
	if( d == 0.0f )
	{
		return false;
	}

	// If denominator is less than zero, segment points away from triangle
	if( d < 0.0f )
	{
		return false;
	}

	Vector v1p = m_Point1 - t.m_Vec1;
	float dt = v1p.Dot( TriNormal );

	if( dt < 0.0f )
	{
		return false;
	}

	if( dt > d )
	{
		return false;
	}

	Vector e = Direction.Cross( v1p );
	float v = v13.Dot( e );

	if( v < 0.0f || v > d )
	{
		return false;
	}

	float w = -( v12.Dot( e ) );

	if( w < 0.0f || v + w > d )
	{
		return false;
	}

	if( pInfo )
	{
		pInfo->m_Collision		= true;
		pInfo->m_HitT			= dt / d;
		pInfo->m_Intersection	= m_Point1 + pInfo->m_HitT * ( m_Point2 - m_Point1 );
		pInfo->m_Plane			= t.GetPlane();
	}

	return true;
}
Example #30
0
//------------------------------------------------------------------------------
void Plane::ToUVSpace( const Vector& _pos, float& o_u, float& o_v ) const
{
		o_u = _pos.Dot( m_u )/30.0 ;
		o_v = _pos.Dot( m_v )/30.0 ;
}