//========= 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 ( ) ;
if ( planeList . Base ( ) )
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 ;
intp m_nContext ;
} ;
struct EnumLeafSphereInfo_t
{
Vector m_vecCenter ;
float m_flRadius ;
Vector m_vecBoxCenter ;
Vector m_vecBoxHalfDiagonal ;
ISpatialLeafEnumerator * m_pIterator ;
intp 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 , intp 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 , intp 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 , intp context ) ;
bool EnumerateLeavesInBox ( const Vector & mins , const Vector & maxs , ISpatialLeafEnumerator * pEnum , intp context ) ;
bool EnumerateLeavesInSphere ( const Vector & center , float radius , ISpatialLeafEnumerator * pEnum , intp context ) ;
bool EnumerateLeavesAlongRay ( Ray_t const & ray , ISpatialLeafEnumerator * pEnum , intp 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 , intp 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 , intp 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 , intp 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 , intp 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 ) ;
}
}