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