summaryrefslogtreecommitdiff
path: root/engine/gl_rsurf.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/gl_rsurf.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/gl_rsurf.cpp')
-rw-r--r--engine/gl_rsurf.cpp5378
1 files changed, 5378 insertions, 0 deletions
diff --git a/engine/gl_rsurf.cpp b/engine/gl_rsurf.cpp
new file mode 100644
index 0000000..82ed85a
--- /dev/null
+++ b/engine/gl_rsurf.cpp
@@ -0,0 +1,5378 @@
+//========= 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 );
+ }
+}
+