text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $NoKeywords: $
  //
  //===========================================================================//
  
  
  #include "render_pch.h"
  #include "client.h"
  #include "gl_model_private.h"
  #include "gl_water.h"
  #include "gl_cvars.h"
  #include "zone.h"
  #include "decal.h"
  #include "decal_private.h"
  #include "gl_lightmap.h"
  #include "r_local.h"
  #include "gl_matsysiface.h"
  #include "gl_rsurf.h"
  #include "materialsystem/imesh.h"
  #include "materialsystem/ivballoctracker.h"
  #include "tier2/tier2.h"
  #include "collisionutils.h"
  #include "cdll_int.h"
  #include "utllinkedlist.h"
  #include "r_areaportal.h"
  #include "bsptreedata.h"
  #include "cmodel_private.h"
  #include "tier0/dbg.h"
  #include "crtmemdebug.h"
  #include "iclientrenderable.h"
  #include "icliententitylist.h"
  #include "icliententity.h"
  #include "gl_rmain.h"
  #include "tier0/vprof.h"
  #include "bitvec.h"
  #include "debugoverlay.h"
  #include "host.h"
  #include "materialsystem/imaterialsystemhardwareconfig.h"
  #include "cl_main.h"
  #include "cmodel_engine.h"
  #include "r_decal.h"
  #include "materialsystem/materialsystem_config.h"
  #include "materialsystem/imaterialproxy.h"
  #include "materialsystem/imaterialvar.h"
  #include "coordsize.h"
  #include "mempool.h"
  #ifndef SWDS
  #include "Overlay.h"
  #endif
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  #define BACKFACE_EPSILON	-0.01f
  
  #define BRUSHMODEL_DECAL_SORT_GROUP		MAX_MAT_SORT_GROUPS
  const int MAX_VERTEX_FORMAT_CHANGES = 128;
  int g_MaxLeavesVisible = 512;
  
  //-----------------------------------------------------------------------------
  // forward declarations
  //-----------------------------------------------------------------------------
  
  class IClientEntity;
  
  // interface to shader drawing
  void Shader_BrushBegin( model_t *model, IClientEntity *baseentity = NULL );
  void Shader_BrushSurface( SurfaceHandle_t surfID, model_t *model, IClientEntity *baseentity = NULL );
  void Shader_BrushEnd( IMatRenderContext *pRenderContext, VMatrix const* brushToWorld, model_t *model, bool bShadowDepth, IClientEntity *baseentity = NULL );
  #ifdef NEWMESH
  void BuildMSurfaceVertexArrays( worldbrushdata_t *pBrushData, SurfaceHandle_t surfID, float overbright, CVertexBufferBuilder &builder );
  #else
  void BuildMSurfaceVertexArrays( worldbrushdata_t *pBrushData, SurfaceHandle_t surfID, float overbright, CMeshBuilder &builder );
  #endif
  
  //-----------------------------------------------------------------------------
  // Information about the fog volumes for this pass of rendering
  //-----------------------------------------------------------------------------
  
  struct FogState_t
  {
  	MaterialFogMode_t m_FogMode;
  	float m_FogStart;
  	float m_FogEnd;
  	float m_FogColor[3];
  	bool m_FogEnabled;
  };
  
  struct FogVolumeInfo_t : public FogState_t
  {
  	bool	m_InFogVolume;
  	float	m_FogSurfaceZ;
  	float	m_FogMinZ;
  	int		m_FogVolumeID;
  };
  
  //-----------------------------------------------------------------------------
  // Cached convars...
  //-----------------------------------------------------------------------------
  struct CachedConvars_t
  {
  	bool	m_bDrawWorld;
  	int		m_nDrawLeaf;
  	bool	m_bDrawFuncDetail;
  };
  
  
  static CachedConvars_t s_ShaderConvars;
  
  // AR - moved so SWDS can access these vars
  Frustum_t g_Frustum;
  
  
  //-----------------------------------------------------------------------------
  // Convars
  //-----------------------------------------------------------------------------
  static ConVar r_drawtranslucentworld( "r_drawtranslucentworld", "1", FCVAR_CHEAT );
  static ConVar mat_forcedynamic( "mat_forcedynamic", "0", FCVAR_CHEAT );
  static ConVar r_drawleaf( "r_drawleaf", "-1", FCVAR_CHEAT, "Draw the specified leaf." );
  static ConVar r_drawworld( "r_drawworld", "1", FCVAR_CHEAT, "Render the world." );
  static ConVar r_drawfuncdetail( "r_drawfuncdetail", "1", FCVAR_CHEAT, "Render func_detail" );
  static ConVar fog_enable_water_fog( "fog_enable_water_fog", "1", FCVAR_CHEAT );
  static ConVar r_fastzreject( "r_fastzreject", "0", FCVAR_ALLOWED_IN_COMPETITIVE, "Activate/deactivates a fast z-setting algorithm to take advantage of hardware with fast z reject. Use -1 to default to hardware settings" );
  static ConVar r_fastzrejectdisp( "r_fastzrejectdisp", "0", 0, "Activates/deactivates fast z rejection on displacements (360 only). Only active when r_fastzreject is on." );
  
  
  //-----------------------------------------------------------------------------
  // Installs a client-side renderer for brush models
  //-----------------------------------------------------------------------------
  static IBrushRenderer* s_pBrushRenderOverride = 0;
  
  //-----------------------------------------------------------------------------
  // Make sure we don't render the same surfaces twice
  //-----------------------------------------------------------------------------
  int	r_surfacevisframe = 0;
  #define r_surfacevisframe dont_use_r_surfacevisframe_here
  
  
  //-----------------------------------------------------------------------------
  // Fast z reject displacements?
  //-----------------------------------------------------------------------------
  static bool s_bFastZRejectDisplacements = false;
  
  //-----------------------------------------------------------------------------
  // Top view bounds
  //-----------------------------------------------------------------------------
  static bool r_drawtopview = false;
  static Vector2D s_OrthographicCenter;
  static Vector2D s_OrthographicHalfDiagonal;
  
  //-----------------------------------------------------------------------------
  //
  //-----------------------------------------------------------------------------
  typedef CVarBitVec CVisitedSurfs;
  
  
  //-----------------------------------------------------------------------------
  // Returns planes in brush models
  //-----------------------------------------------------------------------------
  int R_GetBrushModelPlaneCount( const model_t *model )
  {
  	return model->brush.nummodelsurfaces;
  }
  
  const cplane_t &R_GetBrushModelPlane( const model_t *model, int nIndex, Vector *pOrigin )
  {
  	SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  	surfID += nIndex;
  	Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  
  	if ( pOrigin )
  	{
  		int vertCount = MSurf_VertCount( surfID );
  		if ( vertCount > 0 )
  		{
  			int nFirstVertex = model->brush.pShared->vertindices[MSurf_FirstVertIndex( surfID )];
  			*pOrigin = model->brush.pShared->vertexes[nFirstVertex].position;
  		}
  		else
  		{
  			const cplane_t &plane = MSurf_Plane( surfID );
  			VectorMultiply( plane.normal, plane.dist, *pOrigin );
  		}
  	}
  
  	return MSurf_Plane( surfID );
  }
  
  //-----------------------------------------------------------------------------
  // Computes the centroid of a surface
  //-----------------------------------------------------------------------------
  void Surf_ComputeCentroid( SurfaceHandle_t surfID, Vector *pVecCentroid )
  {
  	int nCount = MSurf_VertCount( surfID );
  	int nFirstVertIndex = MSurf_FirstVertIndex( surfID );
  
  	float flTotalArea = 0.0f;
  	Vector vecNormal;
  	pVecCentroid->Init(0,0,0);
  	int vertIndex = host_state.worldbrush->vertindices[nFirstVertIndex];
  	Vector vecApex = host_state.worldbrush->vertexes[vertIndex].position;
  	for (int v = 1; v < nCount - 1; ++v )
  	{
  		vertIndex = host_state.worldbrush->vertindices[nFirstVertIndex+v];
  		Vector v1 = host_state.worldbrush->vertexes[vertIndex].position;
  		vertIndex = host_state.worldbrush->vertindices[nFirstVertIndex+v+1];
  		Vector v2 = host_state.worldbrush->vertexes[vertIndex].position;
  		CrossProduct( v2 - v1, v1 - vecApex, vecNormal );
  		float flArea = vecNormal.Length();
  		flTotalArea += flArea;
  		*pVecCentroid += (vecApex + v1 + v2) * flArea / 3.0f;
  	}
  
  	if (flTotalArea)
  	{
  		*pVecCentroid /= flTotalArea;
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Converts sort infos to lightmap pages
  //-----------------------------------------------------------------------------
  int SortInfoToLightmapPage( int sortID )
  {
          return materialSortInfoArray[sortID].lightmapPageID;
  }
  
  
  
  #ifndef SWDS
  
  class CWorldRenderList : public CRefCounted1<IWorldRenderList>
  {
  public:
  	CWorldRenderList()
  	{
  	}
  
  	~CWorldRenderList()
  	{
  		Purge();
  	}
  
  	static CWorldRenderList *FindOrCreateList( int nSurfaces )
  	{
  		CWorldRenderList *p = g_Pool.GetObject();
  		if ( p->m_VisitedSurfs.GetNumBits() == 0 )
  		{
  			p->Init( nSurfaces );
  		}
  		else
  		{
  			p->AddRef();
  		}
  
  		AssertMsg( p->m_VisitedSurfs.GetNumBits() == nSurfaces, "World render list pool not cleared between maps" );
  
  		return p;
  	}
  
  	static void PurgeAll()
  	{
  		CWorldRenderList *p;
  		while ( ( p = g_Pool.GetObject( false ) ) != NULL )
  		{
  			p->Purge();
  			delete p;
  		}
  	}
  
  	virtual bool OnFinalRelease()
  	{
  		Reset();
  		g_Pool.PutObject( this );
  		return false;
  	}
  
  	void Init( int nSurfaces )
  	{
  		m_SortList.Init(materials->GetNumSortIDs(), 512);
  		m_AlphaSortList.Init( g_MaxLeavesVisible, 64 );
  		m_DispSortList.Init(materials->GetNumSortIDs(), 32);
  		m_DispAlphaSortList.Init( g_MaxLeavesVisible, 32 );
  		m_VisitedSurfs.Resize( nSurfaces );
  		m_bSkyVisible = false;
  	}
  
  	void Purge()
  	{
  		g_MaxLeavesVisible = max(g_MaxLeavesVisible,m_VisibleLeaves.Count());
  
  		m_VisibleLeaves.Purge();
  		m_VisibleLeafFogVolumes.Purge();
  		for ( int i = 0; i < MAX_MAT_SORT_GROUPS; i++ )
  		{
  			m_ShadowHandles[i].Purge();
  			m_DlightSurfaces[i].Purge();
  		}
  		m_SortList.Shutdown();
  		m_AlphaSortList.Shutdown();
  		m_DispSortList.Shutdown();
  		m_DispAlphaSortList.Shutdown();
  	}
  
  	void Reset()
  	{
  		g_MaxLeavesVisible = max(g_MaxLeavesVisible,m_VisibleLeaves.Count());
  		m_SortList.Reset();
  		m_AlphaSortList.Reset();
  		m_DispSortList.Reset();
  		m_DispAlphaSortList.Reset();
  
  		m_bSkyVisible = false;
  		for (int j = 0; j < MAX_MAT_SORT_GROUPS; ++j)
  		{
  			//Assert(pRenderList->m_ShadowHandles[j].Count() == 0 );
  			m_ShadowHandles[j].RemoveAll();
  			m_DlightSurfaces[j].RemoveAll();
  		}
  
  		// We haven't found any visible leafs this frame
  		m_VisibleLeaves.RemoveAll();
  		m_VisibleLeafFogVolumes.RemoveAll();
  
  		m_VisitedSurfs.ClearAll();
  	}
  
  	CMSurfaceSortList m_SortList;
  	CMSurfaceSortList m_DispSortList;
  	CMSurfaceSortList m_AlphaSortList;
  	CMSurfaceSortList m_DispAlphaSortList;
  
  	//-------------------------------------------------------------------------
  	// List of decals to render this frame (need an extra one for brush models)
  	//-------------------------------------------------------------------------
  	CUtlVector<ShadowDecalHandle_t> m_ShadowHandles[MAX_MAT_SORT_GROUPS];
  	
  	// list of surfaces with dynamic lightmaps
  	CUtlVector<SurfaceHandle_t>	m_DlightSurfaces[MAX_MAT_SORT_GROUPS];
  
  	//-------------------------------------------------------------------------
  	// Used to generate a list of the leaves visited, and in back-to-front order
  	// for this frame of rendering
  	//-------------------------------------------------------------------------
  	CUtlVector<LeafIndex_t>		m_VisibleLeaves;
  	CUtlVector<LeafFogVolume_t>	m_VisibleLeafFogVolumes;
  
  	CVisitedSurfs m_VisitedSurfs;
  	bool						m_bSkyVisible;
  
  	static CObjectPool<CWorldRenderList> g_Pool;
  };
  
  CObjectPool<CWorldRenderList> CWorldRenderList::g_Pool;
  
  IWorldRenderList *AllocWorldRenderList()
  {
  	return CWorldRenderList::FindOrCreateList( host_state.worldbrush->numsurfaces );
  }
  
  
  FORCEINLINE bool VisitSurface( CVisitedSurfs &visitedSurfs, SurfaceHandle_t surfID )
  {
  	return !visitedSurfs.TestAndSet( MSurf_Index( surfID ) );
  }
  
  FORCEINLINE void MarkSurfaceVisited( CVisitedSurfs &visitedSurfs, SurfaceHandle_t surfID )
  {
  	visitedSurfs.Set( MSurf_Index( surfID ) );
  }
  
  FORCEINLINE bool VisitedSurface( CVisitedSurfs &visitedSurfs, SurfaceHandle_t surfID )
  {
  	return visitedSurfs.IsBitSet( MSurf_Index( surfID ) );
  }
  
  FORCEINLINE bool VisitedSurface( CVisitedSurfs &visitedSurfs, int index )
  {
  	return visitedSurfs.IsBitSet( index );
  }
  
  //-----------------------------------------------------------------------------
  // Activates top view
  //-----------------------------------------------------------------------------
  
  void R_DrawTopView( bool enable )
  {
  	r_drawtopview = enable;
  }
  
  void R_TopViewBounds( Vector2D const& mins, Vector2D const& maxs )
  {
  	Vector2DAdd( maxs, mins, s_OrthographicCenter );
  	s_OrthographicCenter *= 0.5f;
  	Vector2DSubtract( maxs, s_OrthographicCenter, s_OrthographicHalfDiagonal );
  }
  
  #define MOVE_DLIGHTS_TO_NEW_TEXTURE 0
  
  #if MOVE_DLIGHTS_TO_NEW_TEXTURE
  bool DlightSurfaceSetQueuingFlag(SurfaceHandle_t surfID)
  {
  	if ( MSurf_Flags( surfID ) & SURFDRAW_HASLIGHTSYTLES )
  	{
  		msurfacelighting_t *pLighting = SurfaceLighting(surfID);
  		for( int maps = 1; maps < MAXLIGHTMAPS && pLighting->m_nStyles[maps] != 255; maps++ )
  		{
  			if( d_lightstylenumframes[pLighting->m_nStyles[maps]] != 1 )
  			{
  				MSurf_Flags( surfID ) |= SURFDRAW_DLIGHTPASS;
  				return true;
  			}
  		}
  
  		return false;
  	}
  
  	MSurf_Flags( surfID ) |= SURFDRAW_DLIGHTPASS;
  	return true;
  }
  #else
  bool DlightSurfaceSetQueuingFlag(SurfaceHandle_t surfID) { return false; }
  #endif
  
  
  
  
  //-----------------------------------------------------------------------------
  // Adds surfaces to list of things to render
  //-----------------------------------------------------------------------------
  void Shader_TranslucentWorldSurface( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	Assert( !SurfaceHasDispInfo( surfID ) && (pRenderList->m_VisibleLeaves.Count() > 0) );
  
  	// Hook into the chain of translucent objects for this leaf
  	int sortGroup = MSurf_SortGroup( surfID );
  	pRenderList->m_AlphaSortList.AddSurfaceToTail( surfID, sortGroup, pRenderList->m_VisibleLeaves.Count()-1 );
  	if ( MSurf_Flags( surfID ) & (SURFDRAW_HASLIGHTSYTLES|SURFDRAW_HASDLIGHT) )
  	{
  		pRenderList->m_DlightSurfaces[sortGroup].AddToTail( surfID );
  		
  		DlightSurfaceSetQueuingFlag(surfID);
  	}
  }
  
  inline void Shader_WorldSurface( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	// Hook it into the list of surfaces to render with this material
  	// Do it in a way that generates a front-to-back ordering for fast z reject
  	Assert( !SurfaceHasDispInfo( surfID ) );
  
  	// Each surface is in exactly one group
  	int nSortGroup = MSurf_SortGroup( surfID );
  
  	// Add decals on non-displacement surfaces
  	if( SurfaceHasDecals( surfID ) )
  	{
  		DecalSurfaceAdd( surfID, nSortGroup );
  	}
  
  	int nMaterialSortID = MSurf_MaterialSortID( surfID );
  
  	if ( MSurf_Flags( surfID ) & (SURFDRAW_HASLIGHTSYTLES|SURFDRAW_HASDLIGHT) )
  	{
  		pRenderList->m_DlightSurfaces[nSortGroup].AddToTail( surfID );
  		if ( !DlightSurfaceSetQueuingFlag(surfID) )
  		{
  			pRenderList->m_SortList.AddSurfaceToTail( surfID, nSortGroup, nMaterialSortID );
  		}
  	}
  	else
  	{
  		pRenderList->m_SortList.AddSurfaceToTail( surfID, nSortGroup, nMaterialSortID );
  	}
  }
  
  // The NoCull flavor of this function optimizes for shadow depth map rendering
  // No decal work, dlights or material sorting, for example
  inline void Shader_WorldSurfaceNoCull( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	// Hook it into the list of surfaces to render with this material
  	// Do it in a way that generates a front-to-back ordering for fast z reject
  	Assert( !SurfaceHasDispInfo( surfID ) );
  
  	// Each surface is in exactly one group
  	int nSortGroup = MSurf_SortGroup( surfID );
  
  	int nMaterialSortID = MSurf_MaterialSortID( surfID );
  	pRenderList->m_SortList.AddSurfaceToTail( surfID, nSortGroup, nMaterialSortID );
  }
  
  
  //-----------------------------------------------------------------------------
  // Adds displacement surfaces to list of things to render
  //-----------------------------------------------------------------------------
  void Shader_TranslucentDisplacementSurface( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	Assert( SurfaceHasDispInfo( surfID ) && (pRenderList->m_VisibleLeaves.Count() > 0));
  
  	// For translucent displacement surfaces, they can exist in many
  	// leaves. We want to choose the leaf that's closest to the camera
  	// to render it in. Thankfully, we're iterating the tree in front-to-back
  	// order, so this is very simple.
  
  	// NOTE: You might expect some problems here when displacements cross fog volume 
  	// planes. However, these problems go away (I hope!) because the first planes
  	// that split a scene are the fog volume planes. That means that if we're
  	// in a fog volume, the closest leaf that the displacement will be in will
  	// also be in the fog volume. If we're not in a fog volume, the closest
  	// leaf that the displacement will be in will not be a fog volume. That should
  	// hopefully hide any discontinuities between fog state that occur when 
  	// rendering displacements that straddle fog volume boundaries.
  
  	// Each surface is in exactly one group
  	int sortGroup = MSurf_SortGroup( surfID );
  	if ( MSurf_Flags( surfID ) & (SURFDRAW_HASLIGHTSYTLES|SURFDRAW_HASDLIGHT) )
  	{
  		pRenderList->m_DlightSurfaces[sortGroup].AddToTail( surfID );
  		if ( !DlightSurfaceSetQueuingFlag(surfID) )
  		{
  			pRenderList->m_DispAlphaSortList.AddSurfaceToTail(surfID, sortGroup, pRenderList->m_VisibleLeaves.Count()-1);
  		}
  	}
  	else
  	{
  		pRenderList->m_DispAlphaSortList.AddSurfaceToTail(surfID, sortGroup, pRenderList->m_VisibleLeaves.Count()-1);
  	}
  }
  
  void Shader_DisplacementSurface( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	Assert( SurfaceHasDispInfo( surfID ) );
  
  	// For opaque displacement surfaces, we're going to build a temporary list of 
  	// displacement surfaces in each material bucket, and then add those to
  	// the actual displacement lists in a separate pass.
  	// We do this to sort the displacement surfaces by material
  
  	// Each surface is in exactly one group
  	int nSortGroup = MSurf_SortGroup( surfID );
  	int nMaterialSortID = MSurf_MaterialSortID( surfID );
  	if ( MSurf_Flags( surfID ) & (SURFDRAW_HASLIGHTSYTLES|SURFDRAW_HASDLIGHT) )
  	{
  		pRenderList->m_DlightSurfaces[nSortGroup].AddToTail( surfID );
  		if ( !DlightSurfaceSetQueuingFlag(surfID) )
  		{
  			pRenderList->m_DispSortList.AddSurfaceToTail( surfID, nSortGroup, nMaterialSortID );
  		}
  	}
  	else
  	{
  		pRenderList->m_DispSortList.AddSurfaceToTail( surfID, nSortGroup, nMaterialSortID );
  	}
  }
   
  //-----------------------------------------------------------------------------
  // Purpose: This draws a single surface using the dynamic mesh
  //-----------------------------------------------------------------------------
  void Shader_DrawSurfaceDynamic( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %d", __FUNCTION__, surfID );
  
  	if( !SurfaceHasPrims( surfID ) )
  	{
  		IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  		CMeshBuilder meshBuilder;
  		meshBuilder.Begin( pMesh, MATERIAL_POLYGON, MSurf_VertCount( surfID ) );
  		BuildMSurfaceVertexArrays( host_state.worldbrush, surfID, OVERBRIGHT, meshBuilder );
  		meshBuilder.End();
  		pMesh->Draw();
  		return;
  	}
  
  	mprimitive_t *pPrim = &host_state.worldbrush->primitives[MSurf_FirstPrimID( surfID )];
  
  	if ( pPrim->vertCount )
  	{
  #ifdef DBGFLAG_ASSERT
  		int primType = pPrim->type;
  #endif
  		IMesh *pMesh = pRenderContext->GetDynamicMesh( false );
  		CMeshBuilder meshBuilder;
  		for( int i = 0; i < MSurf_NumPrims( surfID ); i++, pPrim++ )
  		{
  			// Can't have heterogeneous primitive lists
  			Assert( primType == pPrim->type );
  			switch( pPrim->type )
  			{
  			case PRIM_TRILIST:
  				meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, pPrim->vertCount, pPrim->indexCount );
  				break;
  			case PRIM_TRISTRIP:
  				meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, pPrim->vertCount, pPrim->indexCount );
  				break;
  			default:
  				Assert( 0 );
  				return;
  			}
  			Assert( pPrim->indexCount );
  			BuildMSurfacePrimVerts( host_state.worldbrush, pPrim, meshBuilder, surfID );
  			BuildMSurfacePrimIndices( host_state.worldbrush, pPrim, meshBuilder );
  			meshBuilder.End();
  			pMesh->Draw();
  		}
  	}
  	else
  	{
  		// prims are just a tessellation
  		IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  		CMeshBuilder meshBuilder;
  		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, MSurf_VertCount( surfID ), pPrim->indexCount );
  		BuildMSurfaceVertexArrays( host_state.worldbrush, surfID, OVERBRIGHT, meshBuilder );
  		for ( int primIndex = 0; primIndex < pPrim->indexCount; primIndex++ )
  		{
  			meshBuilder.FastIndex( host_state.worldbrush->primindices[pPrim->firstIndex + primIndex] );
  		}
  
  		meshBuilder.End();
  		pMesh->Draw();
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: This draws a single surface using its static mesh
  //-----------------------------------------------------------------------------
  
  /*
  // NOTE: Since a static vb/dynamic ib IMesh doesn't buffer, we shouldn't use this
  // since it causes a lock and drawindexedprimitive per surface! (gary)
  void Shader_DrawSurfaceStatic( SurfaceHandle_t surfID )
  {
  	VPROF( "Shader_DrawSurfaceStatic" );
  	if ( 
  #ifdef USE_CONVARS
  		mat_forcedynamic.GetInt() || 
  #endif
  		(MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE) )
  	{
  		Shader_DrawSurfaceDynamic( pRenderContext, surfID );
  		return;
  	}
  
  	IMesh *pMesh = pRenderContext->GetDynamicMesh( true, 
  		g_pWorldStatic[MSurf_MaterialSortID( surfID )].m_pMesh );
  	CMeshBuilder meshBuilder;
  	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 0, (MSurf_VertCount( surfID )-2)*3 );
  
  	unsigned short startVert = MSurf_VertBufferIndex( surfID );
  	Assert(startVert!=0xFFFF);
  	for ( int v = 0; v < MSurf_VertCount( surfID )-2; v++ )
  	{
  		meshBuilder.Index( startVert );
  		meshBuilder.AdvanceIndex();
  		meshBuilder.Index( startVert + v + 1 );
  		meshBuilder.AdvanceIndex();
  		meshBuilder.Index( startVert + v + 2 );
  		meshBuilder.AdvanceIndex();
  	}
  	meshBuilder.End();
  	pMesh->Draw();
  }
  */
  
  //-----------------------------------------------------------------------------
  // Sets the lightmapping state
  //-----------------------------------------------------------------------------
  static inline void Shader_SetChainLightmapState( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID )
  {
  	if ( g_pMaterialSystemConfig->nFullbright == 1 )
  	{
  		if( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  		{
  			pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  		}
  		else
  		{
  			pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE );
  		}
  	}
  	else
  	{
  		Assert( MSurf_MaterialSortID( surfID ) >= 0 && MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  		pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Sets the lightmap + texture to render with
  //-----------------------------------------------------------------------------
  void Shader_SetChainTextureState( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID, IClientEntity* pBaseEntity, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	if ( bShadowDepth )
  	{
  		IMaterial *pDrawMaterial = MSurf_TexInfo( surfID )->material;
  		// Select proper override material
  		int nAlphaTest = (int) pDrawMaterial->IsAlphaTested();
  		int nNoCull = (int) pDrawMaterial->IsTwoSided();
  		IMaterial *pDepthWriteMaterial = g_pMaterialDepthWrite[nAlphaTest][nNoCull];
  
  		if ( nAlphaTest == 1 )
  		{
  			static unsigned int originalTextureVarCache = 0;
  			IMaterialVar *pOriginalTextureVar = pDrawMaterial->FindVarFast( "$basetexture", &originalTextureVarCache );
  			static unsigned int originalTextureFrameVarCache = 0;
  			IMaterialVar *pOriginalTextureFrameVar = pDrawMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache );
  			static unsigned int originalAlphaRefCache = 0;
  			IMaterialVar *pOriginalAlphaRefVar = pDrawMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
  
  			static unsigned int textureVarCache = 0;
  			IMaterialVar *pTextureVar = pDepthWriteMaterial->FindVarFast( "$basetexture", &textureVarCache );
  			static unsigned int textureFrameVarCache = 0;
  			IMaterialVar *pTextureFrameVar = pDepthWriteMaterial->FindVarFast( "$frame", &textureFrameVarCache );
  			static unsigned int alphaRefCache = 0;
  			IMaterialVar *pAlphaRefVar = pDepthWriteMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
  
  			if( pTextureVar && pOriginalTextureVar )
  			{
  				pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
  			}
  
  			if( pTextureFrameVar && pOriginalTextureFrameVar )
  			{
  				pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
  			}
  
  			if( pAlphaRefVar && pOriginalAlphaRefVar )
  			{
  				pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
  			}
  		}
  
  		pRenderContext->Bind( pDepthWriteMaterial );
  	}
  	else
  	{
  		pRenderContext->Bind( MSurf_TexInfo( surfID )->material, pBaseEntity ? pBaseEntity->GetClientRenderable() : NULL );
  		Shader_SetChainLightmapState( pRenderContext, surfID );
  	}
  }
  
  void Shader_DrawDynamicChain( const CMSurfaceSortList &sortList, const surfacesortgroup_t &group, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	SurfaceHandle_t hSurfID = sortList.GetSurfaceAtHead(group);
  	if ( !IS_SURF_VALID( hSurfID ))
  		return;
  	Shader_SetChainTextureState( pRenderContext, hSurfID, 0, bShadowDepth );
  
  	MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(sortList, group, surfID)
  	{
  		Shader_DrawSurfaceDynamic( pRenderContext, surfID, bShadowDepth );
  	}
  	MSL_FOREACH_SURFACE_IN_GROUP_END()
  }
  
  void Shader_DrawChainsDynamic( const CMSurfaceSortList &sortList, int nSortGroup, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  	{
  		Shader_DrawDynamicChain( sortList, group, bShadowDepth );
  	}
  	MSL_FOREACH_GROUP_END()
  }
  
  struct vertexformatlist_t
  {
  	unsigned short numbatches;
  	unsigned short firstbatch;
  #ifdef NEWMESH
  	IVertexBuffer *pVertexBuffer;
  #else
  	IMesh	*pMesh;
  #endif
  };
  
  struct batchlist_t
  {
  	SurfaceHandle_t	surfID;		// material and lightmap info
  	unsigned short firstIndex;
  	unsigned short numIndex;
  };
  
  void Shader_DrawChainsStatic( const CMSurfaceSortList &sortList, int nSortGroup, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	//VPROF("DrawChainsStatic");
  	CUtlVectorFixed<vertexformatlist_t, MAX_VERTEX_FORMAT_CHANGES> meshList;
  	int meshMap[MAX_VERTEX_FORMAT_CHANGES];
  	CUtlVectorFixedGrowable<batchlist_t, 512> batchList;
  	CUtlVectorFixedGrowable<const surfacesortgroup_t *, 8> dynamicGroups;
  	bool bWarn = true;
  #ifdef NEWMESH
  	CIndexBufferBuilder indexBufferBuilder;
  #else
  	CMeshBuilder meshBuilder;
  #endif
  
  	bool skipBind = false;
  	if ( g_pMaterialSystemConfig->nFullbright == 1  )
  	{
  		skipBind = true;
  	}
  
  	const CUtlVector<surfacesortgroup_t *> &groupList = sortList.GetSortList(nSortGroup);
  	int count = groupList.Count();
  	
  	int i, listIndex = 0;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	//PIXEVENT( pRenderContext, "Shader_DrawChainsStatic" );
  
  	int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
  	while ( listIndex < count )
  	{
  		const surfacesortgroup_t &groupBase = *groupList[listIndex];
  		SurfaceHandle_t surfIDBase = sortList.GetSurfaceAtHead( groupBase );
  		int sortIDBase = MSurf_MaterialSortID( surfIDBase );
  #ifdef NEWMESH
  		IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer( MATERIAL_INDEX_FORMAT_16BIT, false );
  		indexBufferBuilder.Begin( pBuildIndexBuffer, nMaxIndices );
  		IVertexBuffer *pLastVertexBuffer = NULL;
  #else
  		IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[sortIDBase] );
  		meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, nMaxIndices );
  		IMesh *pLastMesh = NULL;
  #endif
  		int indexCount = 0;
  		int meshIndex = -1;
  
  		for ( ; listIndex < count; listIndex++ )
  		{
  			const surfacesortgroup_t &group = *groupList[listIndex];
  			SurfaceHandle_t surfID = sortList.GetSurfaceAtHead(group);
  			Assert( IS_SURF_VALID( surfID ) );
  			if ( MSurf_Flags(surfID) & SURFDRAW_DYNAMIC )
  			{
  				dynamicGroups.AddToTail( &group );
  				continue;
  			}
  
  			Assert( group.triangleCount > 0 );
  			int numIndex = group.triangleCount * 3;
  			if ( indexCount + numIndex > nMaxIndices )
  			{
  				if ( numIndex > nMaxIndices )
  				{
  					DevMsg("Too many faces with the same material in scene!\n");
  					break;
  				}
  
  #ifdef NEWMESH
  				pLastVertexBuffer = NULL;
  #else
  				pLastMesh = NULL;
  #endif
  				break;
  			}
  			
  			int sortID = MSurf_MaterialSortID( surfID );
  
  #ifdef NEWMESH
  			if ( g_WorldStaticMeshes[sortID] != pLastVertexBuffer )
  #else
  			if ( g_WorldStaticMeshes[sortID] != pLastMesh )
  #endif
  			{
  				if( meshList.Count() < MAX_VERTEX_FORMAT_CHANGES - 1 )
  				{
  					meshIndex = meshList.AddToTail();
  					meshList[meshIndex].numbatches = 0;
  					meshList[meshIndex].firstbatch = batchList.Count();
  #ifdef NEWMESH
  					pLastVertexBuffer = g_WorldStaticMeshes[sortID];
  					Assert( pLastVertexBuffer );
  					meshList[meshIndex].pVertexBuffer = pLastVertexBuffer;
  #else
  					pLastMesh = g_WorldStaticMeshes[sortID];
  					Assert( pLastMesh );
  					meshList[meshIndex].pMesh = pLastMesh;
  #endif
  				}
  				else
  				{
  					if ( bWarn )
  					{
  						Warning( "Too many vertex format changes in frame, whole world not rendered\n" );
  						bWarn = false;
  					}
  					continue;
  				}
  			}
  
  			int batchIndex = batchList.AddToTail();
  			batchlist_t &batch = batchList[batchIndex];
  			batch.firstIndex = indexCount;
  			batch.surfID = surfID;
  			batch.numIndex = numIndex;
  			Assert( indexCount + batch.numIndex < nMaxIndices );
  			indexCount += batch.numIndex;
  
  			meshList[meshIndex].numbatches++;
  
  			MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(sortList, group, surfIDList)
  			{
  				tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "BuildIndicesForWorldSurface" );
  #ifdef NEWMESH
  				BuildIndicesForWorldSurface( indexBufferBuilder, surfIDList, host_state.worldbrush );
  #else
  				Assert( meshBuilder.m_nFirstVertex == 0 );
  				BuildIndicesForWorldSurface( meshBuilder, surfIDList, host_state.worldbrush );
  #endif
  			}
  			MSL_FOREACH_SURFACE_IN_GROUP_END()
  		}
  
  		// close out the index buffer
  #ifdef NEWMESH
  		indexBufferBuilder.End( false ); // this one matches (world rendering)
  #else
  		meshBuilder.End( false, false );
  #endif
  
  		int meshTotal = meshList.Count();
  		VPROF_INCREMENT_COUNTER( "vertex format changes", meshTotal );
  
  		// HACKHACK: Crappy little bubble sort
  		// UNDONE: Make the traversal happen so that they are already sorted when you get here.
  		// NOTE: Profiled in a fairly complex map.  This is not even costing 0.01ms / frame!
  		for ( i = 0; i < meshTotal; i++ )
  		{
  			meshMap[i] = i;
  		}
  
  		bool swapped = true;
  		while ( swapped )
  		{
  			swapped = false;
  			for ( i = 1; i < meshTotal; i++ )
  			{
  #ifdef NEWMESH
  				if ( meshList[meshMap[i]].pVertexBuffer < meshList[meshMap[i-1]].pVertexBuffer )
  #else
  				if ( meshList[meshMap[i]].pMesh < meshList[meshMap[i-1]].pMesh )
  #endif
  				{
  					int tmp = meshMap[i-1];
  					meshMap[i-1] = meshMap[i];
  					meshMap[i] = tmp;
  					swapped = true;
  				}
  			}
  		}
  
  #ifndef NEWMESH
  		pRenderContext->BeginBatch( pBuildMesh );
  #endif
  		for ( int m = 0; m < meshTotal; m++ )
  		{
  			vertexformatlist_t &mesh = meshList[meshMap[m]];
  			IMaterial *pBindMaterial = materialSortInfoArray[MSurf_MaterialSortID( batchList[mesh.firstbatch].surfID )].material;
  #ifdef NEWMESH
  			Assert( mesh.pVertexBuffer && pBuildIndexBuffer );
  #else
  			Assert( mesh.pMesh && pBuildMesh );
  #endif
  #ifdef NEWMESH
  			IIndexBuffer *pIndexBuffer = pRenderContext->GetDynamicIndexBuffer( MATERIAL_INDEX_FORMAT_16BIT, false );
  #else
  //			IMesh *pMesh = pRenderContext->GetDynamicMesh( false, mesh.pMesh, pBuildMesh, pBindMaterial );
  			pRenderContext->BindBatch( mesh.pMesh, pBindMaterial );
  #endif
  
  			for ( int b = 0; b < mesh.numbatches; b++ )
  			{
  				batchlist_t &batch = batchList[b+mesh.firstbatch];
  				IMaterial *pDrawMaterial = materialSortInfoArray[MSurf_MaterialSortID( batch.surfID )].material;
  
  				if ( bShadowDepth )
  				{
  					// Select proper override material
  					int nAlphaTest = (int) pDrawMaterial->IsAlphaTested();
  					int nNoCull = (int) pDrawMaterial->IsTwoSided();
  					IMaterial *pDepthWriteMaterial = g_pMaterialDepthWrite[nAlphaTest][nNoCull];
  
  					if ( nAlphaTest == 1 )
  					{
  						static unsigned int originalTextureVarCache = 0;
  						IMaterialVar *pOriginalTextureVar = pDrawMaterial->FindVarFast( "$basetexture", &originalTextureVarCache );
  						static unsigned int originalTextureFrameVarCache = 0;
  						IMaterialVar *pOriginalTextureFrameVar = pDrawMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache );
  						static unsigned int originalAlphaRefCache = 0;
  						IMaterialVar *pOriginalAlphaRefVar = pDrawMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
  
  						static unsigned int textureVarCache = 0;
  						IMaterialVar *pTextureVar = pDepthWriteMaterial->FindVarFast( "$basetexture", &textureVarCache );
  						static unsigned int textureFrameVarCache = 0;
  						IMaterialVar *pTextureFrameVar = pDepthWriteMaterial->FindVarFast( "$frame", &textureFrameVarCache );
  						static unsigned int alphaRefCache = 0;
  						IMaterialVar *pAlphaRefVar = pDepthWriteMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
  
  						if( pTextureVar && pOriginalTextureVar )
  						{
  							pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
  						}
  
  						if( pTextureFrameVar && pOriginalTextureFrameVar )
  						{
  							pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
  						}
  
  						if( pAlphaRefVar && pOriginalAlphaRefVar )
  						{
  							pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
  						}
  					}
  
  					pRenderContext->Bind( pDepthWriteMaterial );
  				}
  				else
  				{
  					pRenderContext->Bind( pDrawMaterial, NULL );
  						
  					if ( skipBind )
  					{
  						if( MSurf_Flags( batch.surfID ) & SURFDRAW_BUMPLIGHT )
  						{
  							pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  						}
  						else
  						{
  							pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE );
  						}
  					}
  					else
  					{
  						pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( batch.surfID )].lightmapPageID );
  					}
  				}
  #ifdef NEWMESH
  				// FIXME: IMaterial::GetVertexFormat() should do this stripping (add a separate 'SupportsCompression' accessor)
  				VertexFormat_t vertexFormat = pBindMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
  				pRenderContext->BindVertexBuffer( 0, mesh.pVertexBuffer, 0, vertexFormat );
  				pRenderContext->BindIndexBuffer( pIndexBuffer, 0 );
  				Warning( "pRenderContext->Draw( MATERIAL_TRIANGLES, batch.firstIndex = %d, batch.numIndex = %d )\n", 
  					( int )batch.firstIndex, ( int )batch.numIndex );
  				pRenderContext->Draw( MATERIAL_TRIANGLES, batch.firstIndex, batch.numIndex );
  #else
  //				pMesh->Draw( batch.firstIndex, batch.numIndex );
  				pRenderContext->DrawBatch( batch.firstIndex, batch.numIndex );
  #endif
  			}
  		}
  #ifndef NEWMESH
  		pRenderContext->EndBatch();
  #endif
  
  
  		// if we get here and pLast mesh is NULL and we rendered somthing, we need to loop
  #ifdef NEWMESH
  		if ( pLastVertexBuffer || !meshTotal )
  #else
  		if ( pLastMesh || !meshTotal )
  #endif
  			break;
  
  		meshList.RemoveAll();
  		batchList.RemoveAll();
  	}
  	for ( i = 0; i < dynamicGroups.Count(); i++ )
  	{
  		Shader_DrawDynamicChain( sortList, *dynamicGroups[i], bShadowDepth );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // The following methods will display debugging info in the middle of each surface
  //-----------------------------------------------------------------------------
  typedef void (*SurfaceDebugFunc_t)( SurfaceHandle_t surfID, const Vector &vecCentroid );
  
  void DrawSurfaceID( SurfaceHandle_t surfID, const Vector &vecCentroid )
  {
  	char buf[32];
  	Q_snprintf(buf, sizeof( buf ), "0x%p", surfID );
  	CDebugOverlay::AddTextOverlay( vecCentroid, 0, buf );
  }
  
  void DrawSurfaceIDAsInt( SurfaceHandle_t surfID, const Vector &vecCentroid )
  {
  	int nInt = (msurface2_t*)surfID - host_state.worldbrush->surfaces2;
  	char buf[32];
  	Q_snprintf( buf, sizeof( buf ), "%d", nInt );
  	CDebugOverlay::AddTextOverlay( vecCentroid, 0, buf );
  }
  
  void DrawSurfaceMaterial( SurfaceHandle_t surfID, const Vector &vecCentroid )
  {
  	mtexinfo_t * pTexInfo = MSurf_TexInfo(surfID);
  
  	const char *pFullMaterialName = pTexInfo->material ? pTexInfo->material->GetName() : "no material";
  	const char *pSlash = strrchr( pFullMaterialName, '/' );
  	const char *pMaterialName = strrchr( pFullMaterialName, '\\' );
  	if (pSlash > pMaterialName)
  		pMaterialName = pSlash;
  	if (pMaterialName)
  		++pMaterialName;
  	else
  		pMaterialName = pFullMaterialName;
  
  	CDebugOverlay::AddTextOverlay( vecCentroid, 0, pMaterialName );
  }
  
  
  //-----------------------------------------------------------------------------
  // Displays the surface id # in the center of the surface.
  //-----------------------------------------------------------------------------
  void Shader_DrawSurfaceDebuggingInfo( const CUtlVector<msurface2_t *> &surfaceList, SurfaceDebugFunc_t func )
  {
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		SurfaceHandle_t surfID = surfaceList[i];
  		Assert( !SurfaceHasDispInfo( surfID ) );
  
  		// Compute the centroid of the surface
  		int nCount = MSurf_VertCount( surfID );
  		if (nCount >= 3)
  		{
  			Vector vecCentroid;
  			Surf_ComputeCentroid( surfID, &vecCentroid );
  			func( surfID, vecCentroid );
  		}
  	}
  }
  
  
  
  //-----------------------------------------------------------------------------
  // Doesn't draw internal triangles
  //-----------------------------------------------------------------------------
  void Shader_DrawWireframePolygons( const CUtlVector<msurface2_t *> &surfaceList )
  {
  	int nLineCount = 0;
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		int nCount = MSurf_VertCount( surfaceList[i] );
  		if (nCount >= 3)
  		{
  			nLineCount += nCount;
  		}
  	}
  
  	if (nLineCount == 0)
  		return;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	pRenderContext->Bind( g_materialWorldWireframe );
  	IMesh *pMesh = pRenderContext->GetDynamicMesh( false );
  	CMeshBuilder meshBuilder;
  	meshBuilder.Begin( pMesh, MATERIAL_LINES, nLineCount );
  
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		SurfaceHandle_t surfID = surfaceList[i];
  		Assert( !SurfaceHasDispInfo( surfID ) );
  
  		// Compute the centroid of the surface
  		int nCount = MSurf_VertCount( surfID );
  		if (nCount >= 3)
  		{
  			int nFirstVertIndex = MSurf_FirstVertIndex( surfID );
  			int nVertIndex = host_state.worldbrush->vertindices[nFirstVertIndex + nCount - 1];
  			Vector vecPrevPos = host_state.worldbrush->vertexes[nVertIndex].position;
  			for (int v = 0; v < nCount; ++v )
  			{
  				// world-space vertex
  				nVertIndex = host_state.worldbrush->vertindices[nFirstVertIndex + v];
  				Vector& vec = host_state.worldbrush->vertexes[nVertIndex].position;
  
  				// output to mesh
  				meshBuilder.Position3fv( vecPrevPos.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( vec.Base() );
  				meshBuilder.AdvanceVertex();
  
  				vecPrevPos = vec;
  			}
  		}
  	}
  
  	meshBuilder.End();
  	pMesh->Draw();
  }
  
  
  //-----------------------------------------------------------------------------
  // Debugging mode, renders the wireframe.
  //-----------------------------------------------------------------------------
  static void Shader_DrawChainsWireframe(	const CUtlVector<msurface2_t *> &surfaceList )
  {
  	int nWireFrameMode = WireFrameMode();
  
  	switch( nWireFrameMode )
  	{
  	case 3:
  		// Doesn't draw internal triangles
  		Shader_DrawWireframePolygons(surfaceList);
  		break;
  
  	default:
  		{
  			CMatRenderContextPtr pRenderContext( materials );
  			if( nWireFrameMode == 2 )
  			{
  				pRenderContext->Bind( g_materialWorldWireframeZBuffer );
  			}
  			else
  			{
  				pRenderContext->Bind( g_materialWorldWireframe );
  			}
  			for ( int i = 0; i < surfaceList.Count(); i++ )
  			{
  				SurfaceHandle_t surfID = surfaceList[i];
  				Assert( !SurfaceHasDispInfo( surfID ) );
  				Shader_DrawSurfaceDynamic( pRenderContext, surfID, false );
  			}
  		}
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Debugging mode, renders the normals
  //-----------------------------------------------------------------------------
  static void Shader_DrawChainNormals( const CUtlVector<msurface2_t *> &surfaceList )
  {
  	Vector p, tVect, tangentS, tangentT;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	worldbrushdata_t *pBrushData = host_state.worldbrush;
  	pRenderContext->Bind( g_pMaterialWireframeVertexColor );
  
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		SurfaceHandle_t surfID = surfaceList[i];
  		IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  		CMeshBuilder meshBuilder;
  		meshBuilder.Begin( pMesh, MATERIAL_LINES, MSurf_VertCount( surfID ) * 3 );
  	
  		bool negate = TangentSpaceSurfaceSetup( surfID, tVect );
  
  		int vertID;
  		for( vertID = 0; vertID < MSurf_VertCount( surfID ); ++vertID )
  		{
  			int vertIndex = pBrushData->vertindices[MSurf_FirstVertIndex( surfID )+vertID];
  			Vector& pos = pBrushData->vertexes[vertIndex].position;
  			Vector& norm = pBrushData->vertnormals[ pBrushData->vertnormalindices[MSurf_FirstVertNormal( surfID )+vertID] ];
  
  			TangentSpaceComputeBasis( tangentS, tangentT, norm, tVect, negate );
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 0, 0, 255 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, norm, p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 0, 0, 255 );
  			meshBuilder.AdvanceVertex();
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 0, 255, 0 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, tangentT, p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 0, 255, 0 );
  			meshBuilder.AdvanceVertex();
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 255, 0, 0 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, tangentS, p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 255, 0, 0 );
  			meshBuilder.AdvanceVertex();
  		}
  		
  		meshBuilder.End();
  		pMesh->Draw();
  	}
  }
  
  static void Shader_DrawChainBumpBasis( const CUtlVector<msurface2_t *> &surfaceList )
  {
  	Vector p, tVect, tangentS, tangentT;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	worldbrushdata_t *pBrushData = host_state.worldbrush;
  	pRenderContext->Bind( g_pMaterialWireframeVertexColor );
  
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		SurfaceHandle_t surfID = surfaceList[i];
  		IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  		CMeshBuilder meshBuilder;
  		meshBuilder.Begin( pMesh, MATERIAL_LINES, MSurf_VertCount( surfID ) * 3 );
  	
  		bool negate = TangentSpaceSurfaceSetup( surfID, tVect );
  
  		int vertID;
  		for( vertID = 0; vertID < MSurf_VertCount( surfID ); ++vertID )
  		{
  			int vertIndex = pBrushData->vertindices[MSurf_FirstVertIndex( surfID )+vertID];
  			Vector& pos = pBrushData->vertexes[vertIndex].position;
  			Vector& norm = pBrushData->vertnormals[ pBrushData->vertnormalindices[MSurf_FirstVertNormal( surfID )+vertID] ];
  
  			TangentSpaceComputeBasis( tangentS, tangentT, norm, tVect, negate );
  
  			Vector worldSpaceBumpBasis[3];
  
  			for( int j = 0; j < 3; j++ )
  			{
  				worldSpaceBumpBasis[j][0] = 
  					g_localBumpBasis[j][0] * tangentS[0] +
  					g_localBumpBasis[j][1] * tangentS[1] + 
  					g_localBumpBasis[j][2] * tangentS[2];
  				worldSpaceBumpBasis[j][1] = 
  					g_localBumpBasis[j][0] * tangentT[0] +
  					g_localBumpBasis[j][1] * tangentT[1] + 
  					g_localBumpBasis[j][2] * tangentT[2];
  				worldSpaceBumpBasis[j][2] = 
  					g_localBumpBasis[j][0] * norm[0] +
  					g_localBumpBasis[j][1] * norm[1] + 
  					g_localBumpBasis[j][2] * norm[2];
  			}
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 255, 0, 0 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, worldSpaceBumpBasis[0], p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 255, 0, 0 );
  			meshBuilder.AdvanceVertex();
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 0, 255, 0 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, worldSpaceBumpBasis[1], p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 0, 255, 0 );
  			meshBuilder.AdvanceVertex();
  
  			meshBuilder.Position3fv( pos.Base() );
  			meshBuilder.Color3ub( 0, 0, 255 );
  			meshBuilder.AdvanceVertex();
  
  			VectorMA( pos, 5.0f, worldSpaceBumpBasis[2], p );
  			meshBuilder.Position3fv( p.Base() );
  			meshBuilder.Color3ub( 0, 0, 255 );
  			meshBuilder.AdvanceVertex();
  		}
  		
  		meshBuilder.End();
  		pMesh->Draw();
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Debugging mode, renders the luxel grid.
  //-----------------------------------------------------------------------------
  static void Shader_DrawLuxels( const CUtlVector<msurface2_t *> &surfaceList )
  {
  	CMatRenderContextPtr pRenderContext( materials );
  
  	pRenderContext->Bind( g_materialDebugLuxels );
  
  	for ( int i = 0; i < surfaceList.Count(); i++ )
  	{
  		SurfaceHandle_t surfID = surfaceList[i];
  		Assert( !SurfaceHasDispInfo( surfID ) );
  
  		// Gotta bind the lightmap page so the rendering knows the lightmap scale
  		pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID );
  		Shader_DrawSurfaceDynamic( pRenderContext, surfID, false );
  	}
  }
  
  static struct CShaderDebug
  {
  	bool		wireframe;
  	bool		normals;
  	bool		luxels;
  	bool		bumpBasis;
  	bool		surfacematerials;
  	bool		anydebug;
  	int			surfaceid;
  
  	void TestAnyDebug()
  	{
  		anydebug = wireframe || normals || luxels || bumpBasis || ( surfaceid != 0 ) || surfacematerials;
  	}
  
  } g_ShaderDebug;
  
  
  ConVar mat_surfaceid("mat_surfaceid", "0", FCVAR_CHEAT);
  ConVar mat_surfacemat("mat_surfacemat", "0", FCVAR_CHEAT);
  
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  // Output : static void
  //-----------------------------------------------------------------------------
  static void ComputeDebugSettings( void )
  {
  	g_ShaderDebug.wireframe = ShouldDrawInWireFrameMode() || (r_drawworld.GetInt() == 2);
  	g_ShaderDebug.normals = mat_normals.GetBool();
  	g_ShaderDebug.luxels = mat_luxels.GetBool();
  	g_ShaderDebug.bumpBasis = mat_bumpbasis.GetBool();
  	g_ShaderDebug.surfaceid = mat_surfaceid.GetInt();
  	g_ShaderDebug.surfacematerials = mat_surfacemat.GetBool();
  	g_ShaderDebug.TestAnyDebug();
  }
  
  //-----------------------------------------------------------------------------
  // Draw debugging information
  //-----------------------------------------------------------------------------
  static void DrawDebugInformation( const CUtlVector<msurface2_t *> &surfaceList )
  {
  	// Overlay with wireframe if we're in that mode
  	if( g_ShaderDebug.wireframe )
  	{
  		Shader_DrawChainsWireframe(surfaceList);
  	}
  
  	// Overlay with normals if we're in that mode
  	if( g_ShaderDebug.normals )
  	{
  		Shader_DrawChainNormals(surfaceList);
  	}
  
  	if( g_ShaderDebug.bumpBasis )
  	{
  		Shader_DrawChainBumpBasis(surfaceList);
  	}
  	
  	// Overlay with luxel grid if we're in that mode
  	if( g_ShaderDebug.luxels )
  	{
  		Shader_DrawLuxels(surfaceList);
  	}
  
  	if ( g_ShaderDebug.surfaceid )
  	{
  		// Draw the surface id in the middle of the surfaces
  		Shader_DrawSurfaceDebuggingInfo( surfaceList, (g_ShaderDebug.surfaceid != 2 ) ? DrawSurfaceID : DrawSurfaceIDAsInt );
  	}
  	else if ( g_ShaderDebug.surfacematerials )
  	{
  		// Draw the material name in the middle of the surfaces
  		Shader_DrawSurfaceDebuggingInfo( surfaceList, DrawSurfaceMaterial );
  	}
  }
  
  
  void AddProjectedTextureDecalsToList( CWorldRenderList *pRenderList, int nSortGroup )
  {
  	const CMSurfaceSortList &sortList = pRenderList->m_SortList;
  	MSL_FOREACH_GROUP_BEGIN( sortList, nSortGroup, group )
  	{
  		MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(sortList, group, surfID)
  		{
  			Assert( !SurfaceHasDispInfo( surfID ) );
  			if ( SHADOW_DECAL_HANDLE_INVALID != MSurf_ShadowDecals( surfID ) )
  			{
  				// No shadows on water surfaces
  				if ((MSurf_Flags( surfID ) & SURFDRAW_NOSHADOWS) == 0)
  				{
  					MEM_ALLOC_CREDIT();
  					pRenderList->m_ShadowHandles[nSortGroup].AddToTail( MSurf_ShadowDecals( surfID ) );
  				}
  			}
  			// Add overlay fragments to list.
  			if ( OVERLAY_FRAGMENT_INVALID != MSurf_OverlayFragmentList( surfID ) )
  			{
  				OverlayMgr()->AddFragmentListToRenderList( nSortGroup, MSurf_OverlayFragmentList( surfID ), false );
  			}
  		}
  		MSL_FOREACH_SURFACE_IN_GROUP_END();
  	}
  	MSL_FOREACH_GROUP_END()
  }
  
  //-----------------------------------------------------------------------------
  // Draws all of the opaque non-displacement surfaces queued up previously
  //-----------------------------------------------------------------------------
  void Shader_DrawChains( const CWorldRenderList *pRenderList, int nSortGroup, bool bShadowDepth )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	CMatRenderContextPtr pRenderContext(materials);
  	Assert( !g_EngineRenderer->InLightmapUpdate() );
  	VPROF("Shader_DrawChains");
  	// Draw chains...
  #ifdef USE_CONVARS
  	if ( !mat_forcedynamic.GetInt() && !g_pMaterialSystemConfig->bDrawFlat )
  #else
  	if( 1 )
  #endif
  	{
  		if ( g_VBAllocTracker )
  			g_VBAllocTracker->TrackMeshAllocations( "Shader_DrawChainsStatic" );
  		Shader_DrawChainsStatic( pRenderList->m_SortList, nSortGroup, bShadowDepth );
  	}
  	else
  	{
  		if ( g_VBAllocTracker )
  			g_VBAllocTracker->TrackMeshAllocations( "Shader_DrawChainsDynamic" );
  		Shader_DrawChainsDynamic( pRenderList->m_SortList, nSortGroup, bShadowDepth );
  	}
  	if ( g_VBAllocTracker )
  		g_VBAllocTracker->TrackMeshAllocations( NULL );
  
  #if MOVE_DLIGHTS_TO_NEW_TEXTURE
  	for ( int i = 0; i < pRenderList->m_DlightSurfaces[nSortGroup].Count(); i++ )
  	{
  		SurfaceHandle_t surfID = pRenderList->m_DlightSurfaces[nSortGroup][i];
  		if ( !SurfaceHasDispInfo( surfID ) && (MSurf_Flags(surfID) & SURFDRAW_DLIGHTPASS) )
  		{
  			pRenderContext->Bind( MSurf_TexInfo( surfID )->material, NULL );
  			Shader_SetChainLightmapState( pRenderContext, surfID );
  			Shader_DrawSurfaceDynamic( pRenderContext, surfID, bShadowDepth );
  		}
  	}
  #endif
  
  	if ( bShadowDepth )	// Skip debug stuff in shadow depth map
  		return;
  
  #ifdef USE_CONVARS
  	if ( g_ShaderDebug.anydebug )
  	{
  		const CMSurfaceSortList &sortList = pRenderList->m_SortList;
  		// Debugging information
  		MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  		{
  			CUtlVector<msurface2_t *> surfList;
  			sortList.GetSurfaceListForGroup( surfList, group );
  			DrawDebugInformation( surfList );
  		}
  		MSL_FOREACH_GROUP_END()
  	}
  #endif
  }
  
  
  //-----------------------------------------------------------------------------
  // Draws all of the opaque displacement surfaces queued up previously
  //-----------------------------------------------------------------------------
  void Shader_DrawDispChain( int nSortGroup, const CMSurfaceSortList &list, unsigned long flags, ERenderDepthMode DepthMode )
  {
  	VPROF_BUDGET( "Shader_DrawDispChain", VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING );
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	int count = 0;
  	msurface2_t **pList;
  	MSL_FOREACH_GROUP_BEGIN( list, nSortGroup, group )
  	{
  		count += group.surfaceCount;
  	}
  	MSL_FOREACH_GROUP_END()
  
  	if (count)
  	{
  		pList = (msurface2_t **)stackalloc( count * sizeof(msurface2_t *));
  		int i = 0;
  		MSL_FOREACH_GROUP_BEGIN( list, nSortGroup, group )
  		{
  			MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(list,group,surfID)
  			{
  				pList[i] = surfID;
  				++i;
  			}
  			MSL_FOREACH_SURFACE_IN_GROUP_END()
  		}
  		MSL_FOREACH_GROUP_END()
  		Assert(i==count);
  
  		// draw displacments, batch decals
  		DispInfo_RenderList( nSortGroup, pList, count, g_EngineRenderer->ViewGetCurrent().m_bOrtho, flags, DepthMode );
  		stackfree(pList);
  	}
  }
  
  static void Shader_BuildDynamicLightmaps( CWorldRenderList *pRenderList )
  {
  	VPROF( "Shader_BuildDynamicLightmaps" );
  
  	R_DLightStartView();
  
  	// Build all lightmaps for opaque surfaces
  	for ( int nSortGroup = 0; nSortGroup < MAX_MAT_SORT_GROUPS; ++nSortGroup)
  	{
  #if 0
  		int updateStart = g_LightmapUpdateList.Count();
  #endif
  		for ( int i = pRenderList->m_DlightSurfaces[nSortGroup].Count()-1; i >= 0; --i )
  		{
  			LightmapUpdateInfo_t tmp;
  			tmp.m_SurfHandle = pRenderList->m_DlightSurfaces[nSortGroup].Element(i);
  			tmp.transformIndex = 0;
  			g_LightmapUpdateList.AddToTail( tmp );
  		}
  
  		// UNDONE: Redo this list?  Make a new list with the texture coord info for the new lightmaps?
  #if 0
  		pRenderList->m_DlightSurfaces[nSortGroup].RemoveAll();
  		for ( int i = updateStart; i < g_LightmapUpdateList.Count(); i++ )
  		{
  			if ( MSurf_Flags(g_LightmapUpdateList[i].m_SurfHandle) & SURFDRAW_DLIGHTPASS )
  			{
  				pRenderList->m_DlightSurfaces[nSortGroup].AddToTail(g_LightmapUpdateList[i].m_SurfHandle);
  			}
  		}
  #endif
  	}
  
  	R_DLightEndView();
  }
  
  
  //-----------------------------------------------------------------------------
  // Compute if we're in or out of a fog volume
  //-----------------------------------------------------------------------------
  static void ComputeFogVolumeInfo( FogVolumeInfo_t *pFogVolume )
  {
  	pFogVolume->m_InFogVolume = false;
  	int leafID = CM_PointLeafnum( CurrentViewOrigin() );
  	if( leafID < 0 || leafID >= host_state.worldbrush->numleafs )
  		return;
  
  	mleaf_t* pLeaf = &host_state.worldbrush->leafs[leafID];
  	pFogVolume->m_FogVolumeID = pLeaf->leafWaterDataID;
  	if( pFogVolume->m_FogVolumeID == -1 )
  		return;
  
  	pFogVolume->m_InFogVolume = true;
  
  	mleafwaterdata_t* pLeafWaterData = &host_state.worldbrush->leafwaterdata[pLeaf->leafWaterDataID];
  	if( pLeafWaterData->surfaceTexInfoID == -1 )
  	{
  		// Should this ever happen?????
  		pFogVolume->m_FogEnabled = false;
  		return;
  	}
  	mtexinfo_t* pTexInfo = &host_state.worldbrush->texinfo[pLeafWaterData->surfaceTexInfoID];
  
  	IMaterial* pMaterial = pTexInfo->material;
  	if( pMaterial )
  	{
  		IMaterialVar* pFogColorVar	= pMaterial->FindVar( "$fogcolor", NULL );
  		IMaterialVar* pFogEnableVar = pMaterial->FindVar( "$fogenable", NULL );
  		IMaterialVar* pFogStartVar	= pMaterial->FindVar( "$fogstart", NULL );
  		IMaterialVar* pFogEndVar	= pMaterial->FindVar( "$fogend", NULL );
  
  		pFogVolume->m_FogEnabled = pFogEnableVar->GetIntValue() ? true : false;
  		pFogColorVar->GetVecValue( pFogVolume->m_FogColor, 3 );
  		pFogVolume->m_FogStart		= -pFogStartVar->GetFloatValue();
  		pFogVolume->m_FogEnd		= -pFogEndVar->GetFloatValue();
  		pFogVolume->m_FogSurfaceZ	= pLeafWaterData->surfaceZ;
  		pFogVolume->m_FogMinZ		= pLeafWaterData->minZ;
  		pFogVolume->m_FogMode		= MATERIAL_FOG_LINEAR;
  	}
  	else
  	{
  		static bool bComplained = false;
  		if( !bComplained )
  		{
  			Warning( "***Water vmt missing . . check console for missing materials!***\n" );
  			bComplained = true;
  		}
  		pFogVolume->m_FogEnabled = false;
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Resets a world render list
  //-----------------------------------------------------------------------------
  void ResetWorldRenderList( CWorldRenderList *pRenderList )
  {
  	if ( pRenderList )
  	{
  		pRenderList->Reset();
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Call this before rendering; it clears out the lists of stuff to render
  //-----------------------------------------------------------------------------
  void Shader_WorldBegin( CWorldRenderList *pRenderList )
  {
  	// Cache the convars so we don't keep accessing them...
  	s_ShaderConvars.m_bDrawWorld = r_drawworld.GetBool();
  	s_ShaderConvars.m_nDrawLeaf = r_drawleaf.GetInt();
  	s_ShaderConvars.m_bDrawFuncDetail = r_drawfuncdetail.GetBool();
  
  	ResetWorldRenderList( pRenderList );
  
  	// Clear out the decal list
  	DecalSurfacesInit( false );
  
  	// Clear out the render lists of overlays
  	OverlayMgr()->ClearRenderLists();
  
  	// Clear out the render lists of shadows
  	g_pShadowMgr->ClearShadowRenderList( );
  }
  
  
  
  //-----------------------------------------------------------------------------
  // Performs the z-fill
  //-----------------------------------------------------------------------------
  static void Shader_WorldZFillSurfChain( const CMSurfaceSortList &sortList, const surfacesortgroup_t &group, CMeshBuilder &meshBuilder, int &nStartVertIn, unsigned int includeFlags )
  {
  	int nStartVert = nStartVertIn;
  	mvertex_t *pWorldVerts = host_state.worldbrush->vertexes;
  
  	MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(sortList, group, nSurfID)
  	{
  		if ( (MSurf_Flags( nSurfID ) & includeFlags) == 0 )
  			continue;
  
  		// Skip water surfaces since it may move up or down to fixup water transitions.
  		if ( MSurf_Flags( nSurfID ) & SURFDRAW_WATERSURFACE )
  			continue;
  
  		int nSurfTriangleCount = MSurf_VertCount( nSurfID ) - 2;
  
  		unsigned short *pVertIndex = &(host_state.worldbrush->vertindices[MSurf_FirstVertIndex( nSurfID )]);
  
  		// add surface to this batch
  		if ( SurfaceHasPrims(nSurfID) )
  		{
  			mprimitive_t *pPrim = &host_state.worldbrush->primitives[MSurf_FirstPrimID( nSurfID )];
  			if ( pPrim->vertCount == 0 )
  			{
  				int firstVert = MSurf_FirstVertIndex( nSurfID );
  				for ( int i = 0; i < MSurf_VertCount(nSurfID); i++ )
  				{
  					int vertIndex = host_state.worldbrush->vertindices[firstVert + i];
  					meshBuilder.Position3fv( pWorldVerts[vertIndex].position.Base() );
  					meshBuilder.AdvanceVertex();
  				}
  				for ( int primIndex = 0; primIndex < pPrim->indexCount; primIndex++ )
  				{
  					meshBuilder.FastIndex( host_state.worldbrush->primindices[pPrim->firstIndex + primIndex] + nStartVert );
  				}
  			}
  		}
  		else
  		{
  			switch (nSurfTriangleCount)
  			{
  			case 1:
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  
  				meshBuilder.FastIndex( nStartVert );
  				meshBuilder.FastIndex( nStartVert + 1 );
  				meshBuilder.FastIndex( nStartVert + 2 );
  
  				break;
  
  			case 2:
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  				meshBuilder.AdvanceVertex();
  				meshBuilder.FastIndex( nStartVert );
  				meshBuilder.FastIndex( nStartVert + 1 );
  				meshBuilder.FastIndex( nStartVert + 2 );
  				meshBuilder.FastIndex( nStartVert );
  				meshBuilder.FastIndex( nStartVert + 2 );
  				meshBuilder.FastIndex( nStartVert + 3 );
  				break;
  
  			default:
  				{
  					for ( unsigned short v = 0; v < nSurfTriangleCount; ++v )
  					{
  						meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  						meshBuilder.AdvanceVertex();
  
  						meshBuilder.FastIndex( nStartVert );
  						meshBuilder.FastIndex( nStartVert + v + 1 );
  						meshBuilder.FastIndex( nStartVert + v + 2 );
  					}
  
  					meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  					meshBuilder.AdvanceVertex();
  					meshBuilder.Position3fv( pWorldVerts[*pVertIndex++].position.Base() );
  					meshBuilder.AdvanceVertex();
  				}
  				break;
  			}
  		}
  		nStartVert += nSurfTriangleCount + 2;
  	}
  	MSL_FOREACH_SURFACE_IN_GROUP_END()
  
  	nStartVertIn = nStartVert;
  }
  
  static const int s_DrawWorldListsToSortGroup[MAX_MAT_SORT_GROUPS] = 
  {
  	MAT_SORT_GROUP_STRICTLY_ABOVEWATER,
  	MAT_SORT_GROUP_STRICTLY_UNDERWATER,
  	MAT_SORT_GROUP_INTERSECTS_WATER_SURFACE,
  	MAT_SORT_GROUP_WATERSURFACE,
  };
  
  static ConVar r_flashlightrendermodels(  "r_flashlightrendermodels", "1" );
  
  //-----------------------------------------------------------------------------
  // Performs the shadow depth texture fill
  //-----------------------------------------------------------------------------
  static void Shader_WorldShadowDepthFill( CWorldRenderList *pRenderList, unsigned long flags )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	// First, count the number of vertices + indices
  	int nVertexCount = 0;
  	int nIndexCount = 0;
  	ERenderDepthMode DepthMode = DEPTH_MODE_SHADOW;
  	if ( flags & DRAWWORLDLISTS_DRAW_SSAO )
  	{
  		DepthMode = DEPTH_MODE_SSA0;
  	}
  
  	int g;
  	CUtlVector<const surfacesortgroup_t *> alphatestedGroups;
  
  	const CMSurfaceSortList &sortList = pRenderList->m_SortList;
  	for ( g = 0; g < MAX_MAT_SORT_GROUPS; ++g )
  	{
  		if ( ( flags & ( 1 << g ) ) == 0 )
  			continue;
  
  		int nSortGroup = s_DrawWorldListsToSortGroup[g];
  		MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  		{
  			SurfaceHandle_t surfID = sortList.GetSurfaceAtHead(group);
  			if ( MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE )
  				continue;
  			IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
  
  			if( pMaterial->IsTranslucent() )
  				continue;
  
  			if ( pMaterial->IsAlphaTested() )
  			{
  				alphatestedGroups.AddToTail( &group );
  				continue;
  			}
  
  			nVertexCount += group.vertexCount;
  			nIndexCount += group.triangleCount*3;
  		}
  		MSL_FOREACH_GROUP_END()
  
  		// Draws opaque displacement surfaces along with shadows, overlays, flashlights, etc.
  		Shader_DrawDispChain( nSortGroup, pRenderList->m_DispSortList, flags, DepthMode );
  	}
  	if ( nVertexCount == 0 )
  		return;
   
  	CMatRenderContextPtr pRenderContext( materials );
  
  	if ( DepthMode == DEPTH_MODE_SHADOW )
  	{
  		pRenderContext->Bind( g_pMaterialDepthWrite[0][1] );
  	}
  	else
  	{
  		pRenderContext->Bind( g_pMaterialSSAODepthWrite[0][1] );
  	}
  
  	IMesh *pMesh = pRenderContext->GetDynamicMesh( false );
  
  	int nMaxIndices  = pRenderContext->GetMaxIndicesToRender();
  	int nMaxVertices = pRenderContext->GetMaxVerticesToRender( g_pMaterialDepthWrite[0][1] );	// opaque, nocull
  
  	// nBatchIndexCount and nBatchVertexCount are the number of indices and vertices we can fit in this batch
  	// Each batch must have fewer than nMaxIndices and nMaxVertices or the material system will fail
  	int nBatchIndexCount  = min( nIndexCount,  nMaxIndices  );
  	int nBatchVertexCount = min( nVertexCount, nMaxVertices );
  
  
  	CMeshBuilder meshBuilder;
  	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nBatchVertexCount, nBatchIndexCount );
  
  	int nStartVert = 0;
  	for ( g = 0; g < MAX_MAT_SORT_GROUPS; ++g )
  	{
  		if ( ( flags & ( 1 << g ) ) == 0 )
  			continue;
  
  		int nSortGroup = s_DrawWorldListsToSortGroup[g];
  		MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  		{
  			SurfaceHandle_t surfID = sortList.GetSurfaceAtHead(group);
  			// Check to see if we can add this list to the current batch...
  			int nCurrIndexCount = group.triangleCount*3;
  			int nCurrVertexCount = group.vertexCount;
  			if ( ( nCurrIndexCount == 0 ) || ( nCurrVertexCount == 0 ) )
  				continue;
  
  			// this group is too big to draw so push it into the alphatested groups
  			// this will run much slower but at least it won't crash
  			// alphatested groups will draw each surface one at a time.
  			if ( nCurrIndexCount > nMaxIndices || nCurrVertexCount > nMaxVertices )
  			{
  				alphatestedGroups.AddToTail( &group );
  				continue;
  			}
  			IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
  
  			// Opaque only on this loop
  			if( pMaterial->IsTranslucent() ||  pMaterial->IsAlphaTested() )
  				continue;
  
  			Assert( nCurrIndexCount  <= nMaxIndices  );
  			Assert( nCurrVertexCount <= nMaxVertices );
  
  			if ( ( nBatchIndexCount < nCurrIndexCount ) || ( nBatchVertexCount < nCurrVertexCount ) )
  			{
  				// Nope, fire off the current batch...
  				meshBuilder.End();
  				pMesh->Draw();
  				nBatchIndexCount  = min( nIndexCount,  nMaxIndices  );
  				nBatchVertexCount = min( nVertexCount, nMaxVertices );
  				pMesh = pRenderContext->GetDynamicMesh( false );
  				meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nBatchVertexCount, nBatchIndexCount );
  				nStartVert = 0;
  			}
  
  			nBatchIndexCount  -= nCurrIndexCount;
  			nIndexCount       -= nCurrIndexCount;
  			nBatchVertexCount -= nCurrVertexCount;
  			nVertexCount      -= nCurrVertexCount;
  			// 0xFFFFFFFF means include all surfaces
  			Shader_WorldZFillSurfChain( sortList, group, meshBuilder, nStartVert, 0xFFFFFFFF );
  		}
  		MSL_FOREACH_GROUP_END()
  	}
  
  	meshBuilder.End();
  	pMesh->Draw();
  
  	// Now draw the alpha-tested groups we stored away earlier
  	for ( int i = 0; i < alphatestedGroups.Count(); i++ )
  	{
  		Shader_DrawDynamicChain( sortList, *alphatestedGroups[i], true );
  	}
  }
  
  
  
  //-----------------------------------------------------------------------------
  // Performs the z-fill
  //-----------------------------------------------------------------------------
  static void Shader_WorldZFill( CWorldRenderList *pRenderList, unsigned long flags )
  {
  	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  
  	// First, count the number of vertices + indices
  	int nVertexCount = 0;
  	int nIndexCount = 0;
  
  	int g;
  	const CMSurfaceSortList &sortList = pRenderList->m_SortList;
  
  #ifdef _X360
  	bool bFastZRejectDisplacements = s_bFastZRejectDisplacements || ( r_fastzrejectdisp.GetInt() != 0 );
  #endif
  
  	for ( g = 0; g < MAX_MAT_SORT_GROUPS; ++g )
  	{
  		if ( ( flags & ( 1 << g ) ) == 0 )
  			continue;
  
  		int nSortGroup = s_DrawWorldListsToSortGroup[g];
  		MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  		{
  			SurfaceHandle_t surfID = sortList.GetSurfaceAtHead(group);
  			IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
  			if( pMaterial->IsAlphaTested() || pMaterial->IsTranslucent() )
  			{
  				continue;
  			}
  			nVertexCount += group.vertexCountNoDetail;
  			nIndexCount += group.indexCountNoDetail;
  		}
  		MSL_FOREACH_GROUP_END()
  
  #ifdef _X360
  		// Draws opaque displacement surfaces along with shadows, overlays, flashlights, etc.
  		// NOTE: This only makes sense on the 360, since the extra batches aren't
  		// worth it on the PC (I think!)
  		if ( bFastZRejectDisplacements )
  		{
  			Shader_DrawDispChain( nSortGroup, pRenderList->m_DispSortList, flags, true );
  		}
  #endif
  	}
  
  	if ( nVertexCount == 0 )
  		return;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	pRenderContext->Bind( g_pMaterialWriteZ );
  	IMesh *pMesh = pRenderContext->GetDynamicMesh( false );
  
  	int nMaxIndices  = pRenderContext->GetMaxIndicesToRender();
  	int nMaxVertices = pRenderContext->GetMaxVerticesToRender( g_pMaterialWriteZ );
  
  	// nBatchIndexCount and nBatchVertexCount are the number of indices and vertices we can fit in this batch
  	// Each batch must have fewe than nMaxIndices and nMaxVertices or the material system will fail
  	int nBatchIndexCount  = min( nIndexCount,  nMaxIndices  );
  	int nBatchVertexCount = min( nVertexCount, nMaxVertices );
  
  	CMeshBuilder meshBuilder;
  	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nBatchVertexCount, nBatchIndexCount );
  
  	int nStartVert = 0;
  	for ( g = 0; g < MAX_MAT_SORT_GROUPS; ++g )
  	{
  		if ( ( flags & ( 1 << g ) ) == 0 )
  			continue;
  
  		int nSortGroup = s_DrawWorldListsToSortGroup[g];
  		MSL_FOREACH_GROUP_BEGIN(sortList, nSortGroup, group )
  		{
  			SurfaceHandle_t surfID = sortList.GetSurfaceAtHead(group);
  
  			// Check to see if we can add this list to the current batch...
  			int nCurrIndexCount = group.indexCountNoDetail;
  			int nCurrVertexCount = group.vertexCountNoDetail;
  			if ( ( nCurrIndexCount == 0 ) || ( nCurrVertexCount == 0 ) )
  				continue;
  
  			IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
  
  			if( pMaterial->IsAlphaTested() || pMaterial->IsTranslucent() )
  				continue;
  
  			Assert( nCurrIndexCount  <= nMaxIndices  );
  			Assert( nCurrVertexCount <= nMaxVertices );
  
  			if ( ( nBatchIndexCount < nCurrIndexCount ) || ( nBatchVertexCount < nCurrVertexCount ) )
  			{
  				// Nope, fire off the current batch...
  				meshBuilder.End();
  				pMesh->Draw();
  				nBatchIndexCount  = min( nIndexCount,  nMaxIndices  );
  				nBatchVertexCount = min( nVertexCount, nMaxVertices );
  				pMesh = pRenderContext->GetDynamicMesh( false );
  				meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nBatchVertexCount, nBatchIndexCount );
  				nStartVert = 0;
  			}
  
  			nBatchIndexCount  -= nCurrIndexCount;
  			nIndexCount       -= nCurrIndexCount;
  			nBatchVertexCount -= nCurrVertexCount;
  			nVertexCount      -= nCurrVertexCount;
  
  			// only draw surfaces on nodes (i.e. no detail surfaces)
  			Shader_WorldZFillSurfChain( sortList, group, meshBuilder, nStartVert, SURFDRAW_NODE );
  		}
  		MSL_FOREACH_GROUP_END()
  	}
  
  	meshBuilder.End();
  	pMesh->Draw();
  
  	// FIXME: Do fast z reject on displacements!
  }
  
  //-----------------------------------------------------------------------------
  // Call this after lists of stuff to render are made; it renders opaque surfaces
  //-----------------------------------------------------------------------------
  static void Shader_WorldEnd( CWorldRenderList *pRenderList, unsigned long flags, float waterZAdjust )
  {
  	VPROF("Shader_WorldEnd");
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	if ( flags & ( DRAWWORLDLISTS_DRAW_SHADOWDEPTH | DRAWWORLDLISTS_DRAW_SSAO ) )
  	{
  		Shader_WorldShadowDepthFill( pRenderList, flags );
  		return;
  	}
  
  	// Draw the skybox
  	if ( flags & DRAWWORLDLISTS_DRAW_SKYBOX )
  	{
  		if ( pRenderList->m_bSkyVisible || Map_VisForceFullSky() )
  		{
  			if( flags & DRAWWORLDLISTS_DRAW_CLIPSKYBOX )
  			{
  				R_DrawSkyBox( g_EngineRenderer->GetZFar() );
  			}
  			else
  			{
  				// Don't clip the skybox with height clip in this path.
  				MaterialHeightClipMode_t nClipMode = pRenderContext->GetHeightClipMode();
  				pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
  				R_DrawSkyBox( g_EngineRenderer->GetZFar() );
  				pRenderContext->SetHeightClipMode( nClipMode );
  			}
  		}
  	}
  
  	// Perform the fast z-fill pass
  	bool bFastZReject = (r_fastzreject.GetInt() != 0);
  	if ( bFastZReject )
  	{
  		Shader_WorldZFill( pRenderList, flags );
  	}
  	
  	// Gotta draw each sort group
  	// Draw the fog volume first, if there is one, because it turns out
  	// that we only draw fog volumes if we're in the fog volume, which
  	// means it's closer. We want to render closer things first to get
  	// fast z-reject.
  	int i;
  	for ( i = MAX_MAT_SORT_GROUPS; --i >= 0; )
  	{
  		if ( !( flags & ( 1 << i ) ) )
  			continue;
  
  		int nSortGroup = s_DrawWorldListsToSortGroup[i];
  		if ( nSortGroup == MAT_SORT_GROUP_WATERSURFACE  )
  		{
  			if ( waterZAdjust != 0.0f )
  			{
  				pRenderContext->MatrixMode( MATERIAL_MODEL );
  				pRenderContext->PushMatrix();
  				pRenderContext->LoadIdentity();
  				pRenderContext->Translate( 0.0f, 0.0f, waterZAdjust );
  			}
  		}
  
  		// Draws opaque displacement surfaces along with shadows, overlays, flashlights, etc.
  		Shader_DrawDispChain( nSortGroup, pRenderList->m_DispSortList, flags, DEPTH_MODE_NORMAL );
  
  		// Draws opaque non-displacement surfaces
  		// This also add shadows to pRenderList->m_ShadowHandles.
  		Shader_DrawChains( pRenderList, nSortGroup, false );
  		AddProjectedTextureDecalsToList( pRenderList, nSortGroup );
  
  		// Adds shadows to render lists
  		for ( int j = pRenderList->m_ShadowHandles[nSortGroup].Count()-1; j >= 0; --j )
  		{
  			g_pShadowMgr->AddShadowsOnSurfaceToRenderList( pRenderList->m_ShadowHandles[nSortGroup].Element(j) );
  		}
  		pRenderList->m_ShadowHandles[nSortGroup].RemoveAll();
  
  		// Don't stencil or scissor the flashlight if we're rendering to an offscreen view
  		bool bFlashlightMask = !( (flags & DRAWWORLDLISTS_DRAW_REFRACTION ) || (flags & DRAWWORLDLISTS_DRAW_REFLECTION ));
  
  		// Set masking stencil bits for flashlights
  		g_pShadowMgr->SetFlashlightStencilMasks( bFlashlightMask );
  
  		// Draw shadows and flashlights on world surfaces
  		g_pShadowMgr->RenderFlashlights( bFlashlightMask );
  
  		// Render the fragments from the surfaces + displacements.
  		// FIXME: Actually, this call is irrelevant (for displacements) because it's done from
  		// within DrawDispChain currently, but that should change.
  		// We need to split out the disp decal rendering from DrawDispChain
  		// and do it after overlays are rendered....
  		OverlayMgr()->RenderOverlays( nSortGroup );
  		g_pShadowMgr->DrawFlashlightOverlays( nSortGroup, bFlashlightMask );
  		OverlayMgr()->ClearRenderLists( nSortGroup );
  
  		// Draws decals lying on opaque non-displacement surfaces
  		DecalSurfaceDraw( pRenderContext, nSortGroup );
  
  		// Draw the flashlight lighting for the decals.
  		g_pShadowMgr->DrawFlashlightDecals( nSortGroup, bFlashlightMask );
  
  		// Draw RTT shadows
  		g_pShadowMgr->RenderShadows( );
  		g_pShadowMgr->ClearShadowRenderList();
  
  		if ( nSortGroup == MAT_SORT_GROUP_WATERSURFACE && waterZAdjust != 0.0f )
  		{
  			pRenderContext->MatrixMode( MATERIAL_MODEL );
  			pRenderContext->PopMatrix();
  		}
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Renders translucent surfaces
  //-----------------------------------------------------------------------------
  bool Shader_LeafContainsTranslucentSurfaces( IWorldRenderList *pRenderListIn, int sortIndex, unsigned long flags )
  {
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  	int i;
  	for ( i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
  	{
  		if( !( flags & ( 1 << i ) ) )
  			continue;
  
  		int sortGroup = s_DrawWorldListsToSortGroup[i];
  	
  		// Set the fog state here since it will be the same for all things
  		// in this list of translucent objects (except for displacements)
  		const surfacesortgroup_t &group = pRenderList->m_AlphaSortList.GetGroupForSortID( sortGroup, sortIndex );
  		if ( group.surfaceCount )
  			return true;
  		const surfacesortgroup_t &dispGroup = pRenderList->m_DispAlphaSortList.GetGroupForSortID( sortGroup, sortIndex );
  		if ( dispGroup.surfaceCount )
  			return true;
  	}
  
  	return false;
  }
  
  void Shader_DrawTranslucentSurfaces( IWorldRenderList *pRenderListIn, int sortIndex, unsigned long flags, bool bShadowDepth )
  {
  	if ( !r_drawtranslucentworld.GetBool() )
  		return;
  
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	bool skipLight = false;
  	if ( g_pMaterialSystemConfig->nFullbright == 1 )
  	{
  		pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  		skipLight = true;
  	}
  
  
  
  	// Gotta draw each sort group
  	// Draw the fog volume first, if there is one, because it turns out
  	// that we only draw fog volumes if we're in the fog volume, which
  	// means it's closer. We want to render closer things first to get
  	// fast z-reject.
  	int i;
  	CUtlVector<msurface2_t *> surfaceList;
  	for ( i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
  	{
  		if( !( flags & ( 1 << i ) ) )
  		{
  			continue;
  		}
  		int sortGroup = s_DrawWorldListsToSortGroup[i];
  	
  		// Set the fog state here since it will be the same for all things
  		// in this list of translucent objects (except for displacements)
  
  		surfaceList.RemoveAll();
  		const surfacesortgroup_t &group = pRenderList->m_AlphaSortList.GetGroupForSortID( sortGroup, sortIndex );
  		const surfacesortgroup_t &dispGroup = pRenderList->m_DispAlphaSortList.GetGroupForSortID( sortGroup, sortIndex );
  		// Empty? skip...
  		if (!group.surfaceCount && !dispGroup.surfaceCount )
  			continue;
  
  		pRenderList->m_AlphaSortList.GetSurfaceListForGroup( surfaceList, group );
  
  		// Interate in back-to-front order
  		for ( int listIndex = surfaceList.Count(); --listIndex >= 0; )
  		{
  			SurfaceHandle_t surfID = surfaceList[listIndex];
  			pRenderContext->Bind( MSurf_TexInfo( surfID )->material );
  
  			Assert( MSurf_MaterialSortID( surfID ) >= 0 && 
  				    MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  
  			if ( !skipLight )
  			{
  				pRenderContext->BindLightmapPage( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID );
  			}
  			
  //			NOTE: Since a static vb/dynamic ib IMesh doesn't buffer, we shouldn't use this
  //			since it causes a lock and drawindexedprimitive per surface! (gary)
  //			Shader_DrawSurfaceStatic( surfID );
  			Shader_DrawSurfaceDynamic( pRenderContext, surfID, false );
  
  //			g_pShadowMgr->ClearShadowRenderList();
  
  			// Add shadows/flashlights to list.
  			ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID );
  			if (decalHandle != SHADOW_DECAL_HANDLE_INVALID)
  			{
  				g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle );
  			}
  
  			bool bFlashlightMask = !( (flags & DRAWWORLDLISTS_DRAW_REFRACTION ) || (flags & DRAWWORLDLISTS_DRAW_REFLECTION ));
  
  			// Draw flashlights
  			g_pShadowMgr->RenderFlashlights( bFlashlightMask );
  
  			// Draw overlays on the surface.
  			OverlayMgr()->AddFragmentListToRenderList( i, MSurf_OverlayFragmentList( surfID ), false );
  			OverlayMgr()->RenderOverlays( i );
  
  			// Draw flashlight overlays
  			g_pShadowMgr->DrawFlashlightOverlays( i, bFlashlightMask );
  			OverlayMgr()->ClearRenderLists( i );
  
  		    // Draw decals on the surface
  			DrawDecalsOnSingleSurface( pRenderContext, surfID );
  
  			// Draw flashlight decals
  			g_pShadowMgr->DrawFlashlightDecalsOnSingleSurface( surfID, bFlashlightMask );
  
  			// draw shadows
  			g_pShadowMgr->RenderShadows();
  			g_pShadowMgr->ClearShadowRenderList();
  		}
  		// Draw wireframe, etc information
  		DrawDebugInformation( surfaceList );
  
  		// Now draw the translucent displacements; we need to do these *after* the
  		// non-displacement surfaces because most likely the displacement will always
  		// be in front (or at least not behind) the non-displacement translucent surfaces
  		// that exist in the same leaf.
  		
  		// Draws translucent displacement surfaces
  
  		surfaceList.RemoveAll();
  		surfaceList.EnsureCapacity(dispGroup.surfaceCount);
  		MSL_FOREACH_SURFACE_IN_GROUP_BEGIN(pRenderList->m_DispAlphaSortList, dispGroup, surfID)
  		{
  			surfaceList.AddToTail(surfID);
  		}
  		MSL_FOREACH_SURFACE_IN_GROUP_END()
  
  		DispInfo_RenderList( i, surfaceList.Base(), surfaceList.Count(), g_EngineRenderer->ViewGetCurrent().m_bOrtho, flags, DEPTH_MODE_NORMAL );
  	}
  }
  
  
  
  //=============================================================
  //
  //	WORLD MODEL
  //
  //=============================================================
  
  void FASTCALL R_DrawSurface( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	ASSERT_SURF_VALID( surfID );
  	Assert( !SurfaceHasDispInfo( surfID ) );
  	if ( MSurf_Flags( surfID ) & SURFDRAW_SKY )
  	{
  		pRenderList->m_bSkyVisible = true;
  	}
  //	else if ( surf->texinfo->material->IsTranslucent() )
  	else if( MSurf_Flags( surfID ) & SURFDRAW_TRANS )
  	{
  		Shader_TranslucentWorldSurface( pRenderList, surfID );
  	}
  	else
  	{
  		Shader_WorldSurface( pRenderList, surfID );
  	}
  }
  
  // The NoCull flavor of this function calls functions which optimize for shadow depth map rendering
  void FASTCALL R_DrawSurfaceNoCull( CWorldRenderList *pRenderList, SurfaceHandle_t surfID )
  {
  	ASSERT_SURF_VALID( surfID );
  	if( !(MSurf_Flags( surfID ) & SURFDRAW_TRANS) && !(MSurf_Flags( surfID ) & SURFDRAW_SKY) )
  	{
  		Shader_WorldSurfaceNoCull( pRenderList, surfID );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Draws displacements in a leaf
  //-----------------------------------------------------------------------------
  static inline void DrawDisplacementsInLeaf( CWorldRenderList *pRenderList, mleaf_t* pLeaf )
  {
  	// add displacement surfaces
  	if (!pLeaf->dispCount)
  		return;
  
  	CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  	for ( int i = 0; i < pLeaf->dispCount; i++ )
  	{
  		IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i );
  
  		// NOTE: We're not using the displacement's touched method here 
  		// because we're just using the parent surface's visframe in the
  		// surface add methods below...
  		SurfaceHandle_t parentSurfID = pDispInfo->GetParent();
  
  		// already processed this frame? Then don't do it again!
  		if ( VisitSurface( visitedSurfs, parentSurfID ) )
  		{
  			if ( MSurf_Flags( parentSurfID ) & SURFDRAW_TRANS)
  			{
  				Shader_TranslucentDisplacementSurface( pRenderList, parentSurfID );
  			}
  			else
  			{
  				Shader_DisplacementSurface( pRenderList, parentSurfID );
  			}
  		}
  	}
  }
  
  int LeafToIndex( mleaf_t* pLeaf );
  
  //-----------------------------------------------------------------------------
  // Updates visibility + alpha lists
  //-----------------------------------------------------------------------------
  static inline void UpdateVisibleLeafLists( CWorldRenderList *pRenderList, mleaf_t* pLeaf )
  {
  	// Consistency check...
  	MEM_ALLOC_CREDIT();
  	
  	// Add this leaf to the list of visible leafs
  	int nLeafIndex = LeafToIndex( pLeaf );
  	pRenderList->m_VisibleLeaves.AddToTail( nLeafIndex );
  	int leafCount = pRenderList->m_VisibleLeaves.Count();
  	pRenderList->m_VisibleLeafFogVolumes.AddToTail( pLeaf->leafWaterDataID );
  	pRenderList->m_AlphaSortList.EnsureMaxSortIDs( leafCount );
  	pRenderList->m_DispAlphaSortList.EnsureMaxSortIDs( leafCount );
  }
  
   
  //-----------------------------------------------------------------------------
  // Draws all displacements + surfaces in a leaf
  //-----------------------------------------------------------------------------
  static void FASTCALL R_DrawLeaf( CWorldRenderList *pRenderList, mleaf_t *pleaf )
  {
  	// Add this leaf to the list of visible leaves
  	UpdateVisibleLeafLists( pRenderList, pleaf );
  
  	// Debugging to only draw at a particular leaf
  #ifdef USE_CONVARS
  	if ( (s_ShaderConvars.m_nDrawLeaf >= 0) && (s_ShaderConvars.m_nDrawLeaf != LeafToIndex(pleaf)) )
  		return;
  #endif
  
  	// add displacement surfaces
  	DrawDisplacementsInLeaf( pRenderList, pleaf );
  
  #ifdef USE_CONVARS
  	if( !s_ShaderConvars.m_bDrawWorld )
  		return;
  #endif
  
  	// Add non-displacement surfaces
  	int i;
  	int nSurfaceCount = pleaf->nummarknodesurfaces;
  	SurfaceHandle_t *pSurfID = &host_state.worldbrush->marksurfaces[pleaf->firstmarksurface];
  	CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  	for ( i = 0; i < nSurfaceCount; ++i )
  	{
  		// garymctoptimize - can we prefetch the next surfaces?
  		// We seem to be taking a huge hit here for referencing the surface for the first time.
  		SurfaceHandle_t surfID = pSurfID[i];
  		ASSERT_SURF_VALID( surfID );
  		// there are never any displacements or nodraws in the leaf list
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  		Assert( (MSurf_Flags( surfID ) & SURFDRAW_NODE) );
  		Assert( !SurfaceHasDispInfo(surfID) );
  		// mark this one to be drawn at the node
  		MarkSurfaceVisited( visitedSurfs, surfID );
  	}
  
  #ifdef USE_CONVARS
  	if( !s_ShaderConvars.m_bDrawFuncDetail )
  		return;
  #endif
  
  	for ( ; i < pleaf->nummarksurfaces; i++ )
  	{
  		SurfaceHandle_t surfID = pSurfID[i];
  
  		// Don't process the same surface twice
  		if ( !VisitSurface( visitedSurfs, surfID ) )
  			continue;
  
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODE) );
  
  		// Back face cull; only func_detail are drawn here
  		if ( (MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0 )
  		{
  			if ( (DotProduct(MSurf_Plane( surfID ).normal, modelorg) -
  				  MSurf_Plane( surfID ).dist ) < BACKFACE_EPSILON )
  				continue;
  		}
  
  		R_DrawSurface( pRenderList, surfID );
  	}
  }
  
  static ConVar r_frustumcullworld( "r_frustumcullworld", "1" );
  
  static void FASTCALL R_DrawLeafNoCull( CWorldRenderList *pRenderList, mleaf_t *pleaf )
  {
  	// Add this leaf to the list of visible leaves
  	UpdateVisibleLeafLists( pRenderList, pleaf );
  
  	// add displacement surfaces
  	DrawDisplacementsInLeaf( pRenderList, pleaf );
  	int i;
  	SurfaceHandle_t *pSurfID = &host_state.worldbrush->marksurfaces[pleaf->firstmarksurface];
  	CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  	for ( i = 0; i < pleaf->nummarksurfaces; i++ )
  	{
  		SurfaceHandle_t surfID = pSurfID[i];
  
  		// Don't process the same surface twice
  		if ( !VisitSurface( visitedSurfs, surfID ) )
  			continue;
  
  		R_DrawSurfaceNoCull( pRenderList, surfID );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: recurse on the BSP tree, calling the surface visitor
  // Input  : *node - BSP node
  //-----------------------------------------------------------------------------
  static void R_RecursiveWorldNodeNoCull( CWorldRenderList *pRenderList, mnode_t *node, int nCullMask )
  {
  	int			side;
  	cplane_t	*plane;
  	float		dot;
  
  	while (true)
  	{
  		// no polygons in solid nodes
  		if (node->contents == CONTENTS_SOLID)
  			return;		// solid
  
  		// Check PVS signature
  		if (node->visframe != r_visframecount)
  			return;
  
  		// Cull against the screen frustum or the appropriate area's frustum.
  		if ( nCullMask != FRUSTUM_SUPPRESS_CLIPPING )
  		{
  			if (node->contents >= -1)
  			{
  				if ((nCullMask != 0) || ( node->area > 0 ))
  				{
  					if ( R_CullNode( &g_Frustum, node, nCullMask ) )
  						return;
  				}
  			}
  			else
  			{
  				// This prevents us from culling nodes that are too small to worry about
  				if (node->contents == -2)
  				{
  					nCullMask = FRUSTUM_SUPPRESS_CLIPPING;
  				}
  			}
  		}
  
  		// if a leaf node, draw stuff
  		if (node->contents >= 0)
  		{
  			R_DrawLeafNoCull( pRenderList, (mleaf_t *)node );
  			return;
  		}
  
  		// node is just a decision point, so go down the appropriate sides
  
  		// find which side of the node we are on
  		plane = node->plane;
  		if ( plane->type <= PLANE_Z )
  		{
  			dot = modelorg[plane->type] - plane->dist;
  		}
  		else
  		{
  			dot = DotProduct (modelorg, plane->normal) - plane->dist;
  		}
  
  		// recurse down the children, closer side first.
  		// We have to do this because we need to find if the surfaces at this node
  		// exist in any visible leaves closer to the camera than the node is. If so,
  		// their r_surfacevisframe is set to indicate that we need to render them
  		// at this node.
  		side = dot >= 0 ? 0 : 1;
  
  		// Recurse down the side closer to the camera
  		R_RecursiveWorldNodeNoCull (pRenderList, node->children[side], nCullMask );
  
  		// recurse down the side farther from the camera
  		// NOTE: With this while loop, this is identical to just calling
  		// R_RecursiveWorldNodeNoCull (node->children[!side], nCullMask );
  		node = node->children[!side];
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: recurse on the BSP tree, calling the surface visitor
  // Input  : *node - BSP node
  //-----------------------------------------------------------------------------
  
  static void R_RecursiveWorldNode( CWorldRenderList *pRenderList, mnode_t *node, int nCullMask )
  {
  	int			side;
  	cplane_t	*plane;
  	float		dot;
  
  	while (true)
  	{
  		// no polygons in solid nodes
  		if (node->contents == CONTENTS_SOLID)
  			return;		// solid
  
  		// Check PVS signature
  		if (node->visframe != r_visframecount)
  			return;
  
  		// Cull against the screen frustum or the appropriate area's frustum.
  		if ( nCullMask != FRUSTUM_SUPPRESS_CLIPPING )
  		{
  			if (node->contents >= -1)
  			{
  				if ((nCullMask != 0) || ( node->area > 0 ))
  				{
  					if ( R_CullNode( &g_Frustum, node, nCullMask ) )
  						return;
  				}
  			}
  			else
  			{
  				// This prevents us from culling nodes that are too small to worry about
  				if (node->contents == -2)
  				{
  					nCullMask = FRUSTUM_SUPPRESS_CLIPPING;
  				}
  			}
  		}
  
  		// if a leaf node, draw stuff
  		if (node->contents >= 0)
  		{
  			R_DrawLeaf( pRenderList, (mleaf_t *)node );
  			return;
  		}
  
  		// node is just a decision point, so go down the appropriate sides
  
  		// find which side of the node we are on
  		plane = node->plane;
  		if ( plane->type <= PLANE_Z )
  		{
  			dot = modelorg[plane->type] - plane->dist;
  		}
  		else
  		{
  			dot = DotProduct (modelorg, plane->normal) - plane->dist;
  		}
  
  		// recurse down the children, closer side first.
  		// We have to do this because we need to find if the surfaces at this node
  		// exist in any visible leaves closer to the camera than the node is. If so,
  		// their r_surfacevisframe is set to indicate that we need to render them
  		// at this node.
  		side = dot >= 0 ? 0 : 1;
  
  		// Recurse down the side closer to the camera
  		R_RecursiveWorldNode (pRenderList, node->children[side], nCullMask );
  
  		// draw stuff on the node
  
  		SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface );
  		int i = MSurf_Index( surfID );
  		int nLastSurface = i + node->numsurfaces;
  		CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  		for ( ; i < nLastSurface; ++i, ++surfID )
  		{
  			// Only render things at this node that have previously been marked as visible
  			if ( !VisitedSurface( visitedSurfs, i ) )
  				continue;
  
  			// Don't add surfaces that have displacement
  			// UNDONE: Don't emit these at nodes in vbsp!
  			// UNDONE: Emit them at the end of the surface list
  			Assert( !SurfaceHasDispInfo( surfID ) );
  
  			// If a surface is marked to draw at a node, then it's not a func_detail.
  			// Only func_detail render at leaves. In the case of normal world surfaces,
  			// we only want to render them if they intersect a visible leaf.
  			int nFlags = MSurf_Flags( surfID );
  
  			Assert( nFlags & SURFDRAW_NODE );
  
  			Assert( !(nFlags & SURFDRAW_NODRAW) );
  
  			if ( !(nFlags & SURFDRAW_UNDERWATER) && ( side ^ !!(nFlags & SURFDRAW_PLANEBACK)) )
  				continue;		// wrong side
  
  			R_DrawSurface( pRenderList, surfID );
  		}
  
  		// recurse down the side farther from the camera
  		// NOTE: With this while loop, this is identical to just calling
  		// R_RecursiveWorldNode (node->children[!side], nCullMask );
  		node = node->children[!side];
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Set up fog for a particular leaf
  //-----------------------------------------------------------------------------
  #define INVALID_WATER_HEIGHT 1000000.0f
  inline float R_GetWaterHeight( int nFogVolume )
  {
  	if( nFogVolume < 0 || nFogVolume > host_state.worldbrush->numleafwaterdata )
  		return INVALID_WATER_HEIGHT;
  
  	mleafwaterdata_t* pLeafWaterData = &host_state.worldbrush->leafwaterdata[nFogVolume];
  	return pLeafWaterData->surfaceZ;
  }
  
  IMaterial *R_GetFogVolumeMaterial( int nFogVolume, bool bEyeInFogVolume )
  {
  	if( nFogVolume < 0 || nFogVolume > host_state.worldbrush->numleafwaterdata )
  		return NULL;
  
  	mleafwaterdata_t* pLeafWaterData = &host_state.worldbrush->leafwaterdata[nFogVolume];
  	mtexinfo_t* pTexInfo = &host_state.worldbrush->texinfo[pLeafWaterData->surfaceTexInfoID];
  
  	IMaterial* pMaterial = pTexInfo->material;
  	if( bEyeInFogVolume )
  	{
  		IMaterialVar *pVar = pMaterial->FindVar( "$bottommaterial", NULL );
  		if( pVar )
  		{
  			const char *pMaterialName = pVar->GetStringValue();
  			if( pMaterialName )
  			{
  				pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER );
  			}
  		}
  	}
  	return pMaterial;
  }
  
  void R_SetFogVolumeState( int fogVolume, bool useHeightFog )
  {
  	// useHeightFog == eye out of water
  	// !useHeightFog == eye in water
  	IMaterial *pMaterial = R_GetFogVolumeMaterial( fogVolume, !useHeightFog );
  	mleafwaterdata_t* pLeafWaterData = &host_state.worldbrush->leafwaterdata[fogVolume];
  	IMaterialVar* pFogColorVar	= pMaterial->FindVar( "$fogcolor", NULL );
  	IMaterialVar* pFogEnableVar = pMaterial->FindVar( "$fogenable", NULL );
  	IMaterialVar* pFogStartVar	= pMaterial->FindVar( "$fogstart", NULL );
  	IMaterialVar* pFogEndVar	= pMaterial->FindVar( "$fogend", NULL );
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	if( pMaterial && pFogEnableVar->GetIntValueFast() && fog_enable_water_fog.GetBool() )
  	{
  		pRenderContext->SetFogZ( pLeafWaterData->surfaceZ );
  		if( useHeightFog )
  		{
  			pRenderContext->FogMode( MATERIAL_FOG_LINEAR_BELOW_FOG_Z );
  		}
  		else
  		{
  			pRenderContext->FogMode( MATERIAL_FOG_LINEAR );
  		}
  		float fogColor[3];
  		pFogColorVar->GetVecValueFast( fogColor, 3 );
  
  		pRenderContext->FogColor3fv( fogColor );
  		pRenderContext->FogStart( pFogStartVar->GetFloatValueFast() );
  		pRenderContext->FogEnd( pFogEndVar->GetFloatValueFast() );
  		pRenderContext->FogMaxDensity( 1.0 );
  	}
  	else
  	{
  		pRenderContext->FogMode( MATERIAL_FOG_NONE );
  	}
  }
  
  static inline bool R_CullNodeTopView( mnode_t *pNode )
  {
  	Vector2D delta, size;
  	Vector2DSubtract( pNode->m_vecCenter.AsVector2D(), s_OrthographicCenter, delta );
  	Vector2DAdd( pNode->m_vecHalfDiagonal.AsVector2D(), s_OrthographicHalfDiagonal, size );
  	return ( FloatMakePositive( delta.x ) > size.x ) ||
  		( FloatMakePositive( delta.y ) > size.y );
  }
  
  
  //-----------------------------------------------------------------------------
  // Draws all displacements + surfaces in a leaf
  //-----------------------------------------------------------------------------
  static void R_DrawTopViewLeaf( CWorldRenderList *pRenderList, mleaf_t *pleaf )
  {
  	// Add this leaf to the list of visible leaves
  	UpdateVisibleLeafLists( pRenderList, pleaf );
  
  	// add displacement surfaces
  	DrawDisplacementsInLeaf( pRenderList, pleaf );
  
  #ifdef USE_CONVARS
  	if( !s_ShaderConvars.m_bDrawWorld )
  		return;
  #endif
  
  	// Add non-displacement surfaces
  	SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pleaf->firstmarksurface];
  	CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  	for ( int i = 0; i < pleaf->nummarksurfaces; i++ )
  	{
  		SurfaceHandle_t surfID = pHandle[i];
  
  		// Mark this surface as being in a visible leaf this frame. If this
  		// surface is meant to be drawn at a node (SURFDRAW_NODE), 
  		// then it will be drawn in the recursive code down below.
  		if ( !VisitSurface( visitedSurfs, surfID ) )
  			continue;
  
  		// Don't add surfaces that have displacement; they are handled above
  		// In fact, don't even set the vis frame; we need it unset for translucent
  		// displacement code
  		if ( SurfaceHasDispInfo(surfID) )
  			continue;
  
  		if ( MSurf_Flags( surfID ) & SURFDRAW_NODE )
  			continue;
  
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  
  		// Back face cull; only func_detail are drawn here
  		if ( (MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0 )
  		{
  			if (MSurf_Plane( surfID ).normal.z <= 0.0f)
  				continue;
  		}
  
  		// FIXME: For now, blow off translucent world polygons.
  		// Gotta solve the problem of how to render them all, unsorted,
  		// in a pass after the opaque world polygons, and before the
  		// translucent entities.
  
  		if ( !( MSurf_Flags( surfID ) & SURFDRAW_TRANS ))
  //		if ( !surf->texinfo->material->IsTranslucent() )
  			Shader_WorldSurface( pRenderList, surfID );
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Fast path for rendering top-views
  //-----------------------------------------------------------------------------
  void R_RenderWorldTopView( CWorldRenderList *pRenderList, mnode_t *node )
  {
  	CVisitedSurfs &visitedSurfs = pRenderList->m_VisitedSurfs;
  	do
  	{
  		// no polygons in solid nodes
  		if (node->contents == CONTENTS_SOLID)
  			return;		// solid
  
  		// Check PVS signature
  		if (node->visframe != r_visframecount)
  			return;
  
  		// Cull against the screen frustum or the appropriate area's frustum.
  		if( R_CullNodeTopView( node ) )
  			return;
  
  		// if a leaf node, draw stuff
  		if (node->contents >= 0)
  		{
  			R_DrawTopViewLeaf( pRenderList, (mleaf_t *)node );
  			return;
  		}
  
  #ifdef USE_CONVARS
  		if (s_ShaderConvars.m_bDrawWorld)
  #endif
  		{
  			// draw stuff on the node
  			SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface );
  			for ( int i = 0; i < node->numsurfaces; i++, surfID++ )
  			{
  				if ( !VisitSurface( visitedSurfs, surfID ) )
  					continue;
  
  				// Don't add surfaces that have displacement
  				if ( SurfaceHasDispInfo( surfID ) )
  					continue;
  
  				// If a surface is marked to draw at a node, then it's not a func_detail.
  				// Only func_detail render at leaves. In the case of normal world surfaces,
  				// we only want to render them if they intersect a visible leaf.
  				Assert( (MSurf_Flags( surfID ) & SURFDRAW_NODE) );
  
  				if ( MSurf_Flags( surfID ) & (SURFDRAW_UNDERWATER|SURFDRAW_SKY) )
  					continue;
  
  				Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  				// Back face cull
  				if ( (MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0 )
  				{
  					if (MSurf_Plane( surfID ).normal.z <= 0.0f)
  						continue;
  				}
  
  				// FIXME: For now, blow off translucent world polygons.
  				// Gotta solve the problem of how to render them all, unsorted,
  				// in a pass after the opaque world polygons, and before the
  				// translucent entities.
  
  				if ( !( MSurf_Flags( surfID ) & SURFDRAW_TRANS ) )
  //				if ( !surf->texinfo->material->IsTranslucent() )
  					Shader_WorldSurface( pRenderList, surfID );
  			}
  		}
  
  		// Recurse down both children, we don't care the order...
  		R_RenderWorldTopView ( pRenderList, node->children[0]);
  		node = node->children[1];
  
  	} while (node);
  }
  
  //-----------------------------------------------------------------------------
  // Spews the leaf we're in
  //-----------------------------------------------------------------------------
  static void SpewLeaf()
  {
  	int leaf = CM_PointLeafnum( g_EngineRenderer->ViewOrigin() );
  	ConMsg(	"view leaf %d\n", leaf );
  }
  
  
  //-----------------------------------------------------------------------------
  // Main entry points for starting + ending rendering the world
  //-----------------------------------------------------------------------------
  void R_BuildWorldLists( IWorldRenderList *pRenderListIn, WorldListInfo_t* pInfo, 
  	int iForceViewLeaf, const VisOverrideData_t* pVisData, bool bShadowDepth /* = false */, float *pWaterReflectionHeight )
  {
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  	// Safety measure just in case. I haven't seen that we need this, but...
  	if ( g_LostVideoMemory )
  	{
  		if (pInfo)
  		{
  			pInfo->m_ViewFogVolume = MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  			pInfo->m_LeafCount = 0;
  			pInfo->m_pLeafList = pRenderList->m_VisibleLeaves.Base();
  			pInfo->m_pLeafFogVolume = pRenderList->m_VisibleLeafFogVolumes.Base();
  		}
  		return;
  	}
  
  	VPROF( "R_BuildWorldLists" );
  	VectorCopy( g_EngineRenderer->ViewOrigin(), modelorg );
  
  #ifdef USE_CONVARS
  	static ConVar r_spewleaf("r_spewleaf", "0");
  	if ( r_spewleaf.GetInt() )
  	{
  		SpewLeaf();
  	}
  #endif
  
  	Shader_WorldBegin( pRenderList );
  
  	if ( !r_drawtopview )
  	{
  		R_SetupAreaBits( iForceViewLeaf, pVisData, pWaterReflectionHeight );
  
  		if ( bShadowDepth )
  		{
  			R_RecursiveWorldNodeNoCull( pRenderList, host_state.worldbrush->nodes, r_frustumcullworld.GetBool() ? FRUSTUM_CLIP_ALL : FRUSTUM_SUPPRESS_CLIPPING );
  		}
  		else
  		{
  			R_RecursiveWorldNode( pRenderList, host_state.worldbrush->nodes, r_frustumcullworld.GetBool() ? FRUSTUM_CLIP_ALL : FRUSTUM_SUPPRESS_CLIPPING );
  		}
  	}
  	else
  	{
  		R_RenderWorldTopView( pRenderList, host_state.worldbrush->nodes );
  	}
  
  		// This builds all lightmaps, including those for translucent surfaces
  	// Don't bother in topview?
  	if ( !r_drawtopview && !bShadowDepth )
  	{
  		Shader_BuildDynamicLightmaps( pRenderList );
  	}
  
  	// Return the back-to-front leaf ordering
  	if ( pInfo )
  	{
  		// Compute fog volume info for rendering
  		if ( !bShadowDepth )
  		{
  			FogVolumeInfo_t fogInfo;
  			ComputeFogVolumeInfo( &fogInfo );
  			if( fogInfo.m_InFogVolume )
  			{
  				pInfo->m_ViewFogVolume = MAT_SORT_GROUP_STRICTLY_UNDERWATER;
  			}
  			else
  			{
  				pInfo->m_ViewFogVolume = MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  			}
  		}
  		else
  		{
  			pInfo->m_ViewFogVolume = MAT_SORT_GROUP_STRICTLY_ABOVEWATER;
  		}
  		pInfo->m_LeafCount = pRenderList->m_VisibleLeaves.Count();
  		pInfo->m_pLeafList = pRenderList->m_VisibleLeaves.Base();
  		pInfo->m_pLeafFogVolume = pRenderList->m_VisibleLeafFogVolumes.Base();
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Used to determine visible fog volumes
  //-----------------------------------------------------------------------------
  class CVisibleFogVolumeQuery
  {
  public:
  	void FindVisibleFogVolume( const Vector &vecViewPoint, int *pVisibleFogVolume, int *pVisibleFogVolumeLeaf );
  
  private:
  	bool RecursiveGetVisibleFogVolume( mnode_t *node );
  
  	// Input
  	Vector m_vecSearchPoint;
  
  	// Output
  	int m_nVisibleFogVolume;
  	int m_nVisibleFogVolumeLeaf;
  };
  
  
  //-----------------------------------------------------------------------------
  // Main entry point for the query
  //-----------------------------------------------------------------------------
  void CVisibleFogVolumeQuery::FindVisibleFogVolume( const Vector &vecViewPoint, int *pVisibleFogVolume, int *pVisibleFogVolumeLeaf )
  {
  	R_SetupAreaBits();
  
  	m_vecSearchPoint = vecViewPoint;
  	m_nVisibleFogVolume = -1;
  	m_nVisibleFogVolumeLeaf = -1;
  
  	RecursiveGetVisibleFogVolume( host_state.worldbrush->nodes );
  
  	*pVisibleFogVolume = m_nVisibleFogVolume;
  	*pVisibleFogVolumeLeaf = m_nVisibleFogVolumeLeaf;
  }
  
  
  //-----------------------------------------------------------------------------
  // return true to continue searching
  //-----------------------------------------------------------------------------
  bool CVisibleFogVolumeQuery::RecursiveGetVisibleFogVolume( mnode_t *node )
  {
  	int			side;
  	cplane_t	*plane;
  	float		dot;
  
  	// no polygons in solid nodes
  	if (node->contents == CONTENTS_SOLID)
  		return true;		// solid
  
  	// Check PVS signature
  	if (node->visframe != r_visframecount)
  		return true;
  
  	// Cull against the screen frustum or the appropriate area's frustum.
  	int fixmeTempRemove = FRUSTUM_CLIP_ALL;
  	if( R_CullNode( &g_Frustum, node, fixmeTempRemove ) )
  		return true;
  
  	// if a leaf node, check if we are in a fog volume and get outta here.
  	if (node->contents >= 0)
  	{
  		mleaf_t *pLeaf = (mleaf_t *)node;
  
  		// Don't return a leaf that's not filled with liquid
  		if ( pLeaf->leafWaterDataID == -1 )
  			return true;
  
  		// Never return SLIME as being visible, as it's opaque
  		if ( pLeaf->contents & CONTENTS_SLIME )
  			return true;
  
  		m_nVisibleFogVolume = pLeaf->leafWaterDataID;
  		m_nVisibleFogVolumeLeaf = pLeaf - host_state.worldbrush->leafs;
  		return false;  // found it, so stop searching
  	}
  
  	// node is just a decision point, so go down the apropriate sides
  
  	// find which side of the node we are on
  	plane = node->plane;
  	if ( plane->type <= PLANE_Z )
  	{
  		dot = m_vecSearchPoint[plane->type] - plane->dist;
  	}
  	else
  	{
  		dot = DotProduct( m_vecSearchPoint, plane->normal ) - plane->dist;
  	}
  
  	// recurse down the children, closer side first.
  	// We have to do this because we need to find if the surfaces at this node
  	// exist in any visible leaves closer to the camera than the node is. If so,
  	// their r_surfacevisframe is set to indicate that we need to render them
  	// at this node.
  	side = (dot >= 0) ? 0 : 1;
  
  	// Recurse down the side closer to the camera
  	if( !RecursiveGetVisibleFogVolume (node->children[side]) )
  		return false;
  
  	// recurse down the side farther from the camera
  	return RecursiveGetVisibleFogVolume (node->children[!side]);
  }
  
  
  static void ClearFogInfo( VisibleFogVolumeInfo_t *pInfo )
  {
  	pInfo->m_bEyeInFogVolume = false;
  	pInfo->m_nVisibleFogVolume = -1;
  	pInfo->m_nVisibleFogVolumeLeaf = -1;
  	pInfo->m_pFogVolumeMaterial = NULL;
  	pInfo->m_flWaterHeight = INVALID_WATER_HEIGHT;
  }
  
  ConVar fast_fogvolume("fast_fogvolume", "0");
  //-----------------------------------------------------------------------------
  // Main entry point from renderer to get the fog volume
  //-----------------------------------------------------------------------------
  void R_GetVisibleFogVolume( const Vector& vEyePoint, VisibleFogVolumeInfo_t *pInfo )
  {
  	VPROF_BUDGET( "R_GetVisibleFogVolume", VPROF_BUDGETGROUP_WORLD_RENDERING );
  
  	if ( host_state.worldmodel->brush.pShared->numleafwaterdata == 0 )
  	{
  		ClearFogInfo( pInfo );
  		return;
  	}
  
  	int nLeafID = CM_PointLeafnum( vEyePoint );
  	mleaf_t* pLeaf = &host_state.worldbrush->leafs[nLeafID];
  
  	int nLeafContents = pLeaf->contents;
  	if ( pLeaf->leafWaterDataID != -1 )
  	{
  		Assert( nLeafContents & (CONTENTS_SLIME | CONTENTS_WATER) );
  		pInfo->m_bEyeInFogVolume = true;
  		pInfo->m_nVisibleFogVolume = pLeaf->leafWaterDataID;
  		pInfo->m_nVisibleFogVolumeLeaf = nLeafID;
  		pInfo->m_pFogVolumeMaterial = R_GetFogVolumeMaterial( pInfo->m_nVisibleFogVolume, true );
  		pInfo->m_flWaterHeight = R_GetWaterHeight( pInfo->m_nVisibleFogVolume );
  	}
  	else if ( nLeafContents & CONTENTS_TESTFOGVOLUME )
  	{
  		Assert( (nLeafContents & (CONTENTS_SLIME | CONTENTS_WATER)) == 0 );
  		if ( fast_fogvolume.GetBool() && host_state.worldbrush->numleafwaterdata == 1 )
  		{
  			pInfo->m_nVisibleFogVolume = 0;
  			pInfo->m_nVisibleFogVolumeLeaf = host_state.worldbrush->leafwaterdata[0].firstLeafIndex;
  		}
  		else
  		{
  			CVisibleFogVolumeQuery query;
  			query.FindVisibleFogVolume( vEyePoint, &pInfo->m_nVisibleFogVolume, &pInfo->m_nVisibleFogVolumeLeaf ); 
  		}
  
  		pInfo->m_bEyeInFogVolume = false;
  		pInfo->m_pFogVolumeMaterial = R_GetFogVolumeMaterial( pInfo->m_nVisibleFogVolume, false );
  		pInfo->m_flWaterHeight = R_GetWaterHeight( pInfo->m_nVisibleFogVolume );
  	}
  	else
  	{
  		ClearFogInfo( pInfo );
  	}
  
  	if( host_state.worldbrush->m_LeafMinDistToWater )
  	{
  		pInfo->m_flDistanceToWater = ( float )host_state.worldbrush->m_LeafMinDistToWater[nLeafID];
  	}
  	else
  	{
  		pInfo->m_flDistanceToWater = 0.0f;
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Draws the list of surfaces build in the BuildWorldLists phase
  //-----------------------------------------------------------------------------
  
  // Uncomment this to allow code to draw wireframe over a particular surface for debugging
  //#define DEBUG_SURF 1
  
  #ifdef DEBUG_SURF
  int g_DebugSurfIndex = -1;
  #endif
  
  void R_DrawWorldLists( IWorldRenderList *pRenderListIn, unsigned long flags, float waterZAdjust )
  {
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  	if ( g_bTextMode || g_LostVideoMemory )
  		return;
  
  	VPROF("R_DrawWorldLists");
  	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__  );
  	Shader_WorldEnd( pRenderList, flags, waterZAdjust );
  
  #ifdef DEBUG_SURF
  	{
  		VPROF("R_DrawWorldLists (DEBUG_SURF)");
  		if (g_pDebugSurf)
  		{
  			CMatRenderContextPtr pRenderContext( materials );
  			pRenderContext->Bind( g_materialWorldWireframe );
  			Shader_DrawSurfaceDynamic( pRenderContext, g_pDebugSurf, false );
  		}
  	}
  #endif
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: 
  //-----------------------------------------------------------------------------
  void R_SceneBegin( void )
  {
  	ComputeDebugSettings();
  }
  
  void R_SceneEnd( void )
  {
  }
  
  //-----------------------------------------------------------------------------
  // Debugging code to draw the lightmap pages
  //-----------------------------------------------------------------------------
  
  void Shader_DrawLightmapPageSurface( SurfaceHandle_t surfID, float red, float green, float blue )
  {
  	Vector2D lightCoords[32][4];
  
  	int bumpID, count;
  	if ( MSurf_Flags( surfID ) & SURFDRAW_BUMPLIGHT )
  	{
  		count = NUM_BUMP_VECTS + 1;
  	}
  	else
  	{
  		count = 1;
  	}
  
  	BuildMSurfaceVerts( host_state.worldbrush, surfID, NULL, NULL, lightCoords );
  
  	int lightmapPageWidth, lightmapPageHeight;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	pRenderContext->Bind( g_materialWireframe );
  	materials->GetLightmapPageSize( 
  		SortInfoToLightmapPage(MSurf_MaterialSortID( surfID )), 
  		&lightmapPageWidth, &lightmapPageHeight );
  
  	for( bumpID = 0; bumpID < count; bumpID++ )
  	{
  		// assumes that we are already in ortho mode.
  		IMesh* pMesh = pRenderContext->GetDynamicMesh( );
  					
  		CMeshBuilder meshBuilder;
  		meshBuilder.Begin( pMesh, MATERIAL_LINES, MSurf_VertCount( surfID ) );
  		
  		int i;
  
  		for( i = 0; i < MSurf_VertCount( surfID ); i++ )
  		{
  			float x, y;
  			float *texCoord;
  
  			texCoord = &lightCoords[i][bumpID][0];
  
  			x = lightmapPageWidth * texCoord[0];
  			y = lightmapPageHeight * texCoord[1];
  #ifdef _XBOX
  			// xboxissue - border safe
  			x += 32;
  			y += 32;
  #endif			
  			meshBuilder.Position3f( x, y, 0.0f );
  			meshBuilder.AdvanceVertex();
  
  			texCoord = &lightCoords[(i+1)%MSurf_VertCount( surfID )][bumpID][0];
  			x = lightmapPageWidth * texCoord[0];
  			y = lightmapPageHeight * texCoord[1];
  #ifdef _XBOX
  			// xboxissue - border safe
  			x += 32;
  			y += 32;
  #endif			
  			meshBuilder.Position3f( x, y, 0.0f );
  			meshBuilder.AdvanceVertex();
  		}
  		
  		meshBuilder.End();
  		pMesh->Draw();
  	}
  }
  
  void Shader_DrawLightmapPageChains( IWorldRenderList *pRenderListIn, int pageId )
  {
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  	for (int j = 0; j < MAX_MAT_SORT_GROUPS; ++j)
  	{
  		MSL_FOREACH_GROUP_BEGIN( pRenderList->m_SortList, j, group )
  		{
  			SurfaceHandle_t surfID = pRenderList->m_SortList.GetSurfaceAtHead( group );
  			Assert(IS_SURF_VALID(surfID));
  			Assert( MSurf_MaterialSortID( surfID ) >= 0 && MSurf_MaterialSortID( surfID ) < g_WorldStaticMeshes.Count() );
  			if( materialSortInfoArray[MSurf_MaterialSortID( surfID ) ].lightmapPageID != pageId )
  			{
  				continue;
  			}
  			MSL_FOREACH_SURFACE_IN_GROUP_BEGIN( pRenderList->m_SortList, group, surfIDList )
  			{
  				Assert( !SurfaceHasDispInfo( surfIDList ) );
  				Shader_DrawLightmapPageSurface( surfIDList, 0.0f, 1.0f, 0.0f );
  			}
  			MSL_FOREACH_SURFACE_IN_GROUP_END()
  		}
  		MSL_FOREACH_GROUP_END()
  
  		// render displacement lightmap page info
  		MSL_FOREACH_SURFACE_BEGIN(pRenderList->m_DispSortList, j, surfID)
  		{
  			surfID->pDispInfo->RenderWireframeInLightmapPage( pageId );
  		}
  		MSL_FOREACH_SURFACE_END()
  	}
  }
  
  //-----------------------------------------------------------------------------
  //
  // All code related to brush model rendering
  //
  //-----------------------------------------------------------------------------
  
  class CBrushSurface : public IBrushSurface
  {
  public:
  	CBrushSurface( SurfaceHandle_t surfID );
  
  	// Computes texture coordinates + lightmap coordinates given a world position
  	virtual void ComputeTextureCoordinate( const Vector& worldPos, Vector2D& texCoord );
  	virtual void ComputeLightmapCoordinate( const Vector& worldPos, Vector2D& lightmapCoord );
  
  	// Gets the vertex data for this surface
  	virtual int  GetVertexCount() const;
  	virtual void GetVertexData( BrushVertex_t* pVerts );
  
  	// Gets at the material properties for this surface
  	virtual IMaterial* GetMaterial();
  
  private:
  	SurfaceHandle_t m_SurfaceID;
  	SurfaceCtx_t m_Ctx;
  };
  
  
  //-----------------------------------------------------------------------------
  // Constructor
  //-----------------------------------------------------------------------------
  CBrushSurface::CBrushSurface( SurfaceHandle_t surfID ) : m_SurfaceID(surfID)
  {
  	Assert(IS_SURF_VALID(surfID));
  	SurfSetupSurfaceContext( m_Ctx, surfID );
  }
  
  
  //-----------------------------------------------------------------------------
  // Computes texture coordinates + lightmap coordinates given a world position
  //-----------------------------------------------------------------------------
  void CBrushSurface::ComputeTextureCoordinate( const Vector& worldPos, Vector2D& texCoord )
  {
  	SurfComputeTextureCoordinate( m_Ctx, m_SurfaceID, worldPos, texCoord );
  }
  
  void CBrushSurface::ComputeLightmapCoordinate( const Vector& worldPos, Vector2D& lightmapCoord )
  {
  	SurfComputeLightmapCoordinate( m_Ctx, m_SurfaceID, worldPos, lightmapCoord );
  }
  
  
  //-----------------------------------------------------------------------------
  // Gets the vertex data for this surface
  //-----------------------------------------------------------------------------
  int  CBrushSurface::GetVertexCount() const
  {
  	if( !SurfaceHasPrims( m_SurfaceID ) )
  	{
  		// Create a temporary vertex array for the data...
  		return MSurf_VertCount( m_SurfaceID );
  	}
  	else
  	{
  		// not implemented yet
  		Assert(0);
  		return 0;
  	}
  }
  
  void CBrushSurface::GetVertexData( BrushVertex_t* pVerts )
  {
  	Assert( pVerts );
  
  	if( !SurfaceHasPrims( m_SurfaceID ) )
  	{
  		// Fill in the vertex data
  		BuildBrushModelVertexArray( host_state.worldbrush, m_SurfaceID, pVerts );
  	}
  	else
  	{
  		// not implemented yet
  		Assert(0);
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Activates fast z reject for displacements
  //-----------------------------------------------------------------------------
  void R_FastZRejectDisplacements( bool bEnable )
  {
  	s_bFastZRejectDisplacements = bEnable;
  }
  
  
  //-----------------------------------------------------------------------------
  // Gets at the material properties for this surface
  //-----------------------------------------------------------------------------
  IMaterial* CBrushSurface::GetMaterial()
  {
  	return MSurf_TexInfo( m_SurfaceID )->material;
  }
  
  //-----------------------------------------------------------------------------
  // Installs a client-side renderer for brush models
  //-----------------------------------------------------------------------------
  void R_InstallBrushRenderOverride( IBrushRenderer* pBrushRenderer )
  {
  	s_pBrushRenderOverride = pBrushRenderer;
  }
  
  //-----------------------------------------------------------------------------
  // Here, we allow the client DLL to render brush surfaces however they'd like
  // NOTE: This involves a vertex copy, so don't use this everywhere
  //-----------------------------------------------------------------------------
  
  bool Shader_DrawBrushSurfaceOverride( IMatRenderContext *pRenderContext, SurfaceHandle_t surfID, IClientEntity *baseentity )
  {
  	// Set the lightmap state
  	Shader_SetChainLightmapState( pRenderContext, surfID );
  
  	CBrushSurface brushSurface( surfID );
  	return s_pBrushRenderOverride->RenderBrushModelSurface( baseentity, &brushSurface );
  }
  
  
  FORCEINLINE void ModulateMaterial( IMaterial *pMaterial, float *pOldColor )
  {
  	if ( g_bIsBlendingOrModulating )
  	{
  		pOldColor[3] = pMaterial->GetAlphaModulation( );
  		pMaterial->GetColorModulation( &pOldColor[0], &pOldColor[1], &pOldColor[2] );
  		pMaterial->AlphaModulate( r_blend );
  		pMaterial->ColorModulate( r_colormod[0], r_colormod[1], r_colormod[2] );
  	}
  
  }
  
  FORCEINLINE void UnModulateMaterial( IMaterial *pMaterial, float *pOldColor )
  {
  	if ( g_bIsBlendingOrModulating )
  	{
  		pMaterial->AlphaModulate( pOldColor[3] );
  		pMaterial->ColorModulate( pOldColor[0], pOldColor[1], pOldColor[2] );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Main method to draw brush surfaces
  //-----------------------------------------------------------------------------
  void Shader_BrushSurface( SurfaceHandle_t surfID, model_t *model, IClientEntity *baseentity )
  {
  	CMatRenderContextPtr pRenderContext(materials);
  	float pOldColor[4];
  
  	bool drawDecals;
  	if (!s_pBrushRenderOverride)
  	{
  		drawDecals = true;
  
  		IMaterial *pMaterial = MSurf_TexInfo( surfID )->material;
  
  		ModulateMaterial( pMaterial, pOldColor );
  		Shader_SetChainTextureState( pRenderContext, surfID, baseentity, false );
  
  //		NOTE: Since a static vb/dynamic ib IMesh doesn't buffer, we shouldn't use this
  //		since it causes a lock and drawindexedprimitive per surface! (gary)
  //		Shader_DrawSurfaceStatic( surfID );
  		Shader_DrawSurfaceDynamic( pRenderContext, surfID, false );
  
  		// FIXME: This may cause an unnecessary flush to occur!
  		// Thankfully, this is a rare codepath. I don't think anything uses it now.
  		UnModulateMaterial( pMaterial, pOldColor );
  	}
  	else
  	{
  		drawDecals = Shader_DrawBrushSurfaceOverride( pRenderContext, surfID, baseentity );
  	}
  
  	// fixme: need to get "allowDecals" from the material
  	//	if ( g_BrushProperties.allowDecals && pSurf->pdecals ) 
  	if( SurfaceHasDecals( surfID ) && drawDecals )
  	{
  		DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP );
  	}
  
  	// Add overlay fragments to list.
  	// FIXME: A little code support is necessary to get overlays working on brush models
  //	OverlayMgr()->AddFragmentListToRenderList( MSurf_OverlayFragmentList( surfID ), false );
  
  	// Add shadows too....
  	ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID );
  	if (decalHandle != SHADOW_DECAL_HANDLE_INVALID)
  	{
  		g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle );
  	}
  }
  
  
  // UNDONE: These are really guesses.  Do we ever exceed these limits?
  const int MAX_TRANS_NODES = 256;
  const int MAX_TRANS_DECALS = 256;
  const int MAX_TRANS_BATCHES = 1024;
  const int MAX_TRANS_SURFACES = 1024;
  
  class CBrushBatchRender
  {
  public:
  	// These are the compact structs produced by the brush render cache.  The goal is to have a compact
  	// list of drawing instructions for drawing an opaque brush model in the most optimal order.
  	// These structs contain ONLY the opaque surfaces of a brush model.
  	struct brushrendersurface_t
  	{
  		short	surfaceIndex;
  		short	planeIndex;
  	};
  
  	// a batch is a list of surfaces with the same material - they can be drawn with one call to the materialsystem
  	struct brushrenderbatch_t
  	{
  		short	firstSurface;
  		short	surfaceCount;
  		IMaterial *pMaterial;
  		int		sortID;
  		int		indexCount;
  	};
  
  	// a mesh is a list of batches with the same vertex format.
  	struct brushrendermesh_t
  	{
  		short		firstBatch;
  		short		batchCount;
  	};
  
  	// This is the top-level struct containing all data necessary to render an opaque brush model in optimal order
  	struct brushrender_t
  	{
  		// UNDONE: Compact these arrays into a single allocation
  		// UNDONE: Compact entire struct to a single allocation?  Store brushrender_t * in the linked list?
  		void Free()
  		{
  			delete[] pPlanes;
  			delete[] pMeshes;
  			delete[] pBatches;
  			delete[] pSurfaces;
  			pPlanes = NULL;
  			pMeshes = NULL;
  			pBatches = NULL;
  			pSurfaces = NULL;
  		}
  
  		cplane_t				**pPlanes;
  		brushrendermesh_t		*pMeshes;			
  		brushrenderbatch_t		*pBatches;
  		brushrendersurface_t	*pSurfaces;
  		short planeCount;
  		short meshCount;
  		short batchCount;
  		short surfaceCount;
  		short totalIndexCount;
  		short totalVertexCount;
  	};
  
  	// Surfaces are stored in a list like this temporarily for sorting purposes only.  The compact structs do not store these.
  	struct surfacelist_t
  	{
  		SurfaceHandle_t surfID;
  		short	surfaceIndex;
  		short	planeIndex;
  	};
  	
  	// These are the compact structs produced for translucent brush models.  These structs contain
  	// only the translucent surfaces of a brush model.
  
  	// a batch is a list of surfaces with the same material - they can be drawn with one call to the materialsystem
  	struct transbatch_t
  	{
  		short	firstSurface;
  		short	surfaceCount;
  		IMaterial *pMaterial;
  		int		sortID;
  		int		indexCount;
  	};
  
  	// This is a list of surfaces that have decals.
  	struct transdecal_t
  	{
  		short firstSurface;
  		short surfaceCount;
  	};
  
  	// A node is the list of batches that can be drawn without sorting errors.  When no decals are present, surfaces
  	// from the next node may be appended to this one to improve performance without causing sorting errors.
  	struct transnode_t
  	{
  		short			firstBatch;
  		short			batchCount;
  		short			firstDecalSurface;
  		short			decalSurfaceCount;
  	};
  
  	// This is the top-level struct containing all data necessary to render a translucent brush model in optimal order.
  	// NOTE: Unlike the opaque struct, the order of the batches is view-dependent, so caching this is pointless since 
  	// the view usually changes.
  	struct transrender_t
  	{
  		transnode_t		nodes[MAX_TRANS_NODES];
  		SurfaceHandle_t	surfaces[MAX_TRANS_SURFACES];
  		SurfaceHandle_t	decalSurfaces[MAX_TRANS_DECALS];
  		transbatch_t	batches[MAX_TRANS_BATCHES];
  		transbatch_t	*pLastBatch;	// These are used to append surfaces to existing batches across nodes.
  		transnode_t		*pLastNode;		// This improves performance.
  		short			nodeCount;
  		short			batchCount;
  		short			surfaceCount;
  		short			decalSurfaceCount;
  	};
  
  	// Builds a transrender_t, then executes it's drawing commands
  	void DrawTranslucentBrushModel( model_t *model, IClientEntity *baseentity )
  	{
  		transrender_t renderT;
  		renderT.pLastBatch = NULL;
  		renderT.pLastNode = NULL;
  		renderT.nodeCount = 0;
  		renderT.surfaceCount = 0;
  		renderT.batchCount = 0;
  		renderT.decalSurfaceCount = 0;
  		BuildTransLists_r( renderT, model, model->brush.pShared->nodes + model->brush.firstnode );
  		void *pProxyData = baseentity ? baseentity->GetClientRenderable() : NULL;
  		DrawTransLists( renderT, pProxyData );
  	}
  
  	void AddSurfaceToBatch( transrender_t &renderT, transnode_t *pNode, transbatch_t *pBatch, SurfaceHandle_t surfID )
  	{
  		pBatch->surfaceCount++;
  		Assert( renderT.surfaceCount < MAX_TRANS_SURFACES);
  
  		pBatch->indexCount += (MSurf_VertCount( surfID )-2)*3;
  		renderT.surfaces[renderT.surfaceCount] = surfID;
  		renderT.surfaceCount++;
  		if ( SurfaceHasDecals( surfID ) )
  		{
  			Assert( renderT.decalSurfaceCount < MAX_TRANS_DECALS);
  			pNode->decalSurfaceCount++;
  			renderT.decalSurfaces[renderT.decalSurfaceCount] = surfID;
  			renderT.decalSurfaceCount++;
  		}
  	}
  
  	void AddTransNode( transrender_t &renderT )
  	{
  		renderT.pLastNode = &renderT.nodes[renderT.nodeCount];
  		renderT.nodeCount++;
  		Assert( renderT.nodeCount < MAX_TRANS_NODES);
  		renderT.pLastBatch = NULL;
  		renderT.pLastNode->firstBatch = renderT.batchCount;
  		renderT.pLastNode->firstDecalSurface = renderT.decalSurfaceCount;
  		renderT.pLastNode->batchCount = 0;
  		renderT.pLastNode->decalSurfaceCount = 0;
  	}
  
  	void AddTransBatch( transrender_t &renderT, SurfaceHandle_t surfID )
  	{
  		transbatch_t &batch = renderT.batches[renderT.pLastNode->firstBatch + renderT.pLastNode->batchCount];
  		Assert( renderT.batchCount < MAX_TRANS_BATCHES);
  		renderT.pLastNode->batchCount++;
  		renderT.batchCount++;
  		batch.firstSurface = renderT.surfaceCount;
  		batch.surfaceCount = 0;
  		batch.pMaterial = MSurf_TexInfo( surfID )->material;
  		batch.sortID = MSurf_MaterialSortID( surfID );
  		batch.indexCount = 0;
  		renderT.pLastBatch = &batch;
  		AddSurfaceToBatch( renderT, renderT.pLastNode, &batch, surfID );
  	}
  
  	// build node lists
  	void BuildTransLists_r( transrender_t &renderT, model_t *model, mnode_t *node )
  	{
  		float		dot;
  
  		if (node->contents >= 0)
  			return;
  
  		// node is just a decision point, so go down the apropriate sides
  		// find which side of the node we are on
  		cplane_t *plane = node->plane;
  		if ( plane->type <= PLANE_Z )
  		{
  			dot = modelorg[plane->type] - plane->dist;
  		}
  		else
  		{
  			dot = DotProduct (modelorg, plane->normal) - plane->dist;
  		}
  
  		int side = (dot >= 0) ? 0 : 1;
  
  		// back side first - translucent surfaces need to render in back to front order
  		// to appear correctly.
  		BuildTransLists_r( renderT, model, node->children[!side]);
  
  		// emit all surfaces on node
  		CUtlVectorFixed<surfacelist_t, 256> sortList;
  		SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface, model->brush.pShared );
  		for ( int i = 0; i < node->numsurfaces; i++, surfID++ )
  		{
  			// skip opaque surfaces
  			if ( MSurf_Flags(surfID) & SURFDRAW_TRANS )
  			{
  				if ( ((MSurf_Flags( surfID ) & SURFDRAW_NOCULL) == 0) )
  				{
  					// Backface cull here, so they won't do any more work
  					if ( ( side ^ !!(MSurf_Flags( surfID ) & SURFDRAW_PLANEBACK)) )
  						continue;
  				}
  
  				// If this can be appended to the previous batch, do so
  				int sortID = MSurf_MaterialSortID( surfID );
  				if ( renderT.pLastBatch && renderT.pLastBatch->sortID == sortID )
  				{
  					AddSurfaceToBatch( renderT, renderT.pLastNode, renderT.pLastBatch, surfID );
  				}
  				else
  				{
  					// save it off for sorting, then a later append
  					int sortIndex = sortList.AddToTail();
  					sortList[sortIndex].surfID = surfID;
  				}
  			}
  		}
  
  		// We've got surfaces on this node that can't be added to the previous node
  		if ( sortList.Count() )
  		{
  			// sort by material
  			sortList.Sort( SurfaceCmp );
  			
  			// add a new sort group
  			AddTransNode( renderT );
  			int lastSortID = -1;
  			// now add the optimal number of batches to that group
  			for ( int i = 0; i < sortList.Count(); i++ )
  			{
  				surfID = sortList[i].surfID;
  				int sortID = MSurf_MaterialSortID( surfID );
  				if ( lastSortID == sortID )
  				{
  					// can be drawn in a single call with the current list of surfaces, append
  					AddSurfaceToBatch( renderT, renderT.pLastNode, renderT.pLastBatch, surfID );
  				}
  				else
  				{
  					// requires a break (material/lightmap change).
  					AddTransBatch( renderT, surfID );
  					lastSortID = sortID;
  				}
  			}
  
  			// don't batch across decals or decals will sort incorrectly
  			if ( renderT.pLastNode->decalSurfaceCount )
  			{
  				renderT.pLastNode = NULL;
  				renderT.pLastBatch = NULL;
  			}
  		}
  
  		// front side
  		BuildTransLists_r( renderT, model, node->children[side]);
  	}
  
  	void DrawTransLists( transrender_t &renderT, void *pProxyData )
  	{
  		CMatRenderContextPtr pRenderContext( materials );
  
  		PIXEVENT( pRenderContext, "DrawTransLists" );
  
  		bool skipLight = false;
  		if ( g_pMaterialSystemConfig->nFullbright == 1 )
  		{
  			pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  			skipLight = true;
  		}
  
  		float pOldColor[4];
  
  
  		for ( int i = 0; i < renderT.nodeCount; i++ )
  		{
  			int j;
  			const transnode_t &node = renderT.nodes[i];
  			for ( j = 0; j < node.batchCount; j++ )
  			{
  				const transbatch_t &batch = renderT.batches[node.firstBatch+j];
  				
  #ifdef NEWMESH
  				CIndexBufferBuilder indexBufferBuilder;
  #else
  				CMeshBuilder meshBuilder;
  #endif
  				IMaterial *pMaterial = batch.pMaterial;
  
  				ModulateMaterial( pMaterial, pOldColor );
  
  				if ( !skipLight )
  				{
  					pRenderContext->BindLightmapPage( materialSortInfoArray[batch.sortID].lightmapPageID );
  				}
  				pRenderContext->Bind( pMaterial, pProxyData );
  
  #ifdef NEWMESH
  				IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer( MATERIAL_INDEX_FORMAT_16BIT, false );
  				indexBufferBuilder.Begin( pBuildIndexBuffer, batch.indexCount );
  #else
  				IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[batch.sortID], NULL, NULL );
  				meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, batch.indexCount );
  #endif
  
  				for ( int k = 0; k < batch.surfaceCount; k++ )
  				{
  					SurfaceHandle_t surfID = renderT.surfaces[batch.firstSurface + k];
  					Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  #ifdef NEWMESH
  					BuildIndicesForSurface( indexBufferBuilder, surfID );
  #else
  					BuildIndicesForSurface( meshBuilder, surfID );
  #endif
  
  				}
  #ifdef NEWMESH
  				indexBufferBuilder.End( false ); // haven't tested this one yet (alpha blended world geom I think)
  				// FIXME: IMaterial::GetVertexFormat() should do this stripping (add a separate 'SupportsCompression' accessor)
  				VertexFormat_t vertexFormat = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
  				pRenderContext->BindVertexBuffer( 0, g_WorldStaticMeshes[batch.sortID], 0, vertexFormat );
  				pRenderContext->BindIndexBuffer( pBuildIndexBuffer, 0 );
  				pRenderContext->Draw( MATERIAL_TRIANGLES, 0, batch.indexCount );
  #else
  				meshBuilder.End( false, true );
  #endif
  
  				// Don't leave the material in a bogus state
  				UnModulateMaterial( pMaterial, pOldColor );
  			}
  
  			if ( node.decalSurfaceCount )
  			{
  				for ( j = 0; j < node.decalSurfaceCount; j++ )
  				{
  					SurfaceHandle_t surfID = renderT.decalSurfaces[node.firstDecalSurface + j];
  					Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  					if( SurfaceHasDecals( surfID ) )
  					{
  						DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP );
  					}
  
  					// Add shadows too....
  					ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID );
  					if (decalHandle != SHADOW_DECAL_HANDLE_INVALID)
  					{
  						g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle );
  					}
  				}
  
  				// Now draw the decals + shadows for this node
  				// This order relative to the surfaces is important for translucency to work correctly.
  				DecalSurfaceDraw(pRenderContext, BRUSHMODEL_DECAL_SORT_GROUP);
  
  				// FIXME: Decals are not being rendered while illuminated by the flashlight
  				DecalSurfacesInit( true );
  
  				// Draw all shadows on the brush
  				g_pShadowMgr->RenderProjectedTextures( );
  			}
  			if ( g_ShaderDebug.anydebug )
  			{
  				CUtlVector<msurface2_t *> brushList;
  				for ( j = 0; j < node.batchCount; j++ )
  				{
  					const transbatch_t &batch = renderT.batches[node.firstBatch+j];
  					for ( int k = 0; k < batch.surfaceCount; k++ )
  					{
  						brushList.AddToTail( renderT.surfaces[batch.firstSurface + k] );
  					}
  				}
  				DrawDebugInformation( brushList );
  			}
  		}
  	}
  
  	static int __cdecl SurfaceCmp(const surfacelist_t *s0, const surfacelist_t *s1 );
  
  	void LevelInit();
  	brushrender_t *FindOrCreateRenderBatch( model_t *pModel );
  	void DrawOpaqueBrushModel( IClientEntity *baseentity, model_t *model, const Vector& origin, ERenderDepthMode DepthMode );
  	void DrawTranslucentBrushModel( IClientEntity *baseentity, model_t *model, const Vector& origin, bool bShadowDepth, bool bDrawOpaque, bool bDrawTranslucent );
  	void DrawBrushModelShadow( model_t *model, IClientRenderable *pRenderable );
  
  private:
  	void ClearRenderHandles();
  
  	CUtlLinkedList<brushrender_t> m_renderList;
  };
  
  int __cdecl CBrushBatchRender::SurfaceCmp(const surfacelist_t *s0, const surfacelist_t *s1 )
  {
  	int sortID0 = MSurf_MaterialSortID( s0->surfID );
  	int sortID1 = MSurf_MaterialSortID( s1->surfID );
  
  	return sortID0 - sortID1;
  }
  
  
  CBrushBatchRender g_BrushBatchRenderer;
  
  //-----------------------------------------------------------------------------
  // Purpose: This is used when the mat_dxlevel is changed to reset the brush
  //          models.
  //-----------------------------------------------------------------------------
  void R_BrushBatchInit( void )
  {
  	g_BrushBatchRenderer.LevelInit();
  }
  
  void CBrushBatchRender::LevelInit()
  {
  	unsigned short iNext;
  	for( unsigned short i=m_renderList.Head(); i != m_renderList.InvalidIndex(); i=iNext )
  	{
  		iNext = m_renderList.Next(i);
  		m_renderList.Element(i).Free();
  	}
  
  	m_renderList.Purge();
  
  	ClearRenderHandles();
  }
  
  void CBrushBatchRender::ClearRenderHandles( void )
  {
  	for ( int iBrush = 1 ; iBrush < host_state.worldbrush->numsubmodels ; ++iBrush )
  	{
  		char szBrushModel[5]; // inline model names "*1", "*2" etc
  		Q_snprintf( szBrushModel, sizeof( szBrushModel ), "*%i", iBrush );
  		model_t *pModel = modelloader->GetModelForName( szBrushModel, IModelLoader::FMODELLOADER_SERVER );	
  		if ( pModel )
  		{
  			pModel->brush.renderHandle = 0;
  		}
  	}
  }
  
  // Create a compact, optimal list of rendering commands for the opaque parts of a brush model
  // NOTE: This just skips translucent surfaces assuming a separate transrender_t pass!
  CBrushBatchRender::brushrender_t *CBrushBatchRender::FindOrCreateRenderBatch( model_t *pModel )
  {
  	if ( !pModel->brush.nummodelsurfaces )
  		return NULL;
  
  	unsigned short index = pModel->brush.renderHandle - 1;
  
  	if ( m_renderList.IsValidIndex( index ) )
  		return &m_renderList.Element(index);
  
  	index = m_renderList.AddToTail();
  	pModel->brush.renderHandle = index + 1;
  	brushrender_t &renderT = m_renderList.Element(index);
  	renderT.pPlanes = NULL;
  	renderT.pMeshes = NULL;
  	renderT.planeCount = 0;
  	renderT.meshCount = 0;
  	renderT.totalIndexCount = 0;
  	renderT.totalVertexCount = 0;
  
  	CUtlVector<cplane_t *>	planeList;
  	CUtlVector<surfacelist_t> surfaceList;
  
  	int i;
  
  	SurfaceHandle_t surfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface, pModel->brush.pShared );
  	for (i=0 ; i<pModel->brush.nummodelsurfaces; i++, surfID++)
  	{
  		// UNDONE: For now, just draw these in a separate pass
  		if ( MSurf_Flags(surfID) & SURFDRAW_TRANS )
  			continue;
  
  		cplane_t *plane = surfID->plane;
  		int planeIndex = planeList.Find(plane);
  		if ( planeIndex == -1 )
  		{
  			planeIndex = planeList.AddToTail( plane );
  		}
  		surfacelist_t tmp;
  		tmp.surfID = surfID;
  		tmp.surfaceIndex = i;
  		tmp.planeIndex = planeIndex;
  		surfaceList.AddToTail( tmp );
  	}
  	surfaceList.Sort( SurfaceCmp );
  	renderT.pPlanes = new cplane_t *[planeList.Count()];
  	renderT.planeCount = planeList.Count();
  	memcpy( renderT.pPlanes, planeList.Base(), sizeof(cplane_t *)*planeList.Count() );
  	renderT.pSurfaces = new brushrendersurface_t[surfaceList.Count()];
  	renderT.surfaceCount = surfaceList.Count();
  
  	int meshCount = 0;
  	int batchCount = 0;
  	int lastSortID = -1;
  #ifdef NEWMESH
  	IVertexBuffer *pLastVertexBuffer = NULL;
  #else
  	IMesh *pLastMesh = NULL;
  #endif
  	brushrendermesh_t *pMesh = NULL;
  	brushrendermesh_t tmpMesh[MAX_VERTEX_FORMAT_CHANGES];
  	brushrenderbatch_t *pBatch = NULL;
  	brushrenderbatch_t tmpBatch[128];
  
  	for ( i = 0; i < surfaceList.Count(); i++ )
  	{
  		renderT.pSurfaces[i].surfaceIndex = surfaceList[i].surfaceIndex;
  		renderT.pSurfaces[i].planeIndex = surfaceList[i].planeIndex;
  
  		surfID = surfaceList[i].surfID;
  		int sortID = MSurf_MaterialSortID( surfID );
  #ifdef NEWMESH
  		if ( g_WorldStaticMeshes[sortID] != pLastVertexBuffer )
  #else
  		if ( g_WorldStaticMeshes[sortID] != pLastMesh )
  #endif
  		{
  			pMesh = tmpMesh + meshCount;
  			pMesh->firstBatch = batchCount;
  			pMesh->batchCount = 0;
  			lastSortID = -1; // force a new batch
  			meshCount++;
  		}
  		if ( sortID != lastSortID )
  		{
  			pBatch = tmpBatch + batchCount;
  			pBatch->firstSurface = i;
  			pBatch->surfaceCount = 0;
  			pBatch->sortID = sortID;
  			pBatch->pMaterial = MSurf_TexInfo( surfID )->material;
  			pBatch->indexCount = 0;
  			pMesh->batchCount++;
  			batchCount++;
  		}
  #ifdef NEWMESH
  		pLastVertexBuffer = g_WorldStaticMeshes[sortID];
  #else
  		pLastMesh = g_WorldStaticMeshes[sortID];
  #endif
  		lastSortID = sortID;
  		pBatch->surfaceCount++;
  		int vertCount = MSurf_VertCount( surfID );
  		int indexCount = (vertCount - 2) * 3;
  		pBatch->indexCount += indexCount;
  		renderT.totalIndexCount += indexCount;
  		renderT.totalVertexCount += vertCount;
  	}
  
  	renderT.pMeshes = new brushrendermesh_t[meshCount];
  	memcpy( renderT.pMeshes, tmpMesh, sizeof(brushrendermesh_t) * meshCount );
  	renderT.meshCount = meshCount;
  	renderT.pBatches = new brushrenderbatch_t[batchCount];
  	memcpy( renderT.pBatches, tmpBatch, sizeof(brushrenderbatch_t) * batchCount );
  	renderT.batchCount = batchCount;
  	return &renderT;
  }
  
  
  //-----------------------------------------------------------------------------
  // Draws an opaque (parts of a) brush model
  //-----------------------------------------------------------------------------
  void CBrushBatchRender::DrawOpaqueBrushModel( IClientEntity *baseentity, model_t *model, const Vector& origin, ERenderDepthMode DepthMode )
  {
  	VPROF( "R_DrawOpaqueBrushModel" );
  	SurfaceHandle_t firstSurfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  
  	brushrender_t *pRender = FindOrCreateRenderBatch( model );
  	int i;
  	if ( !pRender )
  		return;
  
  	bool skipLight = false;
  	CMatRenderContextPtr pRenderContext( materials );
  
  	PIXEVENT( pRenderContext, "DrawOpaqueBrushModel" );
  
  	if ( (g_pMaterialSystemConfig->nFullbright == 1) || DepthMode == DEPTH_MODE_SHADOW )
  	{
  		pRenderContext->BindLightmapPage( MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP );
  		skipLight = true;
  	}
  
  	void *pProxyData = baseentity ? baseentity->GetClientRenderable() : NULL;
  	bool backface[1024];
  	Assert( pRender->planeCount < 1024 );
  
  	// NOTE: Backface culling is almost no perf gain.  Can be removed from brush model rendering.
  	// Check the shared planes once
  	for ( i = 0; i < pRender->planeCount; i++ )
  	{
  		float dot = DotProduct( modelorg, pRender->pPlanes[i]->normal) - pRender->pPlanes[i]->dist;
  		backface[i] = ( DepthMode == DEPTH_MODE_NORMAL && ( dot < BACKFACE_EPSILON ) ) ? true : false; // don't backface cull when rendering to shadow map
  	}
  
  	float pOldColor[4];
  
  	for ( i = 0; i < pRender->meshCount; i++ )
  	{
  		brushrendermesh_t &mesh = pRender->pMeshes[i];
  		for ( int j = 0; j < mesh.batchCount; j++ )
  		{
  			brushrenderbatch_t &batch = pRender->pBatches[mesh.firstBatch + j];
  
  			int k;
  			for ( k = 0; k < batch.surfaceCount; k++ )
  			{
  				brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k];
  				if ( !backface[surface.planeIndex] )
  					break;
  			}
  
  			if ( k == batch.surfaceCount )
  				continue;
  
  			CMeshBuilder meshBuilder;
  			IMaterial *pMaterial = NULL;
  
  			if ( DepthMode != DEPTH_MODE_NORMAL )
  			{
  				// Select proper override material
  				int nAlphaTest = (int) batch.pMaterial->IsAlphaTested();
  				int nNoCull = (int) batch.pMaterial->IsTwoSided();
  				IMaterial *pDepthWriteMaterial;
  				
  				if ( DepthMode == DEPTH_MODE_SSA0 )
  				{
  					pDepthWriteMaterial = g_pMaterialSSAODepthWrite[nAlphaTest][nNoCull];
  				}
  				else
  				{
  					pDepthWriteMaterial = g_pMaterialDepthWrite[nAlphaTest][nNoCull];
  				}
  
  				if ( nAlphaTest == 1 )
  				{
  					static unsigned int originalTextureVarCache = 0;
  					IMaterialVar *pOriginalTextureVar = batch.pMaterial->FindVarFast( "$basetexture", &originalTextureVarCache );
  					static unsigned int originalTextureFrameVarCache = 0;
  					IMaterialVar *pOriginalTextureFrameVar = batch.pMaterial->FindVarFast( "$frame", &originalTextureFrameVarCache );
  					static unsigned int originalAlphaRefCache = 0;
  					IMaterialVar *pOriginalAlphaRefVar = batch.pMaterial->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
  
  					static unsigned int textureVarCache = 0;
  					IMaterialVar *pTextureVar = pDepthWriteMaterial->FindVarFast( "$basetexture", &textureVarCache );
  					static unsigned int textureFrameVarCache = 0;
  					IMaterialVar *pTextureFrameVar = pDepthWriteMaterial->FindVarFast( "$frame", &textureFrameVarCache );
  					static unsigned int alphaRefCache = 0;
  					IMaterialVar *pAlphaRefVar = pDepthWriteMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
  
  					if( pTextureVar && pOriginalTextureVar )
  					{
  						pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
  					}
  
  					if( pTextureFrameVar && pOriginalTextureFrameVar )
  					{
  						pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
  					}
  
  					if( pAlphaRefVar && pOriginalAlphaRefVar )
  					{
  						pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
  					}
  				}
  
  				pMaterial = pDepthWriteMaterial;
  			}
  			else
  			{
  				pMaterial = batch.pMaterial;
  
  				// Store off the old color + alpha
  				ModulateMaterial( pMaterial, pOldColor );
  				if ( !skipLight )
  				{
  					pRenderContext->BindLightmapPage( materialSortInfoArray[batch.sortID].lightmapPageID );
  				}
  			}
  
  			pRenderContext->Bind( pMaterial, pProxyData );
  #ifdef NEWMESH
  			IIndexBuffer *pBuildIndexBuffer = pRenderContext->GetDynamicIndexBuffer( MATERIAL_INDEX_FORMAT_16BIT, false );
  			CIndexBufferBuilder indexBufferBuilder;
  			indexBufferBuilder.Begin( pBuildIndexBuffer, batch.indexCount );
  #else
  			IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false, g_WorldStaticMeshes[batch.sortID], NULL, NULL );
  			meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 0, batch.indexCount );
  #endif
  
  			for ( ; k < batch.surfaceCount; k++ )
  			{
  				brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k];
  				if ( backface[surface.planeIndex] )
  					continue;
  				SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
  
  				Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  #ifdef NEWMESH
  				BuildIndicesForSurface( indexBufferBuilder, surfID );
  #else
  				BuildIndicesForSurface( meshBuilder, surfID );
  #endif
  
  				if( SurfaceHasDecals( surfID ) && DepthMode == DEPTH_MODE_NORMAL )
  				{
  					DecalSurfaceAdd( surfID, BRUSHMODEL_DECAL_SORT_GROUP );
  				}
  
  				// Add overlay fragments to list.
  				// FIXME: A little code support is necessary to get overlays working on brush models
  				//	OverlayMgr()->AddFragmentListToRenderList( MSurf_OverlayFragmentList( surfID ), false );
  
  				if ( DepthMode == DEPTH_MODE_NORMAL )
  				{
  					// Add render-to-texture shadows too....
  					ShadowDecalHandle_t decalHandle = MSurf_ShadowDecals( surfID );
  					if (decalHandle != SHADOW_DECAL_HANDLE_INVALID)
  					{
  						g_pShadowMgr->AddShadowsOnSurfaceToRenderList( decalHandle );
  					}
  				}
  			}
  			
  #ifdef NEWMESH
  			indexBufferBuilder.End( false ); // this one is broken (opaque brush model. .tv)
  			pRenderContext->BindVertexBuffer( 0, g_WorldStaticMeshes[batch.sortID], 0, g_WorldStaticMeshes[batch.sortID]->GetVertexFormat() );
  			pRenderContext->BindIndexBuffer( pBuildIndexBuffer, 0 );
  			pRenderContext->Draw( MATERIAL_TRIANGLES, 0, pBuildIndexBuffer->NumIndices() );//batch.indexCount );
  #else
  			meshBuilder.End( false, true );
  #endif
  
  			if ( DepthMode == DEPTH_MODE_NORMAL )
  			{
  				// Don't leave the material in a bogus state
  				UnModulateMaterial( pMaterial, pOldColor );
  			}
  		}
  	}
  
  	if ( DepthMode != DEPTH_MODE_NORMAL )
  	{
  		return;
  	}
  
  	if ( g_ShaderDebug.anydebug )
  	{
  		for ( i = 0; i < pRender->meshCount; i++ )
  		{
  			brushrendermesh_t &mesh = pRender->pMeshes[i];
  			CUtlVector<msurface2_t *> brushList;
  			for ( int j = 0; j < mesh.batchCount; j++ )
  			{
  				brushrenderbatch_t &batch = pRender->pBatches[mesh.firstBatch + j];
  				for ( int k = 0; k < batch.surfaceCount; k++ )
  				{
  					brushrendersurface_t &surface = pRender->pSurfaces[batch.firstSurface + k];
  					if ( backface[surface.planeIndex] )
  						continue;
  					SurfaceHandle_t surfID = firstSurfID + surface.surfaceIndex;
  					brushList.AddToTail(surfID);
  				}
  			}
  			// now draw debug for each drawn surface
  			DrawDebugInformation( brushList );
  		}
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Draws an translucent (sorted) brush model
  //-----------------------------------------------------------------------------
  void CBrushBatchRender::DrawTranslucentBrushModel( IClientEntity *baseentity, model_t *model, const Vector& origin, bool bShadowDepth, bool bDrawOpaque, bool bDrawTranslucent )
  {
  	if ( bDrawOpaque )
  	{
  		DrawOpaqueBrushModel( baseentity, model, origin, bShadowDepth ? DEPTH_MODE_SHADOW : DEPTH_MODE_NORMAL );
  	}
  
  	if ( !bShadowDepth && bDrawTranslucent )
  	{
  		DrawTranslucentBrushModel( model, baseentity );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: Draws a brush model shadow for render-to-texture shadows
  //-----------------------------------------------------------------------------
  // UNDONE: This is reasonable, but it could be much faster as follows:
  // Build a vertex buffer cache.  A block-allocated static mesh with 1024 verts
  // per block or something.
  // When a new brush is encountered, fill it in to the current block or the 
  // next one (first fit allocator).  Then this routine could simply draw
  // a static mesh with a single index buffer build, draw call (no dynamic vb).
  void CBrushBatchRender::DrawBrushModelShadow( model_t *model, IClientRenderable *pRenderable )
  {
  	brushrender_t *pRender = FindOrCreateRenderBatch( (model_t *)model );
  	if ( !pRender )
  		return;
  
  	CMatRenderContextPtr pRenderContext( materials );
  
  	pRenderContext->Bind( g_pMaterialShadowBuild, pRenderable );
  
  	// Draws all surfaces in the brush model in arbitrary order
  	SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  	IMesh *pMesh = pRenderContext->GetDynamicMesh();
  	CMeshBuilder meshBuilder;
  	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, pRender->totalVertexCount, pRender->totalIndexCount );
  
  	for ( int i=0 ; i<model->brush.nummodelsurfaces ; i++, surfID++)
  	{
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  
  		if ( MSurf_Flags(surfID) & SURFDRAW_TRANS )
  			continue;
  
  		int startVert = MSurf_FirstVertIndex( surfID );
  		int vertCount = MSurf_VertCount( surfID );
  		int startIndex = meshBuilder.GetCurrentVertex();
  		int j;
  		for ( j = 0; j < vertCount; j++ )
  		{
  			int vertIndex = model->brush.pShared->vertindices[startVert + j];
  
  			// world-space vertex
  			meshBuilder.Position3fv( model->brush.pShared->vertexes[vertIndex].position.Base() );
  			meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  			meshBuilder.AdvanceVertex();
  		}
  
  		for ( j = 0; j < vertCount-2; j++ )
  		{
  			meshBuilder.FastIndex( startIndex );
  			meshBuilder.FastIndex( startIndex + j + 1 );
  			meshBuilder.FastIndex( startIndex + j + 2 );
  		}
  	}
  	meshBuilder.End();
  	pMesh->Draw();
  }
  
  void R_Surface_LevelInit()
  {
  	g_BrushBatchRenderer.LevelInit();
  	// reset this to the default at the start of each level
  	g_MaxLeavesVisible = 512;
  }
  
  
  void R_Surface_LevelShutdown()
  {
  	CWorldRenderList::PurgeAll();
  }
  
  //-----------------------------------------------------------------------------
  static void R_DrawBrushModel_Override( IClientEntity *baseentity, model_t *model, const Vector& origin )
  {
  	VPROF( "R_DrawOpaqueBrushModel_Override" );
  	SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  	for (int i=0 ; i<model->brush.nummodelsurfaces ; i++, surfID++)
  	{
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  
  		Shader_BrushSurface( surfID, model, baseentity );
  	}
  	// now draw debug for each drawn surface
  	if ( g_ShaderDebug.anydebug )
  	{
  		CUtlVector<msurface2_t *> surfaceList;
  		surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  		for (int i=0 ; i<model->brush.nummodelsurfaces ; i++, surfID++)
  		{
  			surfaceList.AddToTail(surfID);
  		}
  		DrawDebugInformation( surfaceList );
  	}
  }
  
  int R_MarkDlightsOnBrushModel( model_t *model, IClientRenderable *pRenderable )
  {
  	int count = 0;
  	if ( g_bActiveDlights )
  	{
  		extern int R_MarkLights (dlight_t *light, int bit, mnode_t *node);
  
  		g_BrushToWorldMatrix.SetupMatrixOrgAngles( pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
  		Vector saveOrigin;
  
  		for (int k=0 ; k<MAX_DLIGHTS ; k++)
  		{
  			if ((cl_dlights[k].die < cl.GetTime()) ||
  				(!cl_dlights[k].IsRadiusGreaterThanZero()))
  				continue;
  
  			VectorCopy( cl_dlights[k].origin, saveOrigin );
  			cl_dlights[k].origin = g_BrushToWorldMatrix.VMul4x3Transpose( saveOrigin );
  
  			mnode_t *node = model->brush.pShared->nodes + model->brush.firstnode;
  			if ( IsBoxIntersectingSphereExtents( node->m_vecCenter, node->m_vecHalfDiagonal, cl_dlights[k].origin, cl_dlights[k].GetRadius() ) )
  			{
  				count += R_MarkLights( &cl_dlights[k], 1<<k, node );
  			}
  			VectorCopy( saveOrigin, cl_dlights[k].origin );
  		}
  		if ( count )
  		{
  			model->flags |= MODELFLAG_HAS_DLIGHT;
  		}
  		g_BrushToWorldMatrix.Identity();
  	}
  	return count;
  }
  
  
  //-----------------------------------------------------------------------------
  // Stuff to do right before and after brush model rendering
  //-----------------------------------------------------------------------------
  void Shader_BrushBegin( model_t *model, IClientEntity *baseentity /*=NULL*/ )
  {
  	// Clear out the render list of decals
  	DecalSurfacesInit( true );
  
  	// Clear out the render lists of shadows
  	g_pShadowMgr->ClearShadowRenderList( );
  }
  
  void Shader_BrushEnd( IMatRenderContext *pRenderContext, VMatrix const* pBrushToWorld, model_t *model, bool bShadowDepth, IClientEntity *baseentity /* = NULL */ )
  {
  	if ( bShadowDepth )
  		return;
  
  	DecalSurfaceDraw(pRenderContext, BRUSHMODEL_DECAL_SORT_GROUP);
  
  	// draw the flashlight lighting for the decals on the brush.
  	g_pShadowMgr->DrawFlashlightDecals( BRUSHMODEL_DECAL_SORT_GROUP, false );
  
  	// Draw all shadows on the brush
  	g_pShadowMgr->RenderProjectedTextures( pBrushToWorld );
  }
  
  class CBrushModelTransform
  {
  public:
  	CBrushModelTransform( const Vector &origin, const QAngle &angles, IMatRenderContext *pRenderContext )
  	{
  		bool rotated = ( angles[0] || angles[1] || angles[2] );
  		m_bIdentity = (origin == vec3_origin) && (!rotated);
  
  		// Don't change state if we don't need to
  		if (!m_bIdentity)
  		{
  			m_savedModelorg = modelorg;
  			pRenderContext->MatrixMode( MATERIAL_MODEL );
  			pRenderContext->PushMatrix();
  			g_BrushToWorldMatrix.SetupMatrixOrgAngles( origin, angles );
  			pRenderContext->LoadMatrix( g_BrushToWorldMatrix );
  			modelorg = g_BrushToWorldMatrix.VMul4x3Transpose(g_EngineRenderer->ViewOrigin());
  		}
  	}
  	~CBrushModelTransform()
  	{
  		if ( !m_bIdentity )
  		{
  			CMatRenderContextPtr pRenderContext( materials );
  			pRenderContext->MatrixMode( MATERIAL_MODEL );
  			pRenderContext->PopMatrix();
  			g_BrushToWorldMatrix.Identity();
  			modelorg = m_savedModelorg;
  		}
  	}
  
  	VMatrix *GetNonIdentityMatrix()
  	{
  		return m_bIdentity ? NULL : &g_BrushToWorldMatrix;
  	}
  
  	inline bool IsIdentity() { return m_bIdentity; }
  
  	Vector	m_savedModelorg;
  	bool	m_bIdentity;
  };
  
  //-----------------------------------------------------------------------------
  // Purpose: Draws a brush model using the global shader/surfaceVisitor
  // Input  : *e - entity to draw
  // Output : void R_DrawBrushModel
  //-----------------------------------------------------------------------------
  void R_DrawBrushModel( IClientEntity *baseentity, model_t *model, 
  	const Vector& origin, const QAngle& angles, ERenderDepthMode DepthMode, bool bDrawOpaque, bool bDrawTranslucent )
  {
  	VPROF( "R_DrawBrushModel" );
  
  #ifdef USE_CONVARS
  	if ( !r_drawbrushmodels.GetInt() )
  	{
  		return;
  	}
  	bool bWireframe = false;
  	if ( r_drawbrushmodels.GetInt() == 2 )
  	{
  		// save and override
  		bWireframe = g_ShaderDebug.wireframe;
  		g_ShaderDebug.wireframe = true;
  		g_ShaderDebug.anydebug = true;
  	}
  #endif
  
  	CMatRenderContextPtr pRenderContext( materials );
  	CBrushModelTransform brushTransform( origin, angles, pRenderContext );
  
  	Assert(model->brush.firstmodelsurface != 0);
  
  	// Draw the puppy...
  	Shader_BrushBegin( model, baseentity );
  	
  	if ( model->flags & MODELFLAG_FRAMEBUFFER_TEXTURE )
  	{
  		CMatRenderContextPtr pRenderContextMat( materials );
  		pRenderContext->CopyRenderTargetToTexture( pRenderContextMat->GetFrameBufferCopyTexture( 0 ) );
  	}
  
  	if ( s_pBrushRenderOverride )
  	{
  		R_DrawBrushModel_Override( baseentity, model, origin );
  	}
  	else
  	{
  		if ( model->flags & MODELFLAG_TRANSLUCENT )
  		{
  			if ( DepthMode == DEPTH_MODE_NORMAL )
  			{
  				g_BrushBatchRenderer.DrawTranslucentBrushModel( baseentity, model, origin, false, bDrawOpaque, bDrawTranslucent );
  			}
  		}
  		else if ( bDrawOpaque )
  		{
  			g_BrushBatchRenderer.DrawOpaqueBrushModel( baseentity, model, origin, DepthMode );
  		}
  	}
  
  	Shader_BrushEnd( pRenderContext, brushTransform.GetNonIdentityMatrix(), model, DepthMode != DEPTH_MODE_NORMAL, baseentity );
  
  #ifdef USE_CONVARS
  	if ( r_drawbrushmodels.GetInt() == 2 )
  	{
  		// restore
  		g_ShaderDebug.wireframe = bWireframe;
  		g_ShaderDebug.TestAnyDebug();
  	}
  #endif
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: Draws a brush model shadow for render-to-texture shadows
  //-----------------------------------------------------------------------------
  
  void R_DrawBrushModelShadow( IClientRenderable *pRenderable )
  {
  	if( !r_drawbrushmodels.GetInt() )
  		return;
  
  	model_t *model = (model_t *)pRenderable->GetModel();
  	const Vector& origin = pRenderable->GetRenderOrigin();
  	QAngle const& angles = pRenderable->GetRenderAngles();
  
  	CMatRenderContextPtr pRenderContext( materials );
  	CBrushModelTransform brushTransform( origin, angles, pRenderContext );
  	g_BrushBatchRenderer.DrawBrushModelShadow( model, pRenderable );
  }
  
  
  void R_DrawIdentityBrushModel( IWorldRenderList *pRenderListIn, model_t *model )
  {
  	if ( !model )
  		return;
  
  	CWorldRenderList *pRenderList = assert_cast<CWorldRenderList *>(pRenderListIn);
  
  	SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
  	
  	for (int j=0 ; j<model->brush.nummodelsurfaces ; ++j, surfID++)
  	{
  		Assert( !(MSurf_Flags( surfID ) & SURFDRAW_NODRAW) );
  
  		// FIXME: Can't insert translucent stuff into the list
  		// of translucent surfaces because we don't know what leaf
  		// we're in. At the moment, the client doesn't add translucent
  		// brushes to the identity brush model list
  //		Assert ( (psurf->flags & SURFDRAW_TRANS ) == 0 );
  
  		// OPTIMIZE: Backface cull these guys?!?!?
  		if ( MSurf_Flags( surfID ) & SURFDRAW_TRANS)
  //		if ( psurf->texinfo->material->IsTranslucent() )
  		{
  			Shader_TranslucentWorldSurface( pRenderList, surfID );
  		}
  		else
  		{
  			Shader_WorldSurface( pRenderList, surfID );
  		}
  	}
  }
  #endif
  //-----------------------------------------------------------------------------
  // Converts leaf pointer to index
  //-----------------------------------------------------------------------------
  inline int LeafToIndex( mleaf_t* pLeaf )
  {
  	return pLeaf - host_state.worldbrush->leafs;
  }
  
  
  //-----------------------------------------------------------------------------
  // Structures to help out with enumeration
  //-----------------------------------------------------------------------------
  enum
  {
  	ENUM_SPHERE_TEST_X = 0x1,
  	ENUM_SPHERE_TEST_Y = 0x2,
  	ENUM_SPHERE_TEST_Z = 0x4,
  
  	ENUM_SPHERE_TEST_ALL = 0x7
  };
  
  struct EnumLeafBoxInfo_t
  {
  	VectorAligned m_vecBoxMax;
  	VectorAligned m_vecBoxMin;
  	VectorAligned m_vecBoxCenter;
  	VectorAligned m_vecBoxHalfDiagonal;
  	ISpatialLeafEnumerator *m_pIterator;
  	int	m_nContext;
  };
  
  struct EnumLeafSphereInfo_t
  {
  	Vector m_vecCenter;
  	float m_flRadius;
  	Vector m_vecBoxCenter;
  	Vector m_vecBoxHalfDiagonal;
  	ISpatialLeafEnumerator *m_pIterator;
  	int	m_nContext;
  };
  
  //-----------------------------------------------------------------------------
  // Finds all leaves of the BSP tree within a particular volume
  //-----------------------------------------------------------------------------
  static bool EnumerateLeafInBox_R(mnode_t *node, EnumLeafBoxInfo_t& info )
  {
  	// no polygons in solid nodes (don't report these leaves either)
  	if (node->contents == CONTENTS_SOLID)
  		return true;		// solid
  
  	// rough cull...
  	if (!IsBoxIntersectingBoxExtents(node->m_vecCenter, node->m_vecHalfDiagonal, 
  		info.m_vecBoxCenter, info.m_vecBoxHalfDiagonal))
  	{
  		return true;
  	}
  	
  	if (node->contents >= 0)
  	{
  		// if a leaf node, report it to the iterator...
  		return info.m_pIterator->EnumerateLeaf( LeafToIndex( (mleaf_t *)node ), info.m_nContext ); 
  	}
  
  	// Does the node plane split the box?
  	// find which side of the node we are on
  	cplane_t* plane = node->plane;
  	if ( plane->type <= PLANE_Z )
  	{
  		if (info.m_vecBoxMax[plane->type] <= plane->dist)
  		{
  			return EnumerateLeafInBox_R( node->children[1], info );
  		}
  		else if (info.m_vecBoxMin[plane->type] >= plane->dist)
  		{
  			return EnumerateLeafInBox_R( node->children[0], info );
  		}
  		else
  		{
  			// Here the box is split by the node
  			bool ret = EnumerateLeafInBox_R( node->children[0], info );
  			if (!ret)
  				return false;
  
  			return EnumerateLeafInBox_R( node->children[1], info );
  		}
  	}
  
  	// Arbitrary split plane here
  	Vector cornermin, cornermax;
  	for (int i = 0; i < 3; ++i)
  	{
  		if (plane->normal[i] >= 0)
  		{
  			cornermin[i] = info.m_vecBoxMin[i];
  			cornermax[i] = info.m_vecBoxMax[i];
  		}
  		else
  		{
  			cornermin[i] = info.m_vecBoxMax[i];
  			cornermax[i] = info.m_vecBoxMin[i];
  		}
  	}
  
  	if (DotProduct( plane->normal, cornermax ) <= plane->dist)
  	{
  		return EnumerateLeafInBox_R( node->children[1], info );
  	}
  	else if (DotProduct( plane->normal, cornermin ) >= plane->dist)
  	{
  		return EnumerateLeafInBox_R( node->children[0], info );
  	}
  	else
  	{
  		// Here the box is split by the node
  		bool ret = EnumerateLeafInBox_R( node->children[0], info );
  		if (!ret)
  			return false;
  
  		return EnumerateLeafInBox_R( node->children[1], info );
  	}
  }
  
  #ifdef _X360
  
  static fltx4 AlignThatVector(const Vector &vc)
  {
  	fltx4 out = __loadunalignedvector(vc.Base());
  
  	/*
  	out.x = vc.x;
  	out.y = vc.y;
  	out.z = vc.z;
  	*/
  
  	// squelch the w component 
  	return __vrlimi( out, __vzero(), 1, 0 );
  }
  
  //-----------------------------------------------------------------------------
  // Finds all leaves of the BSP tree within a particular volume
  //-----------------------------------------------------------------------------
  static bool EnumerateLeafInBox_R(mnode_t * RESTRICT node, const EnumLeafBoxInfo_t * RESTRICT pInfo )
  {
  	// no polygons in solid nodes (don't report these leaves either)
  	if (node->contents == CONTENTS_SOLID)
  		return true;		// solid
  
  	// speculatively get the children into the cache
  	__dcbt(0,node->children[0]);
  	__dcbt(0,node->children[1]);
  
  	// constructing these here prevents LHS if we spill.
  	// it's not quite a quick enough operation to do extemporaneously.
  	fltx4 infoBoxCenter = LoadAlignedSIMD(pInfo->m_vecBoxCenter);
  	fltx4 infoBoxHalfDiagonal = LoadAlignedSIMD(pInfo->m_vecBoxHalfDiagonal);
  
  	Assert(IsBoxIntersectingBoxExtents(AlignThatVector(node->m_vecCenter), AlignThatVector(node->m_vecHalfDiagonal), 
  			LoadAlignedSIMD(pInfo->m_vecBoxCenter), LoadAlignedSIMD(pInfo->m_vecBoxHalfDiagonal)) ==
  			IsBoxIntersectingBoxExtents((node->m_vecCenter), node->m_vecHalfDiagonal,
  			pInfo->m_vecBoxCenter, pInfo->m_vecBoxHalfDiagonal));
  
  
  	// rough cull...
  	if (!IsBoxIntersectingBoxExtents(LoadAlignedSIMD(node->m_vecCenter), LoadAlignedSIMD(node->m_vecHalfDiagonal), 
  		infoBoxCenter, infoBoxHalfDiagonal))
  	{
  		return true;
  	}
  
  	if (node->contents >= 0)
  	{
  		// if a leaf node, report it to the iterator...
  		return pInfo->m_pIterator->EnumerateLeaf( LeafToIndex( (mleaf_t *)node ), pInfo->m_nContext ); 
  	}
  
  	// Does the node plane split the box?
  	// find which side of the node we are on
  	cplane_t* RESTRICT plane = node->plane;
  	if ( plane->type <= PLANE_Z )
  	{
  		if (pInfo->m_vecBoxMax[plane->type] <= plane->dist)
  		{
  			return EnumerateLeafInBox_R( node->children[1], pInfo );
  		}
  		else if (pInfo->m_vecBoxMin[plane->type] >= plane->dist)
  		{
  			return EnumerateLeafInBox_R( node->children[0], pInfo );
  		}
  		else
  		{
  			// Here the box is split by the node
  			return EnumerateLeafInBox_R( node->children[0], pInfo ) && 
  				   EnumerateLeafInBox_R( node->children[1], pInfo );
  		}
  	}
  
  	// Arbitrary split plane here
  	/*
  	Vector cornermin, cornermax;
  	for (int i = 0; i < 3; ++i)
  	{
  		if (plane->normal[i] >= 0)
  		{
  			cornermin[i] = info.m_vecBoxMin[i];
  			cornermax[i] = info.m_vecBoxMax[i];
  		}
  		else
  		{
  			cornermin[i] = info.m_vecBoxMax[i];
  			cornermax[i] = info.m_vecBoxMin[i];
  		}
  	}
  	*/
  	
  	// take advantage of high throughput/high latency
  	fltx4 planeNormal = LoadUnaligned3SIMD( plane->normal.Base() );
  	fltx4 vecBoxMin = LoadAlignedSIMD(pInfo->m_vecBoxMin);
  	fltx4 vecBoxMax = LoadAlignedSIMD(pInfo->m_vecBoxMax);
  	fltx4 cornermin, cornermax;
  	// by now planeNormal is ready...
  	fltx4 control = XMVectorGreaterOrEqual( planeNormal, __vzero() );
  	// now control[i] = planeNormal[i] > 0 ? 0xFF : 0x00
  	cornermin = XMVectorSelect( vecBoxMax, vecBoxMin, control); // cornermin[i] = control[i] ? vecBoxMin[i] : vecBoxMax[i]
  	cornermax = XMVectorSelect( vecBoxMin, vecBoxMax, control);
  
  	// compute dot products
  	fltx4 dotCornerMax = __vmsum3fp(planeNormal, cornermax); // vsumfp ignores w component
  	fltx4 dotCornerMin = __vmsum3fp(planeNormal, cornermin);
  	fltx4 vPlaneDist = ReplicateX4(plane->dist);
  	UINT conditionRegister;
  	XMVectorGreaterR(&conditionRegister,vPlaneDist,dotCornerMax);
  	if (XMComparisonAllTrue(conditionRegister)) // plane->normal . cornermax <= plane->dist
  		return EnumerateLeafInBox_R( node->children[1], pInfo );
  
  	XMVectorGreaterOrEqualR(&conditionRegister,dotCornerMin,vPlaneDist);
  	if ( XMComparisonAllTrue(conditionRegister) )
  		return EnumerateLeafInBox_R( node->children[0], pInfo );
  
  	return EnumerateLeafInBox_R( node->children[0], pInfo ) &&
  		   EnumerateLeafInBox_R( node->children[1], pInfo );
  
  	/*
  	if (DotProduct( plane->normal, cornermax ) <= plane->dist)
  	{
  		return EnumerateLeafInBox_R( node->children[1], info, infoBoxCenter, infoBoxHalfDiagonal );
  	}
  	else if (DotProduct( plane->normal, cornermin ) >= plane->dist)
  	{
  		return EnumerateLeafInBox_R( node->children[0], info, infoBoxCenter, infoBoxHalfDiagonal );
  	}
  	else
  	{
  		// Here the box is split by the node
  		bool ret = EnumerateLeafInBox_R( node->children[0], info, infoBoxCenter, infoBoxHalfDiagonal );
  		if (!ret)
  			return false;
  
  		return EnumerateLeafInBox_R( node->children[1], info, infoBoxCenter, infoBoxHalfDiagonal );
  	}
  	*/
  }
  #endif
  
  //-----------------------------------------------------------------------------
  // Returns all leaves that lie within a spherical volume
  //-----------------------------------------------------------------------------
  bool EnumerateLeafInSphere_R( mnode_t *node, EnumLeafSphereInfo_t& info, int nTestFlags )
  {
  	while (true)
  	{
  		// no polygons in solid nodes (don't report these leaves either)
  		if (node->contents == CONTENTS_SOLID)
  			return true;		// solid
  
  		if (node->contents >= 0)
  		{
  			// leaf cull...
  			// NOTE: using nTestFlags here means that we may be passing in some 
  			// leaves that don't actually intersect the sphere, but instead intersect
  			// the box that surrounds the sphere.
  			if (nTestFlags)
  			{
  				if (!IsBoxIntersectingSphereExtents (node->m_vecCenter, node->m_vecHalfDiagonal, info.m_vecCenter, info.m_flRadius))
  					return true;
  			}
  
  			// if a leaf node, report it to the iterator...
  			return info.m_pIterator->EnumerateLeaf( LeafToIndex( (mleaf_t *)node ), info.m_nContext ); 
  		}
  		else if (nTestFlags)
  		{
  			if (node->contents == -1)
  			{
  				// faster cull...
  				if (nTestFlags & ENUM_SPHERE_TEST_X)
  				{
  					float flDelta = FloatMakePositive( node->m_vecCenter.x - info.m_vecBoxCenter.x );
  					float flSize = node->m_vecHalfDiagonal.x + info.m_vecBoxHalfDiagonal.x;
  					if ( flDelta > flSize )
  						return true;
  
  					// This checks for the node being completely inside the box...
  					if ( flDelta + node->m_vecHalfDiagonal.x < info.m_vecBoxHalfDiagonal.x )
  						nTestFlags &= ~ENUM_SPHERE_TEST_X;
  				}
  
  				if (nTestFlags & ENUM_SPHERE_TEST_Y)
  				{
  					float flDelta = FloatMakePositive( node->m_vecCenter.y - info.m_vecBoxCenter.y );
  					float flSize = node->m_vecHalfDiagonal.y + info.m_vecBoxHalfDiagonal.y;
  					if ( flDelta > flSize )
  						return true;
  
  					// This checks for the node being completely inside the box...
  					if ( flDelta + node->m_vecHalfDiagonal.y < info.m_vecBoxHalfDiagonal.y )
  						nTestFlags &= ~ENUM_SPHERE_TEST_Y;
  				}
  
  				if (nTestFlags & ENUM_SPHERE_TEST_Z)
  				{
  					float flDelta = FloatMakePositive( node->m_vecCenter.z - info.m_vecBoxCenter.z );
  					float flSize = node->m_vecHalfDiagonal.z + info.m_vecBoxHalfDiagonal.z;
  					if ( flDelta > flSize )
  						return true;
  														   
  					if ( flDelta + node->m_vecHalfDiagonal.z < info.m_vecBoxHalfDiagonal.z )
  						nTestFlags &= ~ENUM_SPHERE_TEST_Z;
  				}
  			}
  			else if (node->contents == -2)
  			{
  				// If the box is too small to bother with testing, then blat out the flags
  				nTestFlags = 0;
  			}
  		}
  
  		// Does the node plane split the sphere?
  		// find which side of the node we are on
  		float flNormalDotCenter;
  		cplane_t* plane = node->plane;
  		if ( plane->type <= PLANE_Z )
  		{
  			flNormalDotCenter = info.m_vecCenter[plane->type];
  		}
  		else
  		{
  			// Here, we've got a plane which is not axis aligned, so we gotta do more work
  			flNormalDotCenter = DotProduct( plane->normal, info.m_vecCenter );
  		}
  
  		if (flNormalDotCenter + info.m_flRadius <= plane->dist)
  		{
  			node = node->children[1];
  		}
  		else if (flNormalDotCenter - info.m_flRadius >= plane->dist)
  		{
  			node = node->children[0];
  		}
  		else
  		{
  			// Here the box is split by the node
  			if (!EnumerateLeafInSphere_R( node->children[0], info, nTestFlags ))
  				return false;
  
  			node = node->children[1];
  		}
  	}
  }
  
  
  //-----------------------------------------------------------------------------
  // Enumerate leaves along a non-extruded ray
  //-----------------------------------------------------------------------------
  
  static bool EnumerateLeavesAlongRay_R( mnode_t *node, Ray_t const& ray, 
  	float start, float end, ISpatialLeafEnumerator* pEnum, int context )
  {
  	// no polygons in solid nodes (don't report these leaves either)
  	if (node->contents == CONTENTS_SOLID)
  		return true;		// solid, keep recursing
  
  	// didn't hit anything
  	if (node->contents >= 0)
  	{
  		// if a leaf node, report it to the iterator...
  		return pEnum->EnumerateLeaf( LeafToIndex( (mleaf_t *)node ), context ); 
  	}
  	
  	// Determine which side of the node plane our points are on
  	cplane_t* plane = node->plane;
  
  	float startDotN,deltaDotN;
  	if (plane->type <= PLANE_Z)
  	{
  		startDotN = ray.m_Start[plane->type];
  		deltaDotN = ray.m_Delta[plane->type];
  	}
  	else
  	{
  		startDotN = DotProduct( ray.m_Start, plane->normal );
  		deltaDotN = DotProduct( ray.m_Delta, plane->normal );
  	}
  
  	float front = startDotN + start * deltaDotN - plane->dist;
  	float back = startDotN + end * deltaDotN - plane->dist;
  
  	int side = front < 0;
  	
  	// If they're both on the same side of the plane, don't bother to split
  	// just check the appropriate child
  	if ( (back < 0) == side )
  	{
  		return EnumerateLeavesAlongRay_R (node->children[side], ray, start, end, pEnum, context );
  	}
  	
  	// calculate mid point
  	float frac = front / (front - back);
  	float mid = start * (1.0f - frac) + end * frac;
  	
  	// go down front side	
  	bool ok = EnumerateLeavesAlongRay_R (node->children[side], ray, start, mid, pEnum, context );
  	if (!ok)
  		return ok;
  
  	// go down back side
  	return EnumerateLeavesAlongRay_R (node->children[!side], ray, mid, end, pEnum, context );
  }
  
  
  //-----------------------------------------------------------------------------
  // Enumerate leaves along a non-extruded ray
  //-----------------------------------------------------------------------------
  
  static bool EnumerateLeavesAlongExtrudedRay_R( mnode_t *node, Ray_t const& ray, 
  	float start, float end, ISpatialLeafEnumerator* pEnum, int context )
  {
  	// no polygons in solid nodes (don't report these leaves either)
  	if (node->contents == CONTENTS_SOLID)
  		return true;		// solid, keep recursing
  
  	// didn't hit anything
  	if (node->contents >= 0)
  	{
  		// if a leaf node, report it to the iterator...
  		return pEnum->EnumerateLeaf( LeafToIndex( (mleaf_t *)node ), context ); 
  	}
  	
  	// Determine which side of the node plane our points are on
  	cplane_t* plane = node->plane;
  
  	//
  	float t1, t2, offset;
  	float startDotN,deltaDotN;
  	if (plane->type <= PLANE_Z)
  	{
  		startDotN = ray.m_Start[plane->type];
  		deltaDotN = ray.m_Delta[plane->type];
  		offset = ray.m_Extents[plane->type] + DIST_EPSILON;
  	}
  	else
  	{
  		startDotN = DotProduct( ray.m_Start, plane->normal );
  		deltaDotN = DotProduct( ray.m_Delta, plane->normal );
  		offset = fabs(ray.m_Extents[0]*plane->normal[0]) +
  				fabs(ray.m_Extents[1]*plane->normal[1]) +
  				fabs(ray.m_Extents[2]*plane->normal[2]) + DIST_EPSILON;
  	}
  	t1 = startDotN + start * deltaDotN - plane->dist;
  	t2 = startDotN + end * deltaDotN - plane->dist;
  
  	// If they're both on the same side of the plane (further than the trace
  	// extents), don't bother to split, just check the appropriate child
      if (t1 > offset && t2 > offset )
  //	if (t1 >= offset && t2 >= offset)
  	{
  		return EnumerateLeavesAlongExtrudedRay_R( node->children[0], ray,
  			start, end, pEnum, context );
  	}
  	if (t1 < -offset && t2 < -offset)
  	{
  		return EnumerateLeavesAlongExtrudedRay_R( node->children[1], ray,
  			start, end, pEnum, context );
  	}
  
  	// For the segment of the line that we are going to use
  	// to test against the back side of the plane, we're going
  	// to use the part that goes from start to plane + extent
  	// (which causes it to extend somewhat into the front halfspace,
  	// since plane + extent is in the front halfspace).
  	// Similarly, front the segment which tests against the front side,
  	// we use the entire front side part of the ray + a portion of the ray that
  	// extends by -extents into the back side.
  
  	if (fabs(t1-t2) < DIST_EPSILON)
  	{
  		// Parallel case, send entire ray to both children...
  		bool ret = EnumerateLeavesAlongExtrudedRay_R( node->children[0], 
  			ray, start, end, pEnum, context );
  		if (!ret)
  			return false;
  		return EnumerateLeavesAlongExtrudedRay_R( node->children[1],
  			ray, start, end, pEnum, context );
  	}
  	
  	// Compute the two fractions...
  	// We need one at plane + extent and another at plane - extent.
  	// put the crosspoint DIST_EPSILON pixels on the near side
  	float idist, frac2, frac;
  	int side;
  	if (t1 < t2)
  	{
  		idist = 1.0/(t1-t2);
  		side = 1;
  		frac2 = (t1 + offset) * idist;
  		frac = (t1 - offset) * idist;
  	}
  	else if (t1 > t2)
  	{
  		idist = 1.0/(t1-t2);
  		side = 0;
  		frac2 = (t1 - offset) * idist;
  		frac = (t1 + offset) * idist;
  	}
  	else
  	{
  		side = 0;
  		frac = 1;
  		frac2 = 0;
  	}
  
  	// move up to the node
  	frac = clamp( frac, 0.f, 1.f );
  	float midf = start + (end - start)*frac;
  	bool ret = EnumerateLeavesAlongExtrudedRay_R( node->children[side], ray, start, midf, pEnum, context );
  	if (!ret)
  		return ret;
  
  	// go past the node
  	frac2 = clamp( frac2, 0.f, 1.f );
  	midf = start + (end - start)*frac2;
  	return EnumerateLeavesAlongExtrudedRay_R( node->children[!side], ray, midf, end, pEnum, context );
  }
  
  
  //-----------------------------------------------------------------------------
  //
  // Helper class to iterate over leaves
  //
  //-----------------------------------------------------------------------------
  
  class CEngineBSPTree : public IEngineSpatialQuery
  {
  public:
  	// Returns the number of leaves
  	int LeafCount() const;
  
  	// Enumerates the leaves along a ray, box, etc.
  	bool EnumerateLeavesAtPoint( const Vector& pt, ISpatialLeafEnumerator* pEnum, int context );
  	bool EnumerateLeavesInBox( const Vector& mins, const Vector& maxs, ISpatialLeafEnumerator* pEnum, int context );
  	bool EnumerateLeavesInSphere( const Vector& center, float radius, ISpatialLeafEnumerator* pEnum, int context );
  	bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context );
  };
  
  //-----------------------------------------------------------------------------
  // Singleton accessor
  //-----------------------------------------------------------------------------
  
  static CEngineBSPTree s_ToolBSPTree;
  IEngineSpatialQuery* g_pToolBSPTree = &s_ToolBSPTree;
  
  
  //-----------------------------------------------------------------------------
  // Returns the number of leaves
  //-----------------------------------------------------------------------------
  
  int CEngineBSPTree::LeafCount() const
  {
  	return host_state.worldbrush->numleafs;
  }
  
  //-----------------------------------------------------------------------------
  // Enumerates the leaves at a point
  //-----------------------------------------------------------------------------
  
  bool CEngineBSPTree::EnumerateLeavesAtPoint( const Vector& pt, 
  									ISpatialLeafEnumerator* pEnum, int context )
  {
  	int leaf = CM_PointLeafnum( pt );
  	return pEnum->EnumerateLeaf( leaf, context );
  }
  
  
  static ConVar opt_EnumerateLeavesFastAlgorithm( "opt_EnumerateLeavesFastAlgorithm", "1", FCVAR_NONE, "Use the new SIMD version of CEngineBSPTree::EnumerateLeavesInBox." ); 
  
  
  bool CEngineBSPTree::EnumerateLeavesInBox( const Vector& mins, const Vector& maxs, 
  									ISpatialLeafEnumerator* pEnum, int context )
  {
  	if ( !host_state.worldmodel )
  		return false;
  
  	EnumLeafBoxInfo_t info;
  	VectorAdd( mins, maxs, info.m_vecBoxCenter );
  	info.m_vecBoxCenter *= 0.5f;
  	VectorSubtract( maxs, info.m_vecBoxCenter, info.m_vecBoxHalfDiagonal );
  	info.m_pIterator = pEnum;
  	info.m_nContext = context;
  	info.m_vecBoxMax = maxs;
  	info.m_vecBoxMin = mins;
  #ifdef _X360
  	if (opt_EnumerateLeavesFastAlgorithm.GetBool())
  		return EnumerateLeafInBox_R( host_state.worldbrush->nodes, &info );
  	else
  		return EnumerateLeafInBox_R( host_state.worldbrush->nodes, info );
  #else
  	return EnumerateLeafInBox_R( host_state.worldbrush->nodes, info );
  #endif
  }
  
  
  bool CEngineBSPTree::EnumerateLeavesInSphere( const Vector& center, float radius, 
  									ISpatialLeafEnumerator* pEnum, int context )
  {
  	EnumLeafSphereInfo_t info;
  	info.m_vecCenter = center;
  	info.m_flRadius = radius;
  	info.m_pIterator = pEnum;
  	info.m_nContext = context;
  	info.m_vecBoxCenter = center;
  	info.m_vecBoxHalfDiagonal.Init( radius, radius, radius );
  
  	return EnumerateLeafInSphere_R( host_state.worldbrush->nodes, info, ENUM_SPHERE_TEST_ALL );
  }
  
  
  bool CEngineBSPTree::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context )
  {
  	if (!ray.m_IsSwept)
  	{
  		Vector mins, maxs;
  		VectorAdd( ray.m_Start, ray.m_Extents, maxs );
  		VectorSubtract( ray.m_Start, ray.m_Extents, mins );
  
  		return EnumerateLeavesInBox( mins, maxs, pEnum, context );
  	}
  
  	Vector end;
  	VectorAdd( ray.m_Start, ray.m_Delta, end );
  
  	if ( ray.m_IsRay )
  	{
  		return EnumerateLeavesAlongRay_R( host_state.worldbrush->nodes, ray, 0.0f, 1.0f, pEnum, context );
  	}
  	else
  	{
  		return EnumerateLeavesAlongExtrudedRay_R( host_state.worldbrush->nodes, ray, 0.0f, 1.0f, pEnum, context );
  	}
  }
  
  