Exemple #1
0
static int
test_angle (const vec3_t angles)
{
	int         i;
	quat_t      rotation, r;
	vec3_t      scale, shear;
	mat3_t      mat;

	AngleQuat (angles, rotation);
	QuatToMatrix (rotation, mat, 0, 1);
	Mat3Decompose (mat, r, shear, scale);
	for (i = 0; i < 4; i++)
		if (!compare (rotation[i], r[i]))
			goto negate;
	return 1;
negate:
	// Mat3Decompose always sets the rotation quaternion's scalar to +ve
	// but AngleQuat might produce a -ve scalar.
	QuatNegate (r, r);
	for (i = 0; i < 4; i++)
		if (!compare (rotation[i], r[i]))
			goto fail;

	return 1;
fail:
	printf ("\n\n(%g %g %g)\n", VectorExpand (angles));
	printf ("  [%g %g %g %g]\n", QuatExpand (rotation));
	printf ("  [%g %g %g %g] [%g %g %g] [%g %g %g]\n",
			QuatExpand (r), VectorExpand (scale), VectorExpand (shear));
	return 0;
}
Exemple #2
0
static int
test_transform (const vec3_t angles, const vec3_t scale)
{
	int         i;
	const vec3_t v = {4,5,6};
	vec3_t      x, y;
	quat_t      rotation;
	mat3_t      mat;

	VectorCopy (v, x);
	AngleQuat (angles, rotation);
	VectorCompMult (scale, x, x);
	QuatMultVec (rotation, x, x);

	Mat3Init (rotation, scale, mat);
	Mat3MultVec (mat, v, y);

	for (i = 0; i < 3; i++)
		if (!compare (x[i], y[i]))
			goto fail;

	return 1;
fail:
	printf ("\n\n(%g %g %g) (%g %g %g)\n", VectorExpand (angles),
			VectorExpand (scale));
	printf ("  (%g %g %g)\n", VectorExpand (x));
	printf ("  (%g %g %g)\n", VectorExpand (y));
	return 0;
}
Exemple #3
0
static int
test_inverse (const vec3_t angles, const vec3_t scale)
{
	int         i;
	quat_t      rotation;
	mat3_t      mat, inv, I, res;

	AngleQuat (angles, rotation);
	Mat3Init (rotation, scale, mat);

	Mat3Identity (I);
	Mat3Inverse (mat, inv);
	Mat3Mult (mat, inv, res);

	for (i = 0; i < 3 * 3; i++)
		if (!compare (I[i], res[i]))
			goto fail;

	return 1;
fail:
	printf ("\n\n(%g %g %g) (%g %g %g)\n",
			VectorExpand (angles), VectorExpand (scale));
	printf ("  [%g %g %g]\n  [%g %g %g]\n  [%g %g %g]\n\n", Mat3Expand (mat));
	printf ("  [%g %g %g]\n  [%g %g %g]\n  [%g %g %g]\n\n", Mat3Expand (inv));
	printf ("  [%g %g %g]\n  [%g %g %g]\n  [%g %g %g]\n\n", Mat3Expand (res));
	return 0;
}
void CASW_Path_Utils::DebugDrawRoute( const Vector &vecStartPos, AI_Waypoint_t *pWaypoint )
{
	Vector vecLastPos = vecStartPos;
	Msg(" Pathstart = %f %f %f\n", VectorExpand( g_vecPathStart ) );
	while ( pWaypoint )		
	{
		Msg("  waypoint = %f %f %f\n", VectorExpand( pWaypoint->GetPos() ) );
		DebugDrawLine( vecLastPos + Vector( 0, 0, 10 ) , pWaypoint->GetPos() + Vector( 0, 0, 10 ), 255, 0, 255, true, 30.0f );
		vecLastPos = pWaypoint->GetPos();
		pWaypoint = pWaypoint->GetNext();
	}
}
void CASW_Spawn_Manager::Update()
{
	if ( m_iHordeToSpawn > 0 )
	{		
		if ( m_vecHordePosition != vec3_origin && ( !m_batchInterval.HasStarted() || m_batchInterval.IsElapsed() ) )
		{
			int iToSpawn = MIN( m_iHordeToSpawn, asw_max_alien_batch.GetInt() );
			int iSpawned = SpawnAlienBatch( asw_horde_class.GetString(), iToSpawn, m_vecHordePosition, m_angHordeAngle, 0 );
			if (asw_director_debug.GetInt() >= 4)
				Msg("spawned %d/%d %s (horde) at (%f, %f, %f)\n", iSpawned, m_iHordeToSpawn, asw_horde_class.GetString(), VectorExpand(m_vecHordePosition));
			m_iHordeToSpawn -= iSpawned;

			for (int i = 0; i < iSpawned; i++)
			{
				if (RandomFloat() < asw_horde_wanderers.GetFloat())
				{
					KeyValues *pWanderer = RandomWanderer();
					if (pWanderer)
					{
						FOR_EACH_TRUE_SUBKEY(pWanderer, pNPC)
						{
							if (V_stricmp(pNPC->GetName(), "NPC"))
							{
								Warning("Spawn Manager ignoring non-NPC key in WANDERER definition: %s\n", pNPC->GetName());
								continue;
							}

							const char *szAlienClass = pNPC->GetString("AlienClass");
							if (SpawnAlienAt(szAlienClass, m_vecHordePosition + Vector(0, 0, !V_stricmp(szAlienClass, "asw_buzzer") ? 128 : 32), m_angHordeAngle, pNPC))
							{
								if (asw_director_debug.GetInt() >= 4)
								{
									Msg("spawned %s (horde wanderer) at (%f, %f, %f)\n", szAlienClass, VectorExpand(m_vecHordePosition));
								}
							}
						}
					}
					else
					{
						const char *szAlienClass = asw_wanderer_class.GetString();
						if (SpawnAlienAt(szAlienClass, m_vecHordePosition + Vector(0, 0, !V_stricmp(szAlienClass, "asw_buzzer") ? 128 : 32), m_angHordeAngle))
						{
							if (asw_director_debug.GetInt() >= 4)
							{
								Msg("spawned %s (horde wanderer) at (%f, %f, %f)\n", szAlienClass, VectorExpand(m_vecHordePosition));
							}
						}
					}
				}
			}
void R_DrawPortals()
{
	// Draw the portals.
	if( !r_DrawPortals.GetInt() )
		return;

	IMaterial *pMaterial = materialSystemInterface->FindMaterial( "debug\\debugportals", NULL );
	IMesh *pMesh = materialSystemInterface->GetDynamicMesh( true, NULL, NULL, pMaterial );

	brushdata_t *pBrush = &host_state.worldmodel->brush;
	for( int i=0; i < pBrush->m_nAreaPortals; i++ )
	{
		dareaportal_t *pAreaPortal = &pBrush->m_pAreaPortals[i];

		if( !R_IsAreaVisible( pAreaPortal->otherarea ) )
			continue;

		CMeshBuilder builder;
		builder.Begin( pMesh, MATERIAL_LINES, pAreaPortal->m_nClipPortalVerts );

		for( int j=0; j < pAreaPortal->m_nClipPortalVerts; j++ )
		{
			unsigned short iVert;

			iVert = pAreaPortal->m_FirstClipPortalVert + j;
			builder.Position3f( VectorExpand( pBrush->m_pClipPortalVerts[iVert] ) );
			builder.Color4f( 0, 0, 0, 1 );
			builder.AdvanceVertex();

			iVert = pAreaPortal->m_FirstClipPortalVert + (j+1) % pAreaPortal->m_nClipPortalVerts;
			builder.Position3f( VectorExpand( pBrush->m_pClipPortalVerts[iVert] ) );
			builder.Color4f( 0, 0, 0, 1 );
			builder.AdvanceVertex();
		}

		builder.End( false, true );
	}

	// Draw the clip rectangles.
	for( i=0; i < g_PortalRects.Size(); i++ )
	{
		CPortalRect *pRect = &g_PortalRects[i];
		R_DrawScreenRect( pRect->left, pRect->top, pRect->right, pRect->bottom );
	}
	g_PortalRects.Purge();
}
void CASW_Weapon_Sniper_Rifle::UpdateDynamicLight()
{
	// DLIGHT disabled, since it looks bad
	return;

	C_ASW_Marine *pMarine =GetMarine();
	C_ASW_Player *pPlayer = pMarine ? pMarine->GetCommander() : NULL;
	
	if ( !pMarine || pMarine->GetActiveWeapon() != this || !pPlayer || !pMarine->IsInhabited() || !pPlayer->IsLocalPlayer() )
	{
		if (m_pSniperDynamicLight)
		{			
			m_pSniperDynamicLight->die = gpGlobals->curtime + 0.001;
			m_pSniperDynamicLight = NULL;
		}
		return;
	}

	if ( !m_pSniperDynamicLight || (m_pSniperDynamicLight->key != index) )
	{
		m_pSniperDynamicLight = effects->CL_AllocDlight ( index );
	}

	//m_fAmbientLight = asw_flashlight_marine_ambient.GetFloat();
	//m_fLightingScale = asw_flashlight_marine_lightscale.GetFloat();

	Vector vecForward, vecRight, vecUp;

	if (m_pSniperDynamicLight)
	{
		AngleVectors( GetLocalAngles(), &vecForward, &vecRight, &vecUp );
		m_pSniperDynamicLight->origin = pPlayer->GetCrosshairTracePos() + Vector( 0, 0, 10 );	
		Msg( "crosshair trace pos is %f %f %f\n", VectorExpand( pPlayer->GetCrosshairTracePos() ) );
		debugoverlay->AddTextOverlay( m_pSniperDynamicLight->origin, 0.01f, "Light" );
		m_pSniperDynamicLight->color.r = asw_sniper_dlight_r.GetInt();
		m_pSniperDynamicLight->color.g = asw_sniper_dlight_g.GetInt();
		m_pSniperDynamicLight->color.b = asw_sniper_dlight_b.GetInt();
		m_pSniperDynamicLight->radius = asw_sniper_dlight_radius.GetFloat();
		m_pSniperDynamicLight->color.exponent = asw_sniper_dlight_exponent.GetFloat();
		//m_pSniperDynamicLight->decay = 0;
		m_pSniperDynamicLight->die = gpGlobals->curtime + 30.0f;
	}
}
Exemple #8
0
void CDispInfo::ApplyTerrainMod( ITerrainMod *pMod )
{
    // New bbox.
    Vector bbMin(  1e24,  1e24,  1e24 );
    Vector bbMax( -1e24, -1e24, -1e24 );

    int nVerts, nIndices;
    CalcMaxNumVertsAndIndices( m_Power, &nVerts, &nIndices );

    // Ok, it probably touches us. Lock our buffer and change the verts.
    CMeshBuilder mb;
    mb.BeginModify( m_pMesh->m_pMesh, m_iVertOffset, nVerts );

    for( int iVert=0; iVert < nVerts; iVert++ )
    {
        if( m_AllowedVerts.Get( iVert ) )
        {
            Vector &vPos = m_Verts[iVert].m_vPos;
            Vector &vOriginalPos = m_Verts[iVert].m_vOriginalPos;

            if( pMod->ApplyMod( vPos, vOriginalPos ) )
                mb.Position3f( VectorExpand( vPos ) );

            VectorMin( vPos, bbMin, bbMin );
            VectorMax( vPos, bbMax, bbMax );
        }

        mb.AdvanceVertex();
    }

    mb.EndModify();

    // Set our new bounding box.
    m_BBoxMin = bbMin;
    m_BBoxMax = bbMax;
    UpdateCenterAndRadius();

    // Next time this displacement is seen, force it to rebuild and retesselate.
    m_bForceRebuild = true;
}
Exemple #9
0
static int
test_transform2 (const vec3_t angles, const vec3_t scale,
				 const vec3_t translation)
{
	int         i;
	const vec3_t v = {4,5,6};
	vec3_t      x, y;
	quat_t      rotation;
	mat4_t      mat;
	vec3_t      rot, sc, sh, tr;

	VectorCopy (v, x);
	AngleQuat (angles, rotation);
	VectorCompMult (scale, x, x);
	QuatMultVec (rotation, x, x);
	VectorAdd (translation, x, x);

	Mat4Init (rotation, scale, translation, mat);
	Mat4Decompose (mat, rot, sh, sc, tr);

	VectorCopy (v, y);
	QuatMultVec (rot, y, y);
	VectorShear (sh, y, y);
	VectorCompMult (sc, y, y);//scale
	VectorAdd (tr, y, y);

	for (i = 0; i < 3; i++)
		if (!compare (x[i], y[i]))
			goto fail;

	return 1;
fail:
	printf ("\n\n(%g %g %g) (%g %g %g) (%g %g %g) (%g %g %g)\n",
			VectorExpand (angles), VectorExpand (scale),
			VectorExpand (translation), VectorExpand (v));
	printf ("  (%g %g %g)\n", VectorExpand (x));
	printf ("  (%g %g %g)\n", VectorExpand (y));
	return 0;
}
void CGlowOverlay::Draw( bool bCacheFullSceneState )
{
	extern ConVar	r_drawsprites;
	if( !r_drawsprites.GetBool() )
		return;
	
	// 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() );

	UpdateGlowObstruction( vToGlow, bCacheFullSceneState );
	if( m_flGlowObstructionScale == 0 )
		return;
	
	bool bWireframe = ShouldDrawInWireFrameMode() || (r_drawsprites.GetInt() == 2);
	
	CMatRenderContextPtr pRenderContext( materials );

	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 );
	
		// If we're alpha'd out, then don't bother
		if ( vColor.LengthSqr() < 0.00001f )
			continue;
		
		// Setup the basis to draw the sprite.
		Vector vBasePt, vUp, vRight;
		CalcBasis( vToGlow, flHorzSize, flVertSize, vBasePt, vUp, vRight );

		//Get our diagonal radius
		float radius = (vRight+vUp).Length();
		if ( R_CullSphere( view->GetFrustum(), 5, &vBasePt, radius ) )
			continue;

		// Get our material (deferred default load)
		if ( m_Sprites[iSprite].m_pMaterial == NULL )
		{
			m_Sprites[iSprite].m_pMaterial = materials->FindMaterial( "sprites/light_glow02_add_noz", TEXTURE_GROUP_CLIENT_EFFECTS );
		}

		Assert( m_Sprites[iSprite].m_pMaterial );
		static unsigned int		nHDRColorScaleCache = 0;
		IMaterialVar *pHDRColorScaleVar = m_Sprites[iSprite].m_pMaterial->FindVarFast( "$hdrcolorscale", &nHDRColorScaleCache );
		if( pHDRColorScaleVar )
		{
			pHDRColorScaleVar->SetFloatValue( m_flHDRColorScale );
		}

		// Draw the sprite.
		IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, m_Sprites[iSprite].m_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 );

		if( bWireframe )
		{
			IMaterial *pWireframeMaterial = materials->FindMaterial( "debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER );
			pRenderContext->Bind( pWireframeMaterial );
			
			// Draw the sprite.
			IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, pWireframeMaterial );
			
			CMeshBuilder builder;
			builder.Begin( pMesh, MATERIAL_QUADS, 1 );
			
			Vector vPt;
			
			vPt = vBasePt - vRight + vUp;
			builder.Position3fv( vPt.Base() );
			builder.Color3f( 1.0f, 0.0f, 0.0f );
			builder.AdvanceVertex();
			
			vPt = vBasePt + vRight + vUp;
			builder.Position3fv( vPt.Base() );
			builder.Color3f( 1.0f, 0.0f, 0.0f );
			builder.AdvanceVertex();
			
			vPt = vBasePt + vRight - vUp;
			builder.Position3fv( vPt.Base() );
			builder.Color3f( 1.0f, 0.0f, 0.0f );
			builder.AdvanceVertex();
			
			vPt = vBasePt - vRight - vUp;
			builder.Position3fv( vPt.Base() );
			builder.Color3f( 1.0f, 0.0f, 0.0f );
			builder.AdvanceVertex();
			
			builder.End( false, true );
		}
	}
}
Exemple #11
0
CBaseEntity	* CASW_Rocket::FindPotentialTarget( void ) const
{
	float		bestdist = 0;		
	CBaseEntity	*bestent = NULL;

	Vector v_forward, v_right, v_up;
	AngleVectors( GetAbsAngles(), &v_forward, &v_right, &v_up );

	// find the aimtarget nearest us
	int count = AimTarget_ListCount();		
	if ( count )
	{
		CBaseEntity **pList = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count );
		AimTarget_ListCopy( pList, count );

		CTraceFilterSkipTwoEntities filter(this, GetOwnerEntity(), COLLISION_GROUP_NONE);

		for ( int i = 0; i < count; i++ )
		{
			CBaseEntity *pEntity = pList[i];

			if (!pEntity || !pEntity->IsAlive() || !pEntity->edict() || !pEntity->IsNPC() )
			{
				//Msg("not alive or not an edict, skipping\n");
				continue;
			}

			if (!pEntity || !pEntity->IsAlive() || !pEntity->edict() || !pEntity->IsNPC() )
			{
				//Msg("not alive or not an edict, skipping\n");
				continue;
			}
	
			// don't autoaim onto marines
			if (pEntity->Classify() == CLASS_ASW_MARINE || pEntity->Classify() == CLASS_ASW_COLONIST)
				continue;

			if ( pEntity->Classify() == CLASS_ASW_PARASITE )
			{
				CASW_Parasite *pParasite = static_cast< CASW_Parasite* >( pEntity );
				if ( pParasite->m_bInfesting )
				{
					continue;
				}
			}

			Vector center = pEntity->BodyTarget( GetAbsOrigin() );
			Vector center_flat = center;
			center_flat.z = GetAbsOrigin().z;

			Vector dir = (center - GetAbsOrigin());
			VectorNormalize( dir );

			Vector dir_flat = (center_flat - GetAbsOrigin());
			VectorNormalize( dir_flat );

			// make sure it's in front of the rocket
			float dot = DotProduct (dir, v_forward );
			//if (dot < 0)
			//{					
			//continue;
			//}

			float dist = (pEntity->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
			if (dist > ASW_ROCKET_MAX_HOMING_RANGE)
				continue;

			// check another marine isn't between us and the target to reduce FF
			trace_t tr;
			UTIL_TraceLine(GetAbsOrigin(), pEntity->WorldSpaceCenter(), MASK_SHOT, &filter, &tr);
			if (tr.fraction < 1.0f && tr.m_pEnt != pEntity && tr.m_pEnt && tr.m_pEnt->Classify() == CLASS_ASW_MARINE)
				continue;

			// does this critter already have enough rockets to kill it?
			{ 
				CASW_DamageAllocationMgr::IndexType_t assignmentIndex = m_RocketAssigner.Find( pEntity );
				if ( m_RocketAssigner.IsValid(assignmentIndex) )
				{
					if ( m_RocketAssigner[assignmentIndex].m_flAccumulatedDamage > pEntity->GetHealth() )
					{
						continue;
					}
				}
			}


			// check another marine isn't between us and the target to reduce FF
			UTIL_TraceLine(GetAbsOrigin(), pEntity->WorldSpaceCenter(), MASK_SHOT, &filter, &tr);
			if (tr.fraction < 1.0f && tr.m_pEnt != pEntity && tr.m_pEnt && tr.m_pEnt->Classify() == CLASS_ASW_MARINE)
				continue;

			// increase distance if dot isn't towards us
			dist += (1.0f - dot) * 150;	// bias of x units when object is 90 degrees to the side
			if (bestdist == 0 || dist < bestdist)
			{
				bestdist = dist;
				bestent = pEntity;
			}
		}

		if ( bestent && asw_rocket_debug.GetBool() )
		{
			Vector center = bestent->BodyTarget( GetAbsOrigin() );
			Vector center_flat = center;
			center_flat.z = GetAbsOrigin().z;

			Vector dir = (center - GetAbsOrigin());
			VectorNormalize( dir );
			Msg( "Rocket[%d] starting homing in on %s(%d) dir = %f %f %f\n", entindex(), bestent->GetClassname(), bestent->entindex(), VectorExpand( dir ) );
		}
	}

	return bestent;
}
void CASW_Spawn_Manager::Update()
{
    if ( m_iHordeToSpawn > 0 )
    {
        if ( m_vecHordePosition != vec3_origin && ( !m_batchInterval.HasStarted() || m_batchInterval.IsElapsed() ) )
        {
            int iToSpawn = MIN( m_iHordeToSpawn, asw_max_alien_batch.GetInt() );
            int iSpawned = SpawnAlienBatch( asw_horde_class.GetString(), iToSpawn, m_vecHordePosition, m_angHordeAngle, 0 );
            m_iHordeToSpawn -= iSpawned;
            if ( m_iHordeToSpawn <= 0 )
            {
                ASWDirector()->OnHordeFinishedSpawning();
                m_vecHordePosition = vec3_origin;
            }
            else if ( iSpawned == 0 )			// if we failed to spawn any aliens, then try to find a new horde location
            {
                if ( asw_director_debug.GetBool() )
                {
                    Msg( "Horde failed to spawn any aliens, trying new horde position.\n" );
                }
                if ( !FindHordePosition() )		// if we failed to find a new location, just abort this horde
                {
                    m_iHordeToSpawn = 0;
                    ASWDirector()->OnHordeFinishedSpawning();
                    m_vecHordePosition = vec3_origin;
                }
            }
            m_batchInterval.Start( asw_batch_interval.GetFloat() );
        }
        else if ( m_vecHordePosition == vec3_origin )
        {
            Msg( "Warning: Had horde to spawn but no position, clearing.\n" );
            m_iHordeToSpawn = 0;
            ASWDirector()->OnHordeFinishedSpawning();
        }
    }

    if ( asw_director_debug.GetBool() )
    {
        engine->Con_NPrintf( 14, "SM: Batch interval: %f pos = %f %f %f\n", m_batchInterval.HasStarted() ? m_batchInterval.GetRemainingTime() : -1, VectorExpand( m_vecHordePosition ) );
    }

    if ( m_iAliensToSpawn > 0 )
    {
        if (wandererSize() < WANDERER_QUEUE_SIZE-1) {
            queueWanderers();
            m_iAliensToSpawn--;
        }

    }

    if (wandererQueueStart != wandererQueueEnd) {
        while ( wandererQueueStart != wandererQueueEnd && SpawnAlientAtRandomNode());
    }
}
// heuristic to find reasonably open space - searches for areas with high node connectivity
CASW_Open_Area* CASW_Spawn_Manager::FindNearbyOpenArea( const Vector &vecSearchOrigin, int nSearchHull )
{
    CBaseEntity *pStartEntity = gEntList.FindEntityByClassname( NULL, "info_player_start" );
    int iNumNodes = g_pBigAINet->NumNodes();
    CAI_Node *pHighestConnectivity = NULL;
    int nHighestLinks = 0;
    for ( int i=0 ; i<iNumNodes; i++ )
    {
        CAI_Node *pNode = g_pBigAINet->GetNode( i );
        if ( !pNode || pNode->GetType() != NODE_GROUND )
            continue;

        Vector vecPos = pNode->GetOrigin();
        float flDist = vecPos.DistTo( vecSearchOrigin );
        if ( flDist > 400.0f )
            continue;

        // discard if node is too near start location
        if ( pStartEntity && vecPos.DistTo( pStartEntity->GetAbsOrigin() ) < 1400.0f )  // NOTE: assumes all start points are clustered near one another
            continue;

        // discard if node is inside an escape area
        bool bInsideEscapeArea = false;
        for ( int d=0; d<m_EscapeTriggers.Count(); d++ )
        {
            if ( m_EscapeTriggers[d]->CollisionProp()->IsPointInBounds( vecPos ) )
            {
                bInsideEscapeArea = true;
                break;
            }
        }
        if ( bInsideEscapeArea )
            continue;

        // count links that drones could follow
        int nLinks = pNode->NumLinks();
        int nValidLinks = 0;
        for ( int k = 0; k < nLinks; k++ )
        {
            CAI_Link *pLink = pNode->GetLinkByIndex( k );
            if ( !pLink )
                continue;

            if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) )
                continue;

            nValidLinks++;
        }
        if ( nValidLinks > nHighestLinks )
        {
            nHighestLinks = nValidLinks;
            pHighestConnectivity = pNode;
        }
        if ( asw_director_debug.GetBool() )
        {
            NDebugOverlay::Text( vecPos, UTIL_VarArgs( "%d", nValidLinks ), false, 10.0f );
        }
    }

    if ( !pHighestConnectivity )
        return NULL;

    // now, starting at the new node, find all nearby nodes with a minimum connectivity
    CASW_Open_Area *pArea = new CASW_Open_Area();
    pArea->m_vecOrigin = pHighestConnectivity->GetOrigin();
    pArea->m_pNode = pHighestConnectivity;
    int nMinLinks = nHighestLinks * 0.3f;
    nMinLinks = MAX( nMinLinks, 4 );

    pArea->m_aAreaNodes.AddToTail( pHighestConnectivity );
    if ( asw_director_debug.GetBool() )
    {
        Msg( "minLinks = %d\n", nMinLinks );
    }
    pArea->m_nTotalLinks = 0;
    for ( int i=0 ; i<iNumNodes; i++ )
    {
        CAI_Node *pNode = g_pBigAINet->GetNode( i );
        if ( !pNode || pNode->GetType() != NODE_GROUND )
            continue;

        Vector vecPos = pNode->GetOrigin();
        float flDist = vecPos.DistTo( pArea->m_vecOrigin );
        if ( flDist > 400.0f )
            continue;

        // discard if node is inside an escape area
        bool bInsideEscapeArea = false;
        for ( int d=0; d<m_EscapeTriggers.Count(); d++ )
        {
            if ( m_EscapeTriggers[d]->CollisionProp()->IsPointInBounds( vecPos ) )
            {
                bInsideEscapeArea = true;
                break;
            }
        }
        if ( bInsideEscapeArea )
            continue;

        // count links that drones could follow
        int nLinks = pNode->NumLinks();
        int nValidLinks = 0;
        for ( int k = 0; k < nLinks; k++ )
        {
            CAI_Link *pLink = pNode->GetLinkByIndex( k );
            if ( !pLink )
                continue;

            if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) )
                continue;

            nValidLinks++;
        }
        if ( nValidLinks >= nMinLinks )
        {
            pArea->m_aAreaNodes.AddToTail( pNode );
            pArea->m_nTotalLinks += nValidLinks;
        }
    }
    // highlight and measure bounds
    Vector vecAreaMins = Vector( FLT_MAX, FLT_MAX, FLT_MAX );
    Vector vecAreaMaxs = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX );

    for ( int i = 0; i < pArea->m_aAreaNodes.Count(); i++ )
    {
        vecAreaMins = VectorMin( vecAreaMins, pArea->m_aAreaNodes[i]->GetOrigin() );
        vecAreaMaxs = VectorMax( vecAreaMaxs, pArea->m_aAreaNodes[i]->GetOrigin() );

        if ( asw_director_debug.GetBool() )
        {
            if ( i == 0 )
            {
                NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 20.0f, 255, 255, 64, true, 10.0f );
            }
            else
            {
                NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 10.0f, 255, 128, 0, true, 10.0f );
            }
        }
    }

    Vector vecArea = ( vecAreaMaxs - vecAreaMins );
    float flArea = vecArea.x * vecArea.y;

    if ( asw_director_debug.GetBool() )
    {
        Msg( "area mins = %f %f %f\n", VectorExpand( vecAreaMins ) );
        Msg( "area maxs = %f %f %f\n", VectorExpand( vecAreaMaxs ) );
        NDebugOverlay::Box( vec3_origin, vecAreaMins, vecAreaMaxs, 255, 128, 128, 10, 10.0f );
        Msg( "Total links = %d Area = %f\n", pArea->m_nTotalLinks, flArea );
    }

    return pArea;
}
//-----------------------------------------------------------------------------
// 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 );
	}
}
static void VerifyNeighborConnections( CCoreDispInfo *pListBase, int nDisps )
{
	while ( 1 )
	{
		bool bHappy = true;

		for ( int i=0; i < nDisps; i++ )
		{
			CCoreDispInfo *pDisp = &pListBase[i];
			CDispUtilsHelper *pHelper = pDisp;

			for ( int iEdge=0; iEdge < 4; iEdge++ )
			{
				CDispEdgeIterator it( pHelper, iEdge );
				while ( it.Next() )
				{
					if ( !VerifyNeighborVertConnection( pHelper, it.GetVertIndex(), it.GetCurrentNeighbor(), it.GetNBVertIndex(), iEdge ) )
					{
						pDisp->GetEdgeNeighbor( iEdge )->SetInvalid();
						Warning( "Warning: invalid neighbor connection on displacement near (%.2f %.2f %.2f)\n", VectorExpand( pDisp->GetCornerPoint(0) ) );
						bHappy = false;
					}
				}			
			}
		}

		if ( bHappy )
			break;
	}
}