Implementing Deferred lighting into Source 2013: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Rewriting this tutorial to be more user friendly as the previous one was very time consuming and hard to follow. Sucks to delete so much of it but I don't really know where to put it - feel free to reinstate it somewhere if you have any ideas)
Tag: Replaced
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{langsp}}__NOTOC__
{{LanguageBar}}
{{todo|Remake this page with this [https://github.com/SCell555/sdk-2013-deferred/tree/hybrid/mp/src 2013MP hybrid]!}}
{{warning|This guide aims to provide a quick and simple way of implementing this feature, but the implementation itself needs further work to make it stable, robust, and usable.}}
Having [[Deferred lighting]] in your mod comes with quite a lot of upsides as with it, you can have volumetric lighting, realtime shadows and lighting from the sun and from other light sources, while still keeping the performance steady
{{draft}}
which is something that Source's regular dynamic light entities would be incapable of (aka projected textures).
__NOTOC__


==Implementing into your Source 2013SP mod==
Having [[Deferred lighting|deferred lighting]] in your mod comes with quite a lot of upsides as with it, you can have volumetric lighting, realtime shadows and lighting from the sun and from other light sources, while still keeping the performance steady, which is something that Source's regular dynamic light entities would be incapable of (aka projected textures). However, keep in mind that all publicly available implementations are still unstable and can cause visual glitches and slowdowns.
This implementation was originally made by Kristjan Skutta aka Biohazard90, then it was expended by the Lambda Wars team, and then finally it was ported to Source 2013 by SCell555. Now implementing something like this into
your mod or game takes an insanely long time, as its very complex. So take that into while following this tutorial.


==Clientside==
==Background and resources used==
{{todo|Modify this code so that it will work with {{src13sp|3.1}}, right now this is raw code from {{src13mp|3.1}} hybrid}}
This implementation was originally [https://github.com/jonathonracz/swarm-deferred-src made by Kristjan Skutta aka Biohazard90], based on [https://github.com/NicolasDe/AlienSwarm Alien Swarm's deferred shading approach], then it was [https://github.com/Sandern/lambdawars expended by the Lambda Wars team], and finally it was [https://github.com/SCell555/sdk-2013-deferred ported to Source 2013 by SCell555]. This implementation has been since ported into various Source SDK forks, but for the purpose of simplicity, this tutorial will focus on adding deferred lighting to the [https://github.com/Source-SDK-Resources/source-sdk-vs2022 source-sdk-vs2022] repo, as it is an almost unchanged {{Src13|4}} SDK repo with added compatibility for modern editions of Visual Studio.
Now before we begin you will have to download the following files from {{src13mp|3.1}} hybrid's github, And put them into a folder named deferred then put that into the clients directory.
The end result should be identical, or at least very similar, to [https://github.com/54ac/source-sdk-vs2022-deferred source-sdk-vs2022-deferred], which is an implementation of deferred lighting for the source-sdk-vs2022 repo (both MP and SP).
*deferred ,This folder contents are needed which are:
**cascade_t.cpp
**cascade_t.h
**DefCookieProjectable.cpp
**DefCookieProjectable.h
**DefCookieTexture.cpp
**DefCookieTexture.h
**IDefCookie.h
**IDeferredExtClient.cpp
**cdeferred_manager_client.cpp
**cdeferred_manager_client.h
**clight_editor.cpp
**clight_editor.h
**clight_manager.cpp
**clight_manager.h
**def_light_t.cpp
**def_light_t.h
**deferred_client_common.cpp
**deferred_client_common.h
**deferred_rt.cpp
**deferred_rt.h
**flashlighteffect_deferred.cpp
**flashlighteffect_deferred.h
**viewrender_deferred.cpp
**viewrender_deferred.h
**vgui
***projectable_factory.cpp
***projectable_factory.h
***vgui_deferred.h
***vgui_editor_controls.cpp
***vgui_editor_controls.h
***vgui_editor_props.cpp
***vgui_editor_props.h
***vgui_editor_root.cpp
***vgui_marquee.cpp
***vgui_marquee.h
***vgui_particles.cpp
***vgui_particles.h
***vgui_particles.cpp
***vgui_projectable.cpp
***vgui_projectable.h


Now we are going to add our new files to our client project inside "Source Files" of client_hl2.vpc like so.
==Preparation==
<source lang=cpp>
Using the right tools will make copying code from one repo to another a relatively quick and painless process.
$Folder "Deferred"
# Download [https://winmerge.org WinMerge] or a similar diff/merge tool.
{
# Use git to clone the two repos used in this tutorial (or download them as ZIP files): [https://github.com/Source-SDK-Resources/source-sdk-vs2022 source-sdk-vs2022] and [https://github.com/54ac/source-sdk-vs2022-deferred source-sdk-vs2022-deferred].
$Folder "Shared"
# Make sure the source-sdk-vs2022 repo compiles successfully in your installation of Visual Studio 2022 before you modify it.
{
$File "$SRCDIR\game\shared\deferred\CDefLight.cpp"
$File "$SRCDIR\game\shared\deferred\CDefLight.h"
$File "$SRCDIR\game\shared\deferred\CDefLightContainer.cpp"
$File "$SRCDIR\game\shared\deferred\CDefLightContainer.h"
$File "$SRCDIR\game\shared\deferred\CDefLightGlobal.cpp"
$File "$SRCDIR\game\shared\deferred\CDefLightGlobal.h"
$File "$SRCDIR\game\shared\deferred\deferred_shared_common.cpp"
$File "$SRCDIR\game\shared\deferred\deferred_shared_common.h"
$File "$SRCDIR\game\shared\deferred\ssemath_ext.h"
}
$Folder "Client"
{
$Folder "VGUI"
{
$File "deferred\vgui\projectable_factory.cpp"
$File "deferred\vgui\projectable_factory.h"
$File "deferred\vgui\vgui_deferred.h"
$File "deferred\vgui\vgui_editor_controls.cpp"
$File "deferred\vgui\vgui_editor_controls.h"
$File "deferred\vgui\vgui_editor_props.cpp"
$File "deferred\vgui\vgui_editor_props.h"
$File "deferred\vgui\vgui_editor_root.cpp"
$File "deferred\vgui\vgui_marquee.cpp"
$File "deferred\vgui\vgui_marquee.h"
$File "deferred\vgui\vgui_particles.cpp"
$File "deferred\vgui\vgui_particles.h"
$File "deferred\vgui\vgui_projectable.cpp"
$File "deferred\vgui\vgui_projectable.h"
}
$File "deferred\cascade_t.h"
$File "deferred\cascade_t.cpp"
$File "deferred\DefCookieProjectable.cpp"
$File "deferred\DefCookieProjectable.h"
$File "deferred\DefCookieTexture.cpp"
$File "deferred\DefCookieTexture.h"
$File "deferred\IDefCookie.h"
$File "deferred\IDeferredExtClient.cpp"
$File "deferred\cdeferred_manager_client.cpp"
$File "deferred\cdeferred_manager_client.h"
$File "deferred\clight_editor.cpp"
$File "deferred\clight_editor.h"
$File "deferred\clight_manager.cpp"
$File "deferred\clight_manager.h"
$File "deferred\def_light_t.cpp"
$File "deferred\def_light_t.h"
$File "deferred\deferred_client_common.cpp"
$File "deferred\deferred_client_common.h"
$File "deferred\deferred_rt.cpp"
$File "deferred\deferred_rt.h"
$File "deferred\flashlighteffect_deferred.cpp"
$File "deferred\flashlighteffect_deferred.h"
$File "deferred\viewrender_deferred.cpp"
$File "deferred\viewrender_deferred.h"
}
}
</source>
Firstly we are going to modify '''cdll_client_int.cpp'''. Now put the following code at the top of the file around lines 175 but it must be before #include "tier0/memdbgon.h".
<source lang=cpp>
 
#include "deferred/deferred_shared_common.h"
 
</source>
Now aound lines 1816 in void CHLClient::ResetStringTablePointers() place the following code.
<source lang=cpp>


g_pStringTable_LightCookies = NULL;
The repo also includes diff files which should allow you to easily apply all of the necessary changes per branch as long as you know how to use them. This tutorial will focus on the slightly more user-friendly WinMerge approach.


</source>
==Implementation using WinMerge==
Now around lines 2058 put the following code inside void CHLClient::InstallStringTableCallback( const char *tableName ). And after that we should be done with '''cdll_client_int.cpp'''.
You can copy all the necessary files and apply changes to the code by comparing both folders in WinMerge. The source-sdk-vs2022-deferred aims to modify as few files as possible, so the process should be relatively quick.
<source lang=cpp>
else if ( !Q_strcasecmp( tableName, COOKIE_STRINGTBL_NAME ) )
{
g_pStringTable_LightCookies = networkstringtable->FindTable( tableName );


g_pStringTable_LightCookies->SetStringChangedCallback( NULL, OnCookieTableChanged );
The process is very similar for both the MP and SP branches. In both cases, the new files that need to be transferred are contained in separate deferred folders in the client, server, and shared folders in /game, as well as in /materialsystem/stdshaders and /public. The files that need to be modified are contained in the same folders as well as in /tier1 and the root folder (creategameprojects.bat).
}
</source>


Now open '''viewrender.h''' and around lines 19 paste the following code. 
===File modifications===
<source lang=cpp>
Apply the modifications as shown by WinMerge. Consult the manual if you're unsure how to do this, though you could probably do this solely with the Ctrl and arrow keys if you wanted to. The majority of the modifications will involve files in /game/client and /materialsystem/stdshaders. All new entries for files related to deferred lighting are added in *_base.vpc files, and the creategameprojects.bat file is changed to generate the Visual Studio solution for the shader DLL as well.
#include "../../materialsystem/stdshaders/deferred_global_common.h"
</source>
And around lines 39 paste this too.
<source lang=cpp>
// @Deferred - Biohazard
struct def_light_t;
</source>
Now around lines 96, but the following code into enum view_id_t.
<source lang=cpp>
        // @Deferred - Biohazard
VIEW_DEFERRED_GBUFFER = 8,
VIEW_DEFERRED_SHADOW = 9,
</source>
And around lines 263, but the following code into class CRendering3dView : public CBase3dView.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
void PushComposite();
void PopComposite();


static void PushGBuffer( bool bInitial, float zScale = 1.0f, bool bClearDepth = true );
Once you've applied all the changes shown by WinMerge, run the creategameprojects.bat file to recreate the Visual Studio solutions. At this stage, you should be able to compile the client and server DLLs.
static void PopGBuffer();
#endif
</source>


And around lines 505, paste the following code, and we should be done with viewrender.h.
==Compiling shaders with ShaderCompile==
<source lang=cpp>
In order to compile the shader DLL, you will need to generate the .inc shader include files. This tutorial uses [https://github.com/SCell555/ShaderCompile SCell555's ShaderCompile tool] to simplify the process.


#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
# Download the ShaderCompile 7z file from the latest release on the GitHub releases page.
void ProcessDeferredGlobals( const CViewSetup &view );
# Follow the "getting started" section in the readme of the repo (replace cshader.h and buildshaders.bat, modify game_shader_dx9_base.vpc and buildsdkshaders.bat, copy over ShaderCompile.exe and process_shaders.ps1).
# Run e.g. buildhl2mpshaders.bat to build the shaders.


void ViewDrawGBuffer( const CViewSetup &view, bool &bDrew3dSkybox, SkyboxVisibility_t &nSkyboxVisible,
If the batch file refuses to run, open it in a text editor and encase the paths for GAMEDIR and SOURCEDIR in quotation marks, i.e. so that it looks similar to this (SDKBINDIR is irrelevant here):
bool bDrawViewModel );
<source>
@echo off
setlocal


void BeginRadiosity( const CViewSetup &view );
rem ================================
void UpdateRadiosityPosition();
rem ==== MOD PATH CONFIGURATIONS ===
void PerformRadiosityGlobal( const int iRadiosityCascade, const CViewSetup &view );
void EndRadiosity( const CViewSetup &view );
void DebugRadiosity( const CViewSetup &view );


IMesh *GetRadiosityScreenGrid( const int iCascade );
rem == Set the absolute path to your mod's game directory here ==
IMesh *CreateRadiosityScreenGrid( const Vector2D &vecViewportBase, const float flWorldStepSize );
set GAMEDIR="%cd%\..\..\..\game\mod_hl2mp"


void PerformLighting( const CViewSetup &view );
rem == Set the relative or absolute path to Source SDK Base 2013 Singleplayer\bin ==
set SDKBINDIR=


void ResetCascadeDelay();
rem ==  Set the Path to your mod's root source code ==
void RenderCascadedShadows( const CViewSetup &view, const bool bEnableRadiosity );
rem This should already be correct, accepts relative paths only!
set SOURCEDIR="..\.."


public:
rem ==== MOD PATH CONFIGURATIONS END ===
virtual void DrawLightShadowView( const CViewSetup &view, int iDesiredShadowmap, def_light_t *l );
rem ====================================


protected:
float m_flRenderDelay[SHADOW_NUM_CASCADES];


Vector m_vecRadiosityOrigin[2];
call buildsdkshaders.bat
IMesh *m_pMesh_RadiosityScreenGrid[2];
CUtlVector< IMesh* > m_hRadiosityDebugMeshList[2];
#endif
</source>
Now open viewrender.cpp and around lines 81 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED
#include "deferred/deferred_shared_common.h"
#include "tier1/callqueue.h"
#endif // DEFERRED_ENABLED
</source>
And now around lines 98 replace static convar with a regular convar like so.
<source lang=cpp>
ConVar r_visocclusion( "r_visocclusion", "0", FCVAR_CHEAT );
</source>
Now around lines 155 replace static convars with a regular convars like so.
<source lang=cpp>
ConVar r_ForceWaterLeaf( "r_ForceWaterLeaf", "1", 0, "Enable for optimization to water - considers view in leaf under water for purposes of culling" );
static ConVar mat_drawwater( "mat_drawwater", "1", FCVAR_CHEAT );
ConVar mat_clipz( "mat_clipz", "1" );
</source>
And again aorund lines 175 turn the static convar into a regular one.
<source lang=cpp>
ConVar r_eyewaterepsilon( "r_eyewaterepsilon", "7.0f", FCVAR_CHEAT );
</source>
And around lines 199 replace static int with regular int like so.
<source lang=cpp>
int g_CurrentViewID = VIEW_NONE;
</source>
And around lines 383 put the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
void FlushWorldLists()
{
g_WorldListCache.Flush();
}
#endif
</source>
And around lines 397 replace Setup with the fallowing code.
<source lang=cpp>
bool Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible, bool bGBuffer = false );
</source>
And around lines 399 add the following code inside class CSkyboxView : public CRendering3dView.
<source lang=cpp>
#if DEFERRED_ENABLED // @Deferred - Biohazard
bool m_bGBufferPass;
#endif
</source>
</source>
And around lines 694 paste the following code.
<source lang=cpp>
// Deferred views
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
class CBaseWorldBlendViewDeferred : public CBaseWorldView
{
DECLARE_CLASS( CBaseWorldBlendViewDeferred, CBaseWorldView );
public:
CBaseWorldBlendViewDeferred(CViewRender *pMainView) : CBaseWorldView( pMainView )
{
}


void DrawSetup( float waterHeight, int flags, float waterZAdjust, int iForceViewLeaf = -1, bool bShadowDepth = false );
Once you've compiled all the shaders, launch the creategameprojects.bat file again. You should now be able to compile the shader DLL.
void DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust, bool bShadowDepth = false );


// BUGBUG this causes all sorts of problems
==Notes==
virtual bool ShouldCacheLists(){ return false; };
* At this point, deferred lighting should be implemented. If not, either go through the steps again or simply use the source-sdk-vs2022-deferred repo as your starting point. Make sure to read through the notes in the readme.
* Don't be discouraged by how janky it is - use it as motivation to become the first person to create a robust, stable deferred lighting implementation without making it closed source.


protected:
== External links ==
void DrawOpaqueRenderablesDeferred( bool bNoDecals );
* [https://github.com/NicolasDe/AlienSwarm Alien Swarm SDK (deferred shading)]
};
* [https://github.com/jonathonracz/swarm-deferred-src Biohazard90's deferred lighting implementation for Alien Swarm]
 
* [https://github.com/Sandern/lambdawars Lambda Wars source code]
class CGBufferBlendView : public CBaseWorldBlendViewDeferred
* [https://github.com/SCell555/sdk-2013-deferred SCell555's port of deferred lighting to Source SDK 2013]
{
* [https://github.com/Source-SDK-Resources/source-sdk-vs2022 Source SDK 2013 with Visual Studio 2022 support]
DECLARE_CLASS( CGBufferBlendView, CBaseWorldBlendViewDeferred );
* [https://github.com/54ac/source-sdk-vs2022-deferred Implementation of deferred lighting for the VS2022 fork]
public:
* [https://github.com/SCell555/ShaderCompile SCell555's ShaderCompile tool]
CGBufferBlendView(CViewRender *pMainView) : CBaseWorldBlendViewDeferred( pMainView )
{
}
 
void Setup( const CViewSetup &view, bool bDrewSkybox );
void Draw();
 
virtual void PushView( float waterHeight );
virtual void PopView();
 
private:
VisibleFogVolumeInfo_t m_fogInfo;
bool m_bDrewSkybox;
};
 
abstract_class CBaseShadowBlendView : public CBaseWorldBlendViewDeferred
{
DECLARE_CLASS( CBaseShadowBlendView, CBaseWorldBlendViewDeferred );
public:
CBaseShadowBlendView(CViewRender *pMainView) : CBaseWorldBlendViewDeferred( pMainView )
{
m_bOutputRadiosity = false;
};
 
void Setup( const CViewSetup &view,
ITexture *pDepthTexture,
ITexture *pDummyTexture );
void SetupRadiosityTargets(
ITexture *pAlbedoTexture,
ITexture *pNormalTexture );
 
void SetRadiosityOutputEnabled( bool bEnabled );
 
void Draw();
virtual bool AdjustView( float waterHeight );
virtual void PushView( float waterHeight );
virtual void PopView();
 
virtual void CalcShadowView() = 0;
virtual void CommitData(){};
 
virtual int GetShadowMode() = 0;
 
private:
 
ITexture *m_pDepthTexture;
ITexture *m_pDummyTexture;
ITexture *m_pRadAlbedoTexture;
ITexture *m_pRadNormalTexture;
ViewCustomVisibility_t shadowVis;
 
bool m_bOutputRadiosity;
};
 
class COrthoShadowBlendView : public CBaseShadowBlendView
{
DECLARE_CLASS( COrthoShadowBlendView, CBaseShadowBlendView );
public:
COrthoShadowBlendView(CViewRender *pMainView, const int &index)
: CBaseShadowBlendView( pMainView )
{
iCascadeIndex = index;
}
 
virtual void CalcShadowView();
virtual void CommitData();
 
virtual int GetShadowMode(){
return DEFERRED_SHADOW_MODE_ORTHO;
};
 
private:
int iCascadeIndex;
};
 
class CDualParaboloidShadowBlendView : public CBaseShadowBlendView
{
DECLARE_CLASS( CDualParaboloidShadowBlendView, CBaseShadowBlendView );
public:
CDualParaboloidShadowBlendView(CViewRender *pMainView,
def_light_t *pLight,
const bool &bSecondary)
: CBaseShadowBlendView( pMainView )
{
m_pLight = pLight;
m_bSecondary = bSecondary;
}
virtual bool AdjustView( float waterHeight );
virtual void PushView( float waterHeight );
virtual void PopView();
 
virtual void CalcShadowView();
 
virtual int GetShadowMode(){
return DEFERRED_SHADOW_MODE_DPSM;
};
 
private:
bool m_bSecondary;
def_light_t *m_pLight;
};
 
class CSpotLightShadowBlendView : public CBaseShadowBlendView
{
DECLARE_CLASS( CSpotLightShadowBlendView, CBaseShadowBlendView );
public:
CSpotLightShadowBlendView(CViewRender *pMainView,
def_light_t *pLight, int index )
: CBaseShadowBlendView( pMainView )
{
m_pLight = pLight;
m_iIndex = index;
}
 
virtual void CalcShadowView();
virtual void CommitData();
 
virtual int GetShadowMode(){
return DEFERRED_SHADOW_MODE_PROJECTED;
};
 
private:
def_light_t *m_pLight;
int m_iIndex;
};
#endif
</source>
And now around lines 963 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
static CViewRender g_ViewRender;
IViewRender *GetViewRenderInstance()
{
return &g_ViewRender;
}
#endif
</source>
And now around lines 973 put the fallowing code inside CViewRender::CViewRender().
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
m_pMesh_RadiosityScreenGrid[0] = NULL;
m_pMesh_RadiosityScreenGrid[1] = NULL;
#endif // DEFERRED_ENABLED
</source>
Now around lines 1465 paste the following code into void CViewRender::ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &view,
int nClearFlags, view_id_t viewID, bool bDrawViewModel, int baseDrawFlags, ViewCustomVisibility_t *pCustomVisibility ).
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
const bool bDeferredActive = GetDeferredManager()->IsDeferredRenderingEnabled();
 
if( bDeferredActive )
{
ViewDrawGBuffer( view, bDrew3dSkybox, nSkyboxVisible, bDrawViewModel );
 
PerformLighting( view );
 
 
}
#endif
</source>
And around lines 1502 put the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if( bDeferredActive )
{
GetLightingManager()->RenderVolumetrics( view );
}
#endif
</source>
And around lines 1950 add the following lines of code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
static lightData_Global_t GetActiveGlobalLightState()
{
lightData_Global_t data;
CLightingEditor *pEditor = GetLightingEditor();
 
if ( pEditor->IsEditorLightingActive() && pEditor->GetKVGlobalLight() != NULL )
{
data = pEditor->GetGlobalState();
}
else if ( GetGlobalLight() != NULL )
{
data = GetGlobalLight()->GetState();
}
 
return data;
}
 
struct defData_setGlobals
{
public:
Vector orig, fwd;
float zDists[2];
VMatrix frustumDeltas;
#if DEFCFG_BILATERAL_DEPTH_TEST
VMatrix worldCameraDepthTex;
#endif
 
static void Fire( defData_setGlobals d )
{
IDeferredExtension *pDef = GetDeferredExt();
pDef->CommitOrigin( d.orig );
pDef->CommitViewForward( d.fwd );
pDef->CommitZDists( d.zDists[0], d.zDists[1] );
pDef->CommitFrustumDeltas( d.frustumDeltas );
#if DEFCFG_BILATERAL_DEPTH_TEST
pDef->CommitWorldToCameraDepthTex( d.worldCameraDepthTex );
#endif
};
};
 
 
//-----------------------------------------------------------------------------
// Purpose:  
//-----------------------------------------------------------------------------
void CViewRender::ProcessDeferredGlobals( const CViewSetup &view )
{
VMatrix matPerspective, matView, matViewProj, screen2world;
matView.Identity();
matView.SetupMatrixOrgAngles( vec3_origin, view.angles );
 
MatrixSourceToDeviceSpace( matView );
 
matView = matView.Transpose3x3();
Vector viewPosition;
 
Vector3DMultiply( matView, view.origin, viewPosition );
matView.SetTranslation( -viewPosition );
MatrixBuildPerspectiveX( matPerspective, view.fov, view.m_flAspectRatio,
view.zNear, view.zFar );
MatrixMultiply( matPerspective, matView, matViewProj );
 
MatrixInverseGeneral( matViewProj, screen2world );
 
GetLightingManager()->SetRenderConstants( screen2world, view );
 
Vector frustum_c0, frustum_cc, frustum_1c;
float projDistance = 1.0f;
Vector3DMultiplyPositionProjective( screen2world, Vector(0,projDistance,projDistance), frustum_c0 );
Vector3DMultiplyPositionProjective( screen2world, Vector(0,0,projDistance), frustum_cc );
Vector3DMultiplyPositionProjective( screen2world, Vector(projDistance,0,projDistance), frustum_1c );
 
frustum_c0 -= view.origin;
frustum_cc -= view.origin;
frustum_1c -= view.origin;
 
Vector frustum_up = frustum_c0 - frustum_cc;
Vector frustum_right = frustum_1c - frustum_cc;
 
frustum_cc /= view.zFar;
frustum_right /= view.zFar;
frustum_up /= view.zFar;
 
defData_setGlobals data;
data.orig = view.origin;
AngleVectors( view.angles, &data.fwd );
data.zDists[0] = view.zNear;
data.zDists[1] = view.zFar;
 
data.frustumDeltas.Identity();
data.frustumDeltas.SetBasisVectors( frustum_cc, frustum_right, frustum_up );
data.frustumDeltas = data.frustumDeltas.Transpose3x3();
 
#if DEFCFG_BILATERAL_DEPTH_TEST
VMatrix matWorldToCameraDepthTex;
MatrixBuildScale( matWorldToCameraDepthTex, 0.5f, -0.5f, 1.0f );
matWorldToCameraDepthTex[0][3] = matWorldToCameraDepthTex[1][3] = 0.5f;
MatrixMultiply( matWorldToCameraDepthTex, matViewProj, matWorldToCameraDepthTex );
 
data.worldCameraDepthTex = matWorldToCameraDepthTex.Transpose();
#endif
 
QUEUE_FIRE( defData_setGlobals, Fire, data );
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawGBuffer( const CViewSetup &view, bool &bDrew3dSkybox, SkyboxVisibility_t &nSkyboxVisible,
bool bDrawViewModel )
{
MDLCACHE_CRITICAL_SECTION();
 
int oldViewID = g_CurrentViewID;
g_CurrentViewID = VIEW_DEFERRED_GBUFFER;
 
int nClearFlags = VIEW_CLEAR_DEPTH;
CSkyboxView *pSkyView = new CSkyboxView( this );
if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible, true ) ) != false )
AddViewToScene( pSkyView );
 
SafeRelease( pSkyView );
 
// Start view
unsigned int visFlags;
SetupVis( view, visFlags, NULL );
 
CRefPtr<CGBufferBlendView> pGBufferView = new CGBufferBlendView( this );
pGBufferView->Setup( view, bDrew3dSkybox );
AddViewToScene( pGBufferView );
 
DrawViewModels( view, bDrawViewModel/*, true*/ );
g_CurrentViewID = oldViewID;
}
 
static int GetSourceRadBufferIndex( const int index )
{
Assert( index == 0 || index == 1 );
 
const bool bFar = index == 1;
const int iNumSteps = bFar ? deferred_radiosity_propagate_count_far.GetInt() : deferred_radiosity_propagate_count.GetInt()
+ bFar ? deferred_radiosity_blur_count_far.GetInt() : deferred_radiosity_blur_count.GetInt();
return ( iNumSteps % 2 == 0 ) ? 0 : 1;
}
 
void CViewRender::BeginRadiosity( const CViewSetup &view )
{
Vector fwd;
AngleVectors( view.angles, &fwd );
 
float flAmtVertical = abs( DotProduct( fwd, Vector( 0, 0, 1 ) ) );
flAmtVertical = RemapValClamped( flAmtVertical, 0, 1, 1, 0.5f );
 
for ( int iCascade = 0; iCascade < 2; iCascade++ )
{
const bool bFar = iCascade == 1;
const Vector gridSize( RADIOSITY_BUFFER_SAMPLES_XY, RADIOSITY_BUFFER_SAMPLES_XY,
RADIOSITY_BUFFER_SAMPLES_Z );
const Vector gridSizeHalf = gridSize / 2;
const float gridStepSize = bFar ? RADIOSITY_BUFFER_GRID_STEP_SIZE_FAR
: RADIOSITY_BUFFER_GRID_STEP_SIZE_CLOSE;
const float flGridDistance = bFar ? RADIOSITY_BUFFER_GRID_STEP_DISTANCEMULT_FAR
: RADIOSITY_BUFFER_GRID_STEP_DISTANCEMULT_CLOSE;
 
Vector vecFwd;
AngleVectors( view.angles, &vecFwd );
 
m_vecRadiosityOrigin[iCascade] = view.origin
+ vecFwd * gridStepSize * RADIOSITY_BUFFER_SAMPLES_XY * flGridDistance * flAmtVertical;
 
for ( int i = 0; i < 3; i++ )
m_vecRadiosityOrigin[iCascade][i] -= fmod( m_vecRadiosityOrigin[iCascade][i], gridStepSize );
 
m_vecRadiosityOrigin[iCascade] -= gridSizeHalf * gridStepSize;
 
const int iSourceBuffer = GetSourceRadBufferIndex( iCascade );
static int iLastSourceBuffer[2] = { iSourceBuffer, GetSourceRadBufferIndex( 1 ) };
 
const int clearSizeY = RADIOSITY_BUFFER_RES_Y / 2;
const int clearOffset = (iCascade == 1) ? clearSizeY : 0;
 
CMatRenderContextPtr pRenderContext( materials );
 
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityBuffer( iSourceBuffer ), NULL,
0, clearOffset, RADIOSITY_BUFFER_RES_X, clearSizeY );
pRenderContext->ClearColor3ub( 0, 0, 0 );
pRenderContext->ClearBuffers( true, false );
pRenderContext->PopRenderTargetAndViewport();
 
if ( iLastSourceBuffer[iCascade] != iSourceBuffer )
{
iLastSourceBuffer[iCascade] = iSourceBuffer;
 
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityBuffer( 1 - iSourceBuffer ), NULL,
0, clearOffset, RADIOSITY_BUFFER_RES_X, clearSizeY );
pRenderContext->ClearColor3ub( 0, 0, 0 );
pRenderContext->ClearBuffers( true, false );
pRenderContext->PopRenderTargetAndViewport();
 
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityNormal( 1 - iSourceBuffer ), NULL,
0, clearOffset, RADIOSITY_BUFFER_RES_X, clearSizeY );
pRenderContext->ClearColor3ub( 127, 127, 127 );
pRenderContext->ClearBuffers( true, false );
pRenderContext->PopRenderTargetAndViewport();
}
 
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityNormal( iSourceBuffer ), NULL,
0, clearOffset, RADIOSITY_BUFFER_RES_X, clearSizeY );
pRenderContext->ClearColor3ub( 127, 127, 127 );
pRenderContext->ClearBuffers( true, false );
pRenderContext->PopRenderTargetAndViewport();
}
 
UpdateRadiosityPosition();
}
 
void CViewRender::UpdateRadiosityPosition()
{
struct defData_setupRadiosity
{
public:
radiosityData_t data;
 
static void Fire( defData_setupRadiosity d )
{
GetDeferredExt()->CommitRadiosityData( d.data );
};
};
 
defData_setupRadiosity radSetup;
radSetup.data.vecOrigin[0] = m_vecRadiosityOrigin[0];
radSetup.data.vecOrigin[1] = m_vecRadiosityOrigin[1];
 
QUEUE_FIRE( defData_setupRadiosity, Fire, radSetup );
}
 
void CViewRender::PerformRadiosityGlobal( const int iRadiosityCascade, const CViewSetup &view )
{
const int iSourceBuffer = GetSourceRadBufferIndex( iRadiosityCascade );
const int iOffsetY = (iRadiosityCascade == 1) ? RADIOSITY_BUFFER_RES_Y/2 : 0;
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RADIOSITY_CASCADE, iRadiosityCascade );
 
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityBuffer( iSourceBuffer ), NULL,
0, iOffsetY, RADIOSITY_BUFFER_VIEWPORT_SX, RADIOSITY_BUFFER_VIEWPORT_SY );
pRenderContext->SetRenderTargetEx( 1, GetDefRT_RadiosityNormal( iSourceBuffer ) );
 
pRenderContext->Bind( GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_GLOBAL ) );
GetRadiosityScreenGrid( iRadiosityCascade )->Draw();
 
pRenderContext->PopRenderTargetAndViewport();
}
 
void CViewRender::EndRadiosity( const CViewSetup &view )
{
const int iNumPropagateSteps[2] = { deferred_radiosity_propagate_count.GetInt(),
deferred_radiosity_propagate_count_far.GetInt() };
const int iNumBlurSteps[2] = { deferred_radiosity_blur_count.GetInt(),
deferred_radiosity_blur_count_far.GetInt() };
 
IMaterial *pPropagateMat[2] = {
GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_PROPAGATE_0 ),
GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_PROPAGATE_1 ),
};
 
IMaterial *pBlurMat[2] = {
GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_BLUR_0 ),
GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_BLUR_1 ),
};
 
for ( int iCascade = 0; iCascade < 2; iCascade++ )
{
bool bSecondDestBuffer = GetSourceRadBufferIndex( iCascade ) == 0;
const int iOffsetY = (iCascade==1) ? RADIOSITY_BUFFER_RES_Y / 2 : 0;
 
for ( int i = 0; i < iNumPropagateSteps[iCascade]; i++ )
{
const int index = bSecondDestBuffer ? 1 : 0;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityBuffer( index ), NULL,
0, iOffsetY, RADIOSITY_BUFFER_VIEWPORT_SX, RADIOSITY_BUFFER_VIEWPORT_SY );
pRenderContext->SetRenderTargetEx( 1, GetDefRT_RadiosityNormal( index ) );
 
pRenderContext->Bind( pPropagateMat[ 1 - index ] );
 
GetRadiosityScreenGrid( iCascade )->Draw();
 
pRenderContext->PopRenderTargetAndViewport();
bSecondDestBuffer = !bSecondDestBuffer;
}
 
for ( int i = 0; i < iNumBlurSteps[iCascade]; i++ )
{
const int index = bSecondDestBuffer ? 1 : 0;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport( GetDefRT_RadiosityBuffer( index ), NULL,
0, iOffsetY, RADIOSITY_BUFFER_VIEWPORT_SX, RADIOSITY_BUFFER_VIEWPORT_SY );
pRenderContext->SetRenderTargetEx( 1, GetDefRT_RadiosityNormal( index ) );
 
pRenderContext->Bind( pBlurMat[ 1 - index ] );
 
GetRadiosityScreenGrid( iCascade )->Draw();
 
pRenderContext->PopRenderTargetAndViewport();
bSecondDestBuffer = !bSecondDestBuffer;
}
}
 
#if ( DEFCFG_DEFERRED_SHADING == 0 )
DrawLightPassFullscreen( GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_RADIOSITY_BLEND ),
view.width, view.height );
#endif
}
 
 
IMesh *CViewRender::GetRadiosityScreenGrid( const int iCascade )
{
if ( m_pMesh_RadiosityScreenGrid[iCascade] == NULL )
{
Assert( m_pMesh_RadiosityScreenGrid[iCascade] == NULL );
 
const bool bFar = iCascade == 1;
 
m_pMesh_RadiosityScreenGrid[iCascade] = CreateRadiosityScreenGrid(
Vector2D( 0, (bFar?0.5f:0) ),
bFar ? RADIOSITY_BUFFER_GRID_STEP_SIZE_FAR : RADIOSITY_BUFFER_GRID_STEP_SIZE_CLOSE );
}
 
Assert( m_pMesh_RadiosityScreenGrid[iCascade] != NULL );
 
return m_pMesh_RadiosityScreenGrid[iCascade];
}
 
IMesh *CViewRender::CreateRadiosityScreenGrid( const Vector2D &vecViewportBase,
const float flWorldStepSize )
{
VertexFormat_t format = VERTEX_POSITION
| VERTEX_TEXCOORD_SIZE( 0, 4 )
| VERTEX_TEXCOORD_SIZE( 1, 4 )
| VERTEX_TANGENT_S;
 
const float flTexelGridMargin = 1.5f / RADIOSITY_BUFFER_SAMPLES_XY;
const float flTexelHalf[2] = { 0.5f / RADIOSITY_BUFFER_VIEWPORT_SX,
0.5f / RADIOSITY_BUFFER_VIEWPORT_SY };
 
const float flLocalCoordSingle = 1.0f / RADIOSITY_BUFFER_GRIDS_PER_AXIS;
const float flLocalCoords[4][2] = {
0, 0,
flLocalCoordSingle, 0,
flLocalCoordSingle, flLocalCoordSingle,
0, flLocalCoordSingle,
};
 
CMatRenderContextPtr pRenderContext( materials );
IMesh *pRet = pRenderContext->CreateStaticMesh(
format, TEXTURE_GROUP_OTHER );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pRet, MATERIAL_QUADS,
RADIOSITY_BUFFER_GRIDS_PER_AXIS * RADIOSITY_BUFFER_GRIDS_PER_AXIS );
 
float flGridOrigins[RADIOSITY_BUFFER_SAMPLES_Z][2];
for ( int i = 0; i < RADIOSITY_BUFFER_SAMPLES_Z; i++ )
{
int x = i % RADIOSITY_BUFFER_GRIDS_PER_AXIS;
int y = i / RADIOSITY_BUFFER_GRIDS_PER_AXIS;
 
flGridOrigins[i][0] = x * flLocalCoordSingle + flTexelHalf[0];
flGridOrigins[i][1] = y * flLocalCoordSingle + flTexelHalf[1];
}
 
const float flGridSize = flWorldStepSize * RADIOSITY_BUFFER_SAMPLES_XY;
const float flLocalGridSize[4][2] = {
0, 0,
flGridSize, 0,
flGridSize, flGridSize,
0, flGridSize,
};
const float flLocalGridLimits[4][2] = {
-flTexelGridMargin, -flTexelGridMargin,
1 + flTexelGridMargin, -flTexelGridMargin,
1 + flTexelGridMargin, 1 + flTexelGridMargin,
-flTexelGridMargin, 1 + flTexelGridMargin,
};
 
for ( int x = 0; x < RADIOSITY_BUFFER_GRIDS_PER_AXIS; x++ )
{
for ( int y = 0; y < RADIOSITY_BUFFER_GRIDS_PER_AXIS; y++ )
{
const int iIndexLocal = x + y * RADIOSITY_BUFFER_GRIDS_PER_AXIS;
const int iIndicesOne[2] = { MIN( RADIOSITY_BUFFER_SAMPLES_Z - 1, iIndexLocal + 1 ), MAX( 0, iIndexLocal - 1 ) };
 
for ( int q = 0; q < 4; q++ )
{
meshBuilder.Position3f(
(x * flLocalCoordSingle + flLocalCoords[q][0]) * 2 - flLocalCoordSingle * RADIOSITY_BUFFER_GRIDS_PER_AXIS,
flLocalCoordSingle * RADIOSITY_BUFFER_GRIDS_PER_AXIS - (y * flLocalCoordSingle + flLocalCoords[q][1]) * 2,
0 );
 
meshBuilder.TexCoord4f( 0,
(flGridOrigins[iIndexLocal][0] + flLocalCoords[q][0]) * RADIOSITY_UVRATIO_X + vecViewportBase.x,
(flGridOrigins[iIndexLocal][1] + flLocalCoords[q][1]) * RADIOSITY_UVRATIO_Y + vecViewportBase.y,
flLocalGridLimits[q][0],
flLocalGridLimits[q][1] );
 
meshBuilder.TexCoord4f( 1,
(flGridOrigins[iIndicesOne[0]][0] + flLocalCoords[q][0]) * RADIOSITY_UVRATIO_X + vecViewportBase.x,
(flGridOrigins[iIndicesOne[0]][1] + flLocalCoords[q][1]) * RADIOSITY_UVRATIO_Y + vecViewportBase.y,
(flGridOrigins[iIndicesOne[1]][0] + flLocalCoords[q][0]) * RADIOSITY_UVRATIO_X + vecViewportBase.x,
(flGridOrigins[iIndicesOne[1]][1] + flLocalCoords[q][1]) * RADIOSITY_UVRATIO_Y + vecViewportBase.y );
 
meshBuilder.TangentS3f( flLocalGridSize[q][0],
flLocalGridSize[q][1],
iIndexLocal * flWorldStepSize );
 
meshBuilder.AdvanceVertex();
}
}
}
 
meshBuilder.End();
 
return pRet;
}
 
//-----------------------------------------------------------------------------
// Purpose:  
//-----------------------------------------------------------------------------
void CViewRender::PerformLighting( const CViewSetup &view )
{
bool bResetLightAccum = false;
const bool bRadiosityEnabled = DEFCFG_ENABLE_RADIOSITY != 0 && deferred_radiosity_enable.GetBool();
 
if ( bRadiosityEnabled )
BeginRadiosity( view );
 
if ( GetGlobalLight() != NULL )
{
struct defData_setGlobalLightState
{
public:
lightData_Global_t state;
 
static void Fire( defData_setGlobalLightState d )
{
GetDeferredExt()->CommitLightData_Global( d.state );
};
};
 
defData_setGlobalLightState lightDataState;
lightDataState.state = GetActiveGlobalLightState();
 
if ( !GetLightingEditor()->IsEditorLightingActive() &&
deferred_override_globalLight_enable.GetBool() )
{
lightDataState.state.bShadow = deferred_override_globalLight_shadow_enable.GetBool();
UTIL_StringToVector( lightDataState.state.diff.AsVector3D().Base(), deferred_override_globalLight_diffuse.GetString() );
UTIL_StringToVector( lightDataState.state.ambh.AsVector3D().Base(), deferred_override_globalLight_ambient_high.GetString() );
UTIL_StringToVector( lightDataState.state.ambl.AsVector3D().Base(), deferred_override_globalLight_ambient_low.GetString() );
 
lightDataState.state.bEnabled = ( lightDataState.state.diff.LengthSqr() > 0.01f ||
lightDataState.state.ambh.LengthSqr() > 0.01f ||
lightDataState.state.ambl.LengthSqr() > 0.01f );
}
 
QUEUE_FIRE( defData_setGlobalLightState, Fire, lightDataState );
 
if ( lightDataState.state.bEnabled )
{
bool bShadowedGlobal = lightDataState.state.bShadow;
 
if ( bShadowedGlobal )
{
Vector origins[2] = { view.origin, view.origin + lightDataState.state.vecLight.AsVector3D() * 1024 };
render->ViewSetupVis( false, 2, origins );
 
RenderCascadedShadows( view, bRadiosityEnabled );
}
}
else
bResetLightAccum = true;
}
else
bResetLightAccum = true;
 
CViewSetup lightingView = view;
 
if ( building_cubemaps.GetBool() )
engine->GetScreenSize( lightingView.width, lightingView.height );
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport( GetDefRT_Lightaccum() );
 
if ( bResetLightAccum )
{
pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
pRenderContext->ClearBuffers( true, false );
}
else
DrawLightPassFullscreen( GetDeferredManager()->GetDeferredMaterial( DEF_MAT_LIGHT_GLOBAL ), lightingView.width, lightingView.height );
 
pRenderContext->PopRenderTargetAndViewport();
pRenderContext->PushRenderTargetAndViewport( GetDefRT_Lightaccum2() );
 
pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
pRenderContext->ClearBuffers( true, false );
 
pRenderContext.SafeRelease();
 
GetLightingManager()->RenderLights( lightingView, this );
 
if ( bRadiosityEnabled )
EndRadiosity( view );
 
pRenderContext.GetFrom( materials );
pRenderContext->PopRenderTargetAndViewport();
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::ResetCascadeDelay()
{
for ( int i = 0; i < SHADOW_NUM_CASCADES; i++ )
m_flRenderDelay[i] = 0;
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::RenderCascadedShadows( const CViewSetup &view, const bool bEnableRadiosity )
{
for ( int i = 0; i < SHADOW_NUM_CASCADES; i++ )
{
const cascade_t &cascade = GetCascadeInfo(i);
const bool bDoRadiosity = bEnableRadiosity && cascade.bOutputRadiosityData;
const int iRadTarget = cascade.iRadiosityCascadeTarget;
 
float delta = m_flRenderDelay[i] - gpGlobals->curtime;
if ( delta > 0.0f && delta < 1.0f )
{
if ( bDoRadiosity )
PerformRadiosityGlobal( iRadTarget, view );
continue;
}
 
m_flRenderDelay[i] = gpGlobals->curtime + cascade.flUpdateDelay;
 
#if CSM_USE_COMPOSITED_TARGET == 0
int textureIndex = i;
#else
int textureIndex = 0;
#endif
 
CRefPtr<COrthoShadowBlendView> pOrthoDepth = new COrthoShadowBlendView( this, i );
pOrthoDepth->Setup( view, GetShadowDepthRT_Ortho( textureIndex ), GetShadowColorRT_Ortho( textureIndex ) );
if ( bDoRadiosity )
{
pOrthoDepth->SetRadiosityOutputEnabled( true );
pOrthoDepth->SetupRadiosityTargets( GetRadiosityAlbedoRT_Ortho( textureIndex ),
GetRadiosityNormalRT_Ortho( textureIndex ) );
}
AddViewToScene( pOrthoDepth );
 
if ( bDoRadiosity )
PerformRadiosityGlobal( iRadTarget, view );
}
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::DrawLightShadowView( const CViewSetup &view, int iDesiredShadowmap, def_light_t *l )
{
CViewSetup setup;
setup.origin = l->pos;
setup.angles = l->ang;
setup.m_bOrtho = false;
setup.m_flAspectRatio = 1;
setup.x = setup.y = 0;
 
Vector origins[2] = { view.origin, l->pos };
render->ViewSetupVis( false, 2, origins );
 
switch ( l->iLighttype )
{
default:
Assert( 0 );
break;
case DEFLIGHTTYPE_POINT:
{
CRefPtr<CDualParaboloidShadowBlendView> pDPView0 = new CDualParaboloidShadowBlendView( this, l, false );
pDPView0->Setup( setup, GetShadowDepthRT_DP( iDesiredShadowmap ), GetShadowColorRT_DP( iDesiredShadowmap ) );
AddViewToScene( pDPView0 );
 
CRefPtr<CDualParaboloidShadowBlendView> pDPView1 = new CDualParaboloidShadowBlendView( this, l, true );
pDPView1->Setup( setup, GetShadowDepthRT_DP( iDesiredShadowmap ), GetShadowColorRT_DP( iDesiredShadowmap ) );
AddViewToScene( pDPView1 );
}
break;
case DEFLIGHTTYPE_SPOT:
{
CRefPtr<CSpotLightShadowBlendView> pProjView = new CSpotLightShadowBlendView( this, l, iDesiredShadowmap );
pProjView->Setup( setup, GetShadowDepthRT_Proj( iDesiredShadowmap ), GetShadowColorRT_Proj( iDesiredShadowmap ) );
AddViewToScene( pProjView );
}
break;
}
}
#endif
</source>
And around lines 2056 inside void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatToDraw ) put the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
const bool bDeferredActive = GetDeferredManager()->IsDeferredRenderingEnabled();
#endif
</source>
Now around lines 2060 replace m_CurrentView = view; with the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
CViewSetup worldView = view;
 
if( bDeferredActive )
{
CLightingEditor *pLightEditor = GetLightingEditor();
 
if ( pLightEditor->IsEditorActive() && !building_cubemaps.GetBool() )
pLightEditor->GetEditorView( &worldView.origin, &worldView.angles );
else
pLightEditor->SetEditorView( &worldView.origin, &worldView.angles );
 
}
 
m_CurrentView = worldView;
#else
const CViewSetup &worldView = view;
m_CurrentView = view;
#endif
</source>
And around lines 2147 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if( bDeferredActive )
{
ProcessDeferredGlobals( worldView );
GetLightingManager()->LightSetup( worldView );
}
#endif
</source>
And around lines 2208 paste this code as well. Inside void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatToDraw ).
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if( bDeferredActive )
{
// must happen before teardown
CLightingEditor *pLightEditor = GetLightingEditor();
pLightEditor->OnRender();
 
GetLightingManager()->LightTearDown();
}
#endif
</source>
Around line 4119 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
void CRendering3dView::PushComposite()// @Deferred - Biohazard
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_COMPOSITION );
}
 
void CRendering3dView::PopComposite()// @Deferred - Biohazard
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_INVALID );
}
 
void CRendering3dView::PushGBuffer( bool bInitial, float zScale, bool bClearDepth ) // @Deferred - Biohazard
{
ITexture *pNormals = GetDefRT_Normals();
ITexture *pDepth = GetDefRT_Depth();
 
CMatRenderContextPtr pRenderContext( materials );
 
pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
 
if ( bInitial )
{
pRenderContext->PushRenderTargetAndViewport( pDepth );
pRenderContext->ClearBuffers( true, false );
pRenderContext->PopRenderTargetAndViewport();
}
 
#if DEFCFG_DEFERRED_SHADING == 1 // @Deferred - Biohazard
pRenderContext->PushRenderTargetAndViewport( GetDefRT_Albedo() );
#else
pRenderContext->PushRenderTargetAndViewport( pNormals );
#endif
 
if ( bClearDepth )
pRenderContext->ClearBuffers( false, true );
 
pRenderContext->SetRenderTargetEx( 1, pDepth );
 
#if DEFCFG_DEFERRED_SHADING == 1/ / @Deferred - Biohazard
pRenderContext->SetRenderTargetEx( 2, pNormals );
pRenderContext->SetRenderTargetEx( 3, GetDefRT_Specular() );
#endif
 
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_GBUFFER );
 
struct defData_setZScale
{
public:
float zScale;
 
static void Fire( defData_setZScale d )
{
GetDeferredExt()->CommitZScale( d.zScale );
};
};
 
defData_setZScale data;
data.zScale = zScale;
QUEUE_FIRE( defData_setZScale, Fire, data );
}
 
void CRendering3dView::PopGBuffer() // @Deferred - Biohazard
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_INVALID );
 
pRenderContext->PopRenderTargetAndViewport();
}
#endif
</source>
Around lines 5320 paste the following code at the top of void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostRender, ITexture *pRenderTarget, ITexture *pDepthTarget ).
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if ( m_bGBufferPass )
{
m_DrawFlags |= DF_SKIP_WORLD_DECALS_AND_OVERLAYS;
 
#if DEFCFG_DEFERRED_SHADING
m_DrawFlags |= DF_DRAWSKYBOX;
#endif
}
#endif
</source>
Around lines 5357 paste if ( !m_bGBufferPass ) before Enable3dSkyboxFog(); like this.
<source lang=cpp>
if ( !m_bGBufferPass )
Enable3dSkyboxFog();
</source>
At around line 5409 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if ( m_bGBufferPass )
PushGBuffer( true, skyScale );
else
PushComposite();
#endif
</source>
Now around 5427 replace BuildRenderableRenderLists with the following.
<source lang=cpp>
BuildRenderableRenderLists( m_bGBufferPass ? VIEW_SHADOW_DEPTH_TEXTURE : iSkyBoxViewID );
</source>
And around lines 5438 replace DrawTranslucentRenderables and DrawNoZBufferTranslucentRenderables with the following code.
<source lang=cpp>
if ( !m_bGBufferPass )
{
// Iterate over all leaves and render objects in those leaves
DrawTranslucentRenderables( true, false );
DrawNoZBufferTranslucentRenderables();
}
</source>
Around 5486 replace m_pMainView,CGlowOverlay::UpdateSkyOverlays,PixelVisibility_EndCurrentView() with the following.
<source lang=cpp>
if ( !m_bGBufferPass )
{
m_pMainView->DisableFog();
 
CGlowOverlay::UpdateSkyOverlays( zFar, m_bCacheFullSceneState );
 
PixelVisibility_EndCurrentView();
}
</source>
And around lines 5501 paste the following code.
<source lang=cpp>
#ifdef DEFERRED_ENABLED // @Deferred - Biohazard
if ( m_bGBufferPass )
PopGBuffer();
else
PopComposite();
#endif
</source>
Now around lines 5513 replace bool CSkyboxView::Setup with the following code.
<source lang=cpp>
bool CSkyboxView::Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible, bool bGBuffer )
</source>
Around lines 5515 put the following at the top of the bool CSkyboxView::Setup function.
<source lang=cpp>
m_bGBufferPass = bGBuffer;
</source>
Around lines 5547 replace if( r_skybox.GetBool() ) with the following code.
<source lang=cpp>
if( !m_bGBufferPass && r_skybox.GetBool() )
</source>
Around lines 6328 paste the following code inside the void CSimpleWorldView::Draw() function.
<source lang=cpp>
PushComposite();
</source>
NOw around lines 6351 paste the following code inside the void CSimpleWorldView::Draw() function.
<source lang=cpp>
PopComposite();
</source>
Around lines 6411 replace void CBaseWaterView::CSoftwareIntersectionView::Draw() function with the following code.
<source lang=cpp>
void CBaseWaterView::CSoftwareIntersectionView::Draw()
{
PushComposite();
DrawSetup( GetOuter()->m_waterHeight, m_DrawFlags, GetOuter()->m_waterZAdjust );
DrawExecute( GetOuter()->m_waterHeight, CurrentViewID(), GetOuter()->m_waterZAdjust );
PopComposite();
}
</source>
Now around lines 6511 replace DrawSetup,EnableWorldFog(); and DrawExecute with the following code.
<source lang=cpp>
PushComposite();
 
DrawSetup( m_waterHeight, m_DrawFlags, m_waterZAdjust );
EnableWorldFog();
DrawExecute( m_waterHeight, CurrentViewID(), m_waterZAdjust );
 
PopComposite();
</source>
Around lines 6502 replace DrawSetup , EnableWorldFog(); and DrawExecute with the following.
<source lang=cpp>
PushComposite();
 
DrawSetup( m_waterHeight, m_DrawFlags, m_waterZAdjust );
EnableWorldFog();
DrawExecute( m_waterHeight, CurrentViewID(), m_waterZAdjust );
 
PopComposite();
</source>
Now around lines 6562 put the following code at the top of the void CAboveWaterView::CReflectionView::Draw() function.
<source lang=cpp>
PushComposite();
</source>
And again at lines 6585 put the following code before SetupCurrentView( origin, angles, ( view_id_t )nSaveViewID );.
<source lang=cpp>
PopComposite();
</source>
Now around lines 6613 inside the void CAboveWaterView::CRefractionView::Draw() function at the top place the following code.
<source lang=cpp>
PushComposite();
</source>
And again inside the same function place the following code before SetupCurrentView( origin, angles, ( view_id_t )nSaveViewID );.
<source lang=cpp>
PopComposite();
</source>
Around lines 6614 put the following code at the top of the void CAboveWaterView::CIntersectionView::Draw() function.
<source lang=cpp>
PushComposite();
</source>
Now put this code at the end of that same function.
<source lang=cpp>
PopComposite();
</source>
Now around lines 6745 inside the void CUnderWaterView::Draw() function put the following code before DrawSetup( m_waterHeight, m_DrawFlags, m_waterZAdjust );.
<source lang=cpp>
PushComposite();
</source>
And inside the same function put this code after m_ClearFlags = 0;.
<source lang=cpp>
PopComposite();
</source>
Now around lines 6788 inside void CUnderWaterView::CRefractionView::Draw() function place the following code just before DrawSetup.
<source lang=cpp>
PushComposite();
</source>
And inside the same function place the following code after DrawExecute.
<source lang=cpp>
PopComposite();
</source>
And lastly put this at the end of the file. After this we should be done with viewrender.cpp.
<source lang=cpp>
// Deferred views implementations
#ifdef DEFERRED_ENABLED  // @Deferred - Biohazard
 
//-----------------------------------------------------------------------------
// Draws the world + entities
//-----------------------------------------------------------------------------
void CBaseWorldBlendViewDeferred::DrawSetup( float waterHeight, int nSetupFlags, float waterZAdjust, int iForceViewLeaf, bool bShadowDepth )
{
int savedViewID = g_CurrentViewID;
g_CurrentViewID = bShadowDepth ? VIEW_DEFERRED_SHADOW : VIEW_MAIN;
 
bool bViewChanged = AdjustView( waterHeight );
 
if ( bViewChanged )
{
render->Push3DView( *this, 0, NULL, GetFrustum() );
}
 
render->BeginUpdateLightmaps();
 
bool bDrawEntities = ( nSetupFlags & DF_DRAW_ENTITITES ) != 0;
bool bDrawReflection = ( nSetupFlags & DF_RENDER_REFLECTION ) != 0;
BuildWorldRenderLists( bDrawEntities, iForceViewLeaf, ShouldCacheLists(), false, bDrawReflection ? &waterHeight : NULL );
 
PruneWorldListInfo();
 
if ( bDrawEntities )
{
bool bOptimized = bShadowDepth || savedViewID == VIEW_DEFERRED_GBUFFER;
BuildRenderableRenderLists( bOptimized ? VIEW_SHADOW_DEPTH_TEXTURE : savedViewID );
}
 
render->EndUpdateLightmaps();
 
if ( bViewChanged )
{
render->PopView( GetFrustum() );
}
 
g_CurrentViewID = savedViewID;
}
 
 
void CBaseWorldBlendViewDeferred::DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust, bool bShadowDepth )
{
// @MULTICORE (toml 8/16/2006): rethink how, where, and when this is done...
//g_pClientShadowMgr->ComputeShadowTextures( *this, m_pWorldListInfo->m_LeafCount, m_pWorldListInfo->m_pLeafDataList );
 
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
 
int savedViewID = g_CurrentViewID;
g_CurrentViewID = viewID;
 
// Update our render view flags.
int iDrawFlagsBackup = m_DrawFlags;
m_DrawFlags |= m_pMainView->GetBaseDrawFlags();
 
PushView( waterHeight );
 
CMatRenderContextPtr pRenderContext( materials );
 
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 32 );
#endif
 
ITexture *pSaveFrameBufferCopyTexture = pRenderContext->GetFrameBufferCopyTexture( 0 );
pRenderContext->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() );
pRenderContext.SafeRelease();
 
 
Begin360ZPass();
m_DrawFlags |= DF_SKIP_WORLD_DECALS_AND_OVERLAYS;
DrawWorld( waterZAdjust );
m_DrawFlags &= ~DF_SKIP_WORLD_DECALS_AND_OVERLAYS;
if ( m_DrawFlags & DF_DRAW_ENTITITES )
{
DrawOpaqueRenderablesDeferred( m_bDrawWorldNormal );
}
End360ZPass(); // DrawOpaqueRenderables currently already calls End360ZPass. No harm in calling it again to make sure we're always ending it
 
// Only draw decals on opaque surfaces after now. Benefit is two-fold: Early Z benefits on PC, and
// we're pulling out stuff that uses the dynamic VB from the 360 Z pass
// (which can lead to rendering corruption if we overflow the dyn. VB ring buffer).
if ( !bShadowDepth )
{
m_DrawFlags |= DF_SKIP_WORLD;
DrawWorld( waterZAdjust );
m_DrawFlags &= ~DF_SKIP_WORLD;
}
//if ( !m_bDrawWorldNormal )
{
if ( m_DrawFlags & DF_DRAW_ENTITITES )
{
DrawTranslucentRenderables( false, false );
if ( !bShadowDepth )
DrawNoZBufferTranslucentRenderables();
}
else
{
// Draw translucent world brushes only, no entities
DrawTranslucentWorldInLeaves( false );
}
}
 
pRenderContext.GetFrom( materials );
pRenderContext->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture );
PopView();
 
m_DrawFlags = iDrawFlagsBackup;
 
g_CurrentViewID = savedViewID;
 
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
 
static void DrawOpaqueRenderables_ModelRenderablesDeferred( int nCount, ModelRenderSystemData_t* pModelRenderables )
{
g_pModelRenderSystem->DrawModels( pModelRenderables, nCount, MODEL_RENDER_MODE_NORMAL );
}
 
static void DrawOpaqueRenderables_NPCsDeferred( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bNoDecals )
{
DrawOpaqueRenderables_Range( nCount, ppEntities, bNoDecals );
}
 
static void DrawOpaqueRenderables_DrawStaticPropsDeferred( int nCount, CClientRenderablesList::CEntry **ppEntities )
{
if ( nCount == 0 )
return;
 
float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
render->SetColorModulation( one );
render->SetBlend( 1.0f );
const int MAX_STATICS_PER_BATCH = 512;
IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ];
RenderableInstance_t pInstances[ MAX_STATICS_PER_BATCH ];
int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH;
 
for( int i = 0; i < nCount; ++i )
{
CClientRenderablesList::CEntry *itEntity = ppEntities[i];
if ( itEntity->m_pRenderable )
NULL;
else
continue;
 
pInstances[ numScheduled ] = itEntity->m_InstanceData;
pStatics[ numScheduled ++ ] = itEntity->m_pRenderable;
if ( -- numAvailable > 0 )
continue; // place a hint for compiler to predict more common case in the loop
staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, false, vcollide_wireframe.GetBool() );
numScheduled = 0;
numAvailable = MAX_STATICS_PER_BATCH;
}
if ( numScheduled )
staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, false, vcollide_wireframe.GetBool() );
}
 
void CBaseWorldBlendViewDeferred::DrawOpaqueRenderablesDeferred( bool bNoDecals )
{
VPROF("CViewRender::DrawOpaqueRenderables" );
 
if( !r_drawopaquerenderables.GetBool() )
return;
 
if( !m_pMainView->ShouldDrawEntities() )
return;
 
render->SetBlend( 1 );
 
//
// Prepare to iterate over all leaves that were visible, and draw opaque things in them.
//
RopeManager()->ResetRenderCache();
g_pParticleSystemMgr->ResetRenderCache();
 
// Categorize models by type
int nOpaqueRenderableCount = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE];
CUtlVector< CClientRenderablesList::CEntry* > brushModels( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount );
CUtlVector< CClientRenderablesList::CEntry* > staticProps( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount );
CUtlVector< CClientRenderablesList::CEntry* > otherRenderables( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount );
CClientRenderablesList::CEntry *pOpaqueList = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE];
for ( int i = 0; i < nOpaqueRenderableCount; ++i )
{
switch( pOpaqueList[i].m_nModelType )
{
case RENDERABLE_MODEL_BRUSH: brushModels.AddToTail( &pOpaqueList[i] ); break;
case RENDERABLE_MODEL_STATIC_PROP: staticProps.AddToTail( &pOpaqueList[i] ); break;
default: otherRenderables.AddToTail( &pOpaqueList[i] ); break;
}
}
 
//
// First do the brush models
//
DrawOpaqueRenderables_DrawBrushModels( brushModels.Count(), brushModels.Base(), bNoDecals );
 
// Move all static props to modelrendersystem
bool bUseFastPath = ( cl_modelfastpath.GetInt() != 0 );
 
//
// Sort everything that's not a static prop
//
int nStaticPropCount = staticProps.Count();
int numOpaqueEnts = otherRenderables.Count();
CUtlVector< CClientRenderablesList::CEntry* > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry **)stackalloc( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts );
CUtlVector< ModelRenderSystemData_t > arrModelRenderables( (ModelRenderSystemData_t *)stackalloc( ( numOpaqueEnts + nStaticPropCount ) * sizeof( ModelRenderSystemData_t ) ), numOpaqueEnts + nStaticPropCount );
 
// Queue up RENDER_GROUP_OPAQUE_ENTITY entities to be rendered later.
CClientRenderablesList::CEntry *itEntity;
if( r_drawothermodels.GetBool() )
{
MDLCACHE_CRITICAL_SECTION();
for ( int i = 0; i < numOpaqueEnts; ++i )
{
itEntity = otherRenderables[i];
if ( !itEntity->m_pRenderable )
continue;
 
IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown();
IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable();
C_BaseEntity *pEntity = pUnknown->GetBaseEntity();
 
// FIXME: Strangely, some static props are in the non-static prop bucket
// which is what the last case in this if statement is for
if ( bUseFastPath && pModelRenderable )
{
ModelRenderSystemData_t data;
data.m_pRenderable = itEntity->m_pRenderable;
data.m_pModelRenderable = pModelRenderable;
data.m_InstanceData = itEntity->m_InstanceData;
arrModelRenderables.AddToTail( data );
otherRenderables.FastRemove( i );
--i; --numOpaqueEnts;
continue;
}
 
if ( !pEntity )
continue;
 
if ( pEntity->IsNPC() || pEntity->IsUnit() )
{
arrRenderEntsNpcsFirst.AddToTail( itEntity );
otherRenderables.FastRemove( i );
--i; --numOpaqueEnts;
continue;
}
}
}
 
// Queue up the static props to be rendered later.
for ( int i = 0; i < nStaticPropCount; ++i )
{
itEntity = staticProps[i];
if ( !itEntity->m_pRenderable )
continue;
 
IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown();
IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable();
if ( !bUseFastPath || !pModelRenderable )
continue;
 
ModelRenderSystemData_t data;
data.m_pRenderable = itEntity->m_pRenderable;
data.m_pModelRenderable = pModelRenderable;
data.m_InstanceData = itEntity->m_InstanceData;
arrModelRenderables.AddToTail( data );
 
staticProps.FastRemove( i );
--i; --nStaticPropCount;
}
 
//
// Draw model renderables now (ie. models that use the fast path)
//
DrawOpaqueRenderables_ModelRenderablesDeferred( arrModelRenderables.Count(), arrModelRenderables.Base() );
 
// Turn off z pass here. Don't want non-fastpath models with potentially large dynamic VB requirements overwrite
// stuff in the dynamic VB ringbuffer. We're calling End360ZPass again in DrawExecute, but that's not a problem.
// Begin360ZPass/End360ZPass don't have to be matched exactly.
End360ZPass();
 
//
// Draw static props + opaque entities that aren't using the fast path.
//
DrawOpaqueRenderables_Range( otherRenderables.Count(), otherRenderables.Base(), bNoDecals );
DrawOpaqueRenderables_DrawStaticPropsDeferred( staticProps.Count(), staticProps.Base() );
 
//
// Draw NPCs now
//
DrawOpaqueRenderables_NPCsDeferred( arrRenderEntsNpcsFirst.Count(), arrRenderEntsNpcsFirst.Base(), bNoDecals );
 
//
// Ropes and particles
//
RopeManager()->DrawRenderCache( false );
g_pParticleSystemMgr->DrawRenderCache( false );
}
 
void CGBufferBlendView::Setup( const CViewSetup &view, bool bDrewSkybox )
{
m_fogInfo.m_bEyeInFogVolume = false;
m_bDrewSkybox = bDrewSkybox;
 
BaseClass::Setup( view );
m_bDrawWorldNormal = true;
 
m_ClearFlags = 0;
m_DrawFlags = DF_DRAW_ENTITITES;
 
m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER;
 
#if DEFCFG_DEFERRED_SHADING
if ( !bDrewSkybox )
m_DrawFlags |= DF_DRAWSKYBOX;
#endif
}
 
void CGBufferBlendView::Draw()
{
VPROF( "CViewRender::ViewDrawScene_NoWater" );
 
CMatRenderContextPtr pRenderContext( materials );
PIXEVENT( pRenderContext, "CSimpleWorldViewDeferred::Draw" );
 
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 32 ); //lean toward pixel shader threads
#endif
 
SetupCurrentView( origin, angles, VIEW_DEFERRED_GBUFFER );
 
DrawSetup( 0, m_DrawFlags, 0 );
 
const bool bOptimizedGbuffer = DEFCFG_DEFERRED_SHADING == 0;
DrawExecute( 0, CurrentViewID(), 0, bOptimizedGbuffer );
 
pRenderContext.GetFrom( materials );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
 
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
 
void CGBufferBlendView::PushView( float waterHeight )
{
PushGBuffer( !m_bDrewSkybox );
}
 
void CGBufferBlendView::PopView()
{
PopGBuffer();
}
 
void CBaseShadowBlendView::Setup( const CViewSetup &view, ITexture *pDepthTexture, ITexture *pDummyTexture )
{
m_pDepthTexture = pDepthTexture;
m_pDummyTexture = pDummyTexture;
 
BaseClass::Setup( view );
 
m_bDrawWorldNormal =  true;
 
m_DrawFlags = DF_DRAW_ENTITITES | DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER;
m_ClearFlags = 0;
 
CalcShadowView();
 
m_pCustomVisibility = &shadowVis;
shadowVis.AddVisOrigin( origin );
}
 
void CBaseShadowBlendView::SetupRadiosityTargets( ITexture *pAlbedoTexture, ITexture *pNormalTexture )
{
m_pRadAlbedoTexture = pAlbedoTexture;
m_pRadNormalTexture = pNormalTexture;
}
 
void CBaseShadowBlendView::Draw()
{
int oldViewID = g_CurrentViewID;
SetupCurrentView( origin, angles, VIEW_DEFERRED_SHADOW );
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_SHADOWPASS );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_SHADOW_MODE,
GetShadowMode() );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_SHADOW_RADIOSITY,
m_bOutputRadiosity ? 1 : 0 );
pRenderContext.SafeRelease();
DrawSetup( 0, m_DrawFlags, 0, -1, true );
 
DrawExecute( 0, CurrentViewID(), 0, true );
 
pRenderContext.GetFrom( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_SHADOW_RADIOSITY,
0 );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_RENDER_STAGE,
DEFERRED_RENDER_STAGE_INVALID );
 
g_CurrentViewID = oldViewID;
}
 
bool CBaseShadowBlendView::AdjustView( float waterHeight )
{
CommitData();
 
return true;
}
 
void CBaseShadowBlendView::PushView( float waterHeight )
{
render->Push3DView( *this, 0, m_pDummyTexture, GetFrustum(), m_pDepthTexture );
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport( m_pDummyTexture, m_pDepthTexture, x, y, width, height );
 
#if defined( DEBUG ) || defined( SHADOWMAPPING_USE_COLOR )
pRenderContext->ClearColor4ub( 255, 255, 255, 255 );
pRenderContext->ClearBuffers( true, true );
#else
pRenderContext->ClearBuffers( false, true );
#endif
 
if ( m_bOutputRadiosity )
{
Assert( !IsErrorTexture( m_pRadAlbedoTexture ) );
Assert( !IsErrorTexture( m_pRadNormalTexture ) );
 
pRenderContext->SetRenderTargetEx( 1, m_pRadAlbedoTexture );
pRenderContext->SetRenderTargetEx( 2, m_pRadNormalTexture );
}
}
 
void CBaseShadowBlendView::PopView()
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PopRenderTargetAndViewport();
 
render->PopView( GetFrustum() );
}
 
void CBaseShadowBlendView::SetRadiosityOutputEnabled( bool bEnabled )
{
m_bOutputRadiosity = bEnabled;
}
 
//#define ORTHO_TESTVALUES_DEBUG
 
void COrthoShadowBlendView::CalcShadowView()
{
const cascade_t &m_data = GetCascadeInfo( iCascadeIndex );
Vector mainFwd;
AngleVectors( angles, &mainFwd );
 
lightData_Global_t state = GetActiveGlobalLightState();
QAngle lightAng;
VectorAngles( -state.vecLight.AsVector3D(), lightAng );
 
Vector viewFwd, viewRight, viewUp;
AngleVectors( lightAng, &viewFwd, &viewRight, &viewUp );
 
#ifdef ORTHO_TESTVALUES_DEBUG
float flProjectionSize = m_data.flProjectionSize;
if( iCascadeIndex == 1 )
{
static ConVar def_test_ortho1("def_test_ortho1", "0");
if( def_test_ortho1.GetInt() != 0 )
flProjectionSize = def_test_ortho1.GetFloat();
}
else
{
static ConVar def_test_ortho0("def_test_ortho0", "0");
if( def_test_ortho0.GetInt() != 0 )
flProjectionSize = def_test_ortho0.GetFloat();
}
 
int iResolution = m_data.iResolution;
if( iCascadeIndex == 1 )
{
static ConVar def_test_add_res1("def_test_add_res1", "0");
if( def_test_add_res1.GetInt() != 0 )
iResolution = def_test_add_res1.GetInt();
}
else
{
static ConVar def_test_add_res0("def_test_add_res0", "0");
if( def_test_add_res0.GetInt() != 0 )
iResolution = def_test_add_res0.GetInt();
}
 
const float halfOrthoSize = flProjectionSize * 0.5f;
#else
const float halfOrthoSize = m_data.flProjectionSize * 0.5f;
#endif // ORTHO_TESTVALUES_DEBUG
 
origin += -viewFwd * m_data.flOriginOffset +
viewUp * halfOrthoSize -
viewRight * halfOrthoSize +
mainFwd * halfOrthoSize;


angles = lightAng;
[[Category:Programming]]
 
[[Category:Shaders|*]]
x = 0;
y = 0;
#ifndef ORTHO_TESTVALUES_DEBUG
height = m_data.iResolution;
width = m_data.iResolution;
#else
height = width = iResolution;
#endif // ORTHO_TESTVALUES_DEBUG
 
m_bOrtho = true;
m_OrthoLeft = 0;
#ifndef ORTHO_TESTVALUES_DEBUG
m_OrthoTop = -m_data.flProjectionSize;
m_OrthoRight = m_data.flProjectionSize;
#else
m_OrthoTop = -flProjectionSize;
m_OrthoRight = flProjectionSize;
#endif // ORTHO_TESTVALUES_DEBUG
m_OrthoBottom = 0;
 
zNear = zNearViewmodel = 0;
zFar = zFarViewmodel = m_data.flFarZ;
m_flAspectRatio = 0;
 
#ifndef ORTHO_TESTVALUES_DEBUG
float mapping_world = m_data.flProjectionSize / m_data.iResolution;
#else
float mapping_world = flProjectionSize / iResolution;
#endif // ORTHO_TESTVALUES_DEBUG
origin -= fmod( DotProduct( viewRight, origin ), mapping_world ) * viewRight;
origin -= fmod( DotProduct( viewUp, origin ), mapping_world ) * viewUp;
 
origin -= fmod( DotProduct( viewFwd, origin ), GetDepthMapDepthResolution( zFar - zNear ) ) * viewFwd;
 
#if CSM_USE_COMPOSITED_TARGET
x = m_data.iViewport_x;
y = m_data.iViewport_y;
 
#ifdef ORTHO_TESTVALUES_DEBUG
if( iCascadeIndex == 1 )
{
static ConVar def_test_add_x1("def_test_add_x1", "0");
static ConVar def_test_add_y1("def_test_add_y1", "0");
x += def_test_add_x1.GetInt();
y += def_test_add_y1.GetInt();
}
else
{
static ConVar def_test_add_x0("def_test_add_x0", "0");
static ConVar def_test_add_y0("def_test_add_y0", "0");
x += def_test_add_x0.GetInt();
y += def_test_add_y0.GetInt();
}
#endif // ORTHO_TESTVALUES_DEBUG
#endif
}
 
void COrthoShadowBlendView::CommitData()
{
struct sendShadowDataOrtho
{
shadowData_ortho_t data;
int index;
static void Fire( sendShadowDataOrtho d )
{
IDeferredExtension *pDef = GetDeferredExt();
pDef->CommitShadowData_Ortho( d.index, d.data );
};
};
 
const cascade_t &data = GetCascadeInfo( iCascadeIndex );
 
Vector fwd, right, down;
AngleVectors( angles, &fwd, &right, &down );
down *= -1.0f;
 
Vector vecScale( m_OrthoRight, abs( m_OrthoTop ), zFar - zNear );
 
sendShadowDataOrtho shadowData;
shadowData.index = iCascadeIndex;
 
#if CSM_USE_COMPOSITED_TARGET
shadowData.data.iRes_x = CSM_COMP_RES_X;
shadowData.data.iRes_y = CSM_COMP_RES_Y;
#else
shadowData.data.iRes_x = width;
shadowData.data.iRes_y = height;
#endif
 
shadowData.data.vecSlopeSettings.Init(
data.flSlopeScaleMin, data.flSlopeScaleMax, data.flNormalScaleMax, 1.0f / zFar
);
shadowData.data.vecOrigin.Init( origin );
 
Vector4D matrix_scale_offset( 0.5f, -0.5f, 0.5f, 0.5f );
 
#if CSM_USE_COMPOSITED_TARGET
shadowData.data.vecUVTransform.Init( x / (float)CSM_COMP_RES_X,
y / (float)CSM_COMP_RES_Y,
width / (float) CSM_COMP_RES_X,
height / (float) CSM_COMP_RES_Y );
#endif
 
VMatrix a,b,c,d,screenToTexture;
render->GetMatricesForView( *this, &a, &b, &c, &d );
MatrixBuildScale( screenToTexture, matrix_scale_offset.x,
matrix_scale_offset.y,
1.0f );
 
screenToTexture[0][3] = matrix_scale_offset.z;
screenToTexture[1][3] = matrix_scale_offset.w;
 
MatrixMultiply( screenToTexture, c, shadowData.data.matWorldToTexture );
 
QUEUE_FIRE( sendShadowDataOrtho, Fire, shadowData );
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_SHADOW_INDEX, iCascadeIndex );
}
 
 
bool CDualParaboloidShadowBlendView::AdjustView( float waterHeight )
{
BaseClass::AdjustView( waterHeight );
 
// HACK: when pushing our actual view the renderer fails building the worldlist right!
// So we can't. Shit.
return false;
}
 
void CDualParaboloidShadowBlendView::PushView( float waterHeight )
{
BaseClass::PushView( waterHeight );
}
 
void CDualParaboloidShadowBlendView::PopView()
{
BaseClass::PopView();
}
 
void CDualParaboloidShadowBlendView::CalcShadowView()
{
float flRadius = m_pLight->flRadius;
 
m_bOrtho = true;
m_OrthoTop = m_OrthoLeft = -flRadius;
m_OrthoBottom = m_OrthoRight = flRadius;
 
int dpsmRes = GetShadowResolution_Point();
 
width = dpsmRes;
height = dpsmRes;
 
zNear = zNearViewmodel = 0;
zFar = zFarViewmodel = flRadius;
 
if ( m_bSecondary )
{
y = dpsmRes;
 
Vector fwd, up;
AngleVectors( angles, &fwd, NULL, &up );
VectorAngles( -fwd, up, angles );
}
}
 
 
void CSpotLightShadowBlendView::CalcShadowView()
{
float flRadius = m_pLight->flRadius;
 
int spotRes = GetShadowResolution_Spot();
 
width = spotRes;
height = spotRes;
 
zNear = zNearViewmodel = DEFLIGHT_SPOT_ZNEAR;
zFar = zFarViewmodel = flRadius;
 
fov = fovViewmodel = m_pLight->GetFOV();
}
 
void CSpotLightShadowBlendView::CommitData()
{
struct sendShadowDataProj
{
shadowData_proj_t data;
int index;
static void Fire( sendShadowDataProj d )
{
IDeferredExtension *pDef = GetDeferredExt();
pDef->CommitShadowData_Proj( d.index, d.data );
};
};
 
Vector fwd;
AngleVectors( angles, &fwd );
 
sendShadowDataProj data;
data.index = m_iIndex;
data.data.vecForward.Init( fwd );
data.data.vecOrigin.Init( origin );
// slope min, slope max, normal max, depth
//data.data.vecSlopeSettings.Init( 0.005f, 0.02f, 3, zFar );
data.data.vecSlopeSettings.Init( 0.001f, 0.005f, 3, 0 );
 
QUEUE_FIRE( sendShadowDataProj, Fire, data );
 
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_DEFERRED_SHADOW_INDEX, m_iIndex );
}
#endif
</source>
 
==Serverside==
{{todo}}
==Shared==
{{todo}}
==Shaders==
{{todo}}
{{cleanup|Actually properly research what is needed for this implementation. Until someone actually decides to the hard work this section should be dedicated to the stuff that is most definietly needed for this implementation to work. And when thats done this notice should be deleted.
[https://github.com/Sandern/lambdawars Lamba War's source code]
Reference project : [https://github.com/SCell555/sdk-2013-deferred Source 2013MP deferred] this project uses the older Alien swarm deferred's version but it still should give us some idea what needs to be changed.
 
===Cpp files and header files needed to be modified or added===
*Client
**cdll_client_int.cpp ,modification needed
**viewrender.h ,modification needed
**viewrender.cpp ,modification needed
**flashlighteffect.h ,modification needed
**flashlighteffect.cpp ,modification needed
**c_entityflame.cpp ,modification needed although it's not necessary it's still a quality of life feature
**deferred ,This folder contents are needed which are:
***cascade_t.cpp
***cascade_t.h
***DefCookieProjectable.cpp
***DefCookieProjectable.h
***DefCookieTexture.cpp
***DefCookieTexture.h
***IDefCookie.h
***IDeferredExtClient.cpp
***cdeferred_manager_client.cpp
***cdeferred_manager_client.h
***clight_editor.cpp
***clight_editor.h
***clight_manager.cpp
***clight_manager.h
***def_light_t.cpp
***def_light_t.h
***deferred_client_common.cpp
***deferred_client_common.h
***deferred_rt.cpp
***deferred_rt.h
***flashlighteffect_deferred.cpp
***flashlighteffect_deferred.h
***viewrender_deferred.cpp
***viewrender_deferred.h
***vgui ,This folder is also needed
****projectable_factory.cpp
****projectable_factory.h
****vgui_deferred.h
****vgui_editor_controls.cpp
****vgui_editor_controls.h
****vgui_editor_props.cpp
****vgui_editor_props.h
****vgui_editor_root.cpp
****vgui_marquee.cpp
****vgui_marquee.h
****vgui_particles.cpp
****vgui_particles.h
****vgui_particles.cpp
****vgui_projectable.cpp
****vgui_projectable.h
*Server
**gameinterface.cpp ,modification needed
**lights.cpp ,modification needed
**EntityFlame.h ,modification needed although it's not necessary it's still a quality of life feature
**deferred ,This folder contents are needed which are:
***cdeferred_manager_server.cpp
***deferred_server_common.h
***cdeferred_manager_server.h
*Shared
**deferred ,This folder contents are needed which are:
***CDefLight.cpp
***CDefLight.h
***CDefLightContainer.cpp
***CDefLightContainer.h
***CDefLightGlobal.cpp
***CDefLightGlobal.h
***deferred_shared_common.cpp
***deferred_shared_common.h
***ssemath_ext.h
*Public
**renderparm.h ,modification needed
===Shaders===
*common_deferred_fxc.h
*deferred_context.h
*deferred_global_common.h
*deferred_includes.h
*deferred_utility.h
*defpass_composite.h
*defpass_gbuffer.h
*defpass_shadow.h
*IDeferredExt.h
*lighting_helper.h
*lighting_pass_basic.h
*lighting_pass_volum.h
*lightshafts_helper.h
*vertexlitgeneric_dx9_helper.h
*lightmappedgeneric_deferred_ps30.h
*lightmappedgeneric_dx9_helper.h
*debug_lightingctrl.cpp
*debug_radiosity_grid.cpp
*deferred_decalModulate.cpp
*deferred_model.cpp
*deferred_brush.cpp
*defpass_composite.cpp
*deferred_utility.cpp
*defpass_gbuffer.cpp
*defpass_shadow.cpp
*GlobalLitGeneric.cpp
*IDeferredExt.cpp
*lighting_global.cpp
*lighting_pass_basic.cpp
*lighting_pass_volum.cpp
*lighting_volume.cpp
*lighting_world.cpp
*radiosity_blend.cpp
*radiosity_global.cpp
*radiosity_propagate.cpp
*volume_prepass.cpp
*volume_blend.cpp
*unlitgeneric_dx9.cpp
*vertexlitgeneric_dx9.cpp
*vertexlitgeneric_dx9_helper.cpp
*lightmappedgeneric_dx9.cpp
*lightmappedgeneric_dx9_helper.cpp
*lightmappedgeneric_dx9_deferred_helper.cpp
*phong_dx9_helper.cpp
*gbuffer_vs30.fxc
*gbuffer_ps30.fxc
*gbuffer_defshading_ps30.fxc
*shadowpass_vs30.fxc
*shadowpass_ps30.fxc
*composite_vs30.fxc
*composite_ps30.fxc
*defconstruct_vs30.fxc
*decalmodulate_vs20.fxc
*decalmodulate_ps2x.fxc
*lightingpass_global_ps30.fxc
*lightingpass_point_ps30.fxc
*lightingpass_spot_ps30.fxc
*screenspace_shading_ps30.fxc
*screenspace_combine_ps30.fxc
*volume_blend_ps30.fxc
*volume_prepass_vs30.fxc
*volume_prepass_ps30.fxc
*volumpass_point_ps30.fxc
*volumpass_spot_ps30.fxc
*radiosity_gen_global_ps30.fxc
*radiosity_gen_vs30.fxc
*radiosity_propagate_ps30.fxc
*radiosity_propagate_vs30.fxc
*radiosity_blend_ps30.fxc
*screenspace_vs20.fxc
*gaussianblur_6_ps30.fxc
*debug_shadow_ortho_ps30.fxc
*debug_lighting_ctrl_ps30.fxc
*debug_radiosity_grid_ps30.fxc
*debug_radiosity_grid_vs30.fxc
*globallitgeneric_ps30.fxc
*globallitgeneric_vs30.fxc
*phong_deferred_ps30.fxc
*lightmappedgeneric_deferred_vs30.fxc
*lightmappedgeneric_deferred_ps30.fxc
*vertexlit_and_unlit_generic_bump_deferred_ps30.fxc
}}
 
== External links ==
*[https://github.com/Sandern/lambdawars Lambda war's source code]

Latest revision as of 10:24, 22 August 2024

English (en)Translate (Translate)
Warning.pngWarning:This guide aims to provide a quick and simple way of implementing this feature, but the implementation itself needs further work to make it stable, robust, and usable.
Icon-under construction-blue.png
This is a draft page. It is a work in progress open to editing by anyone.
Remember to check for any notes left by the tagger at this article's talk page.


Having deferred lighting in your mod comes with quite a lot of upsides as with it, you can have volumetric lighting, realtime shadows and lighting from the sun and from other light sources, while still keeping the performance steady, which is something that Source's regular dynamic light entities would be incapable of (aka projected textures). However, keep in mind that all publicly available implementations are still unstable and can cause visual glitches and slowdowns.

Background and resources used

This implementation was originally made by Kristjan Skutta aka Biohazard90, based on Alien Swarm's deferred shading approach, then it was expended by the Lambda Wars team, and finally it was ported to Source 2013 by SCell555. This implementation has been since ported into various Source SDK forks, but for the purpose of simplicity, this tutorial will focus on adding deferred lighting to the source-sdk-vs2022 repo, as it is an almost unchanged Source 2013 Source 2013 SDK repo with added compatibility for modern editions of Visual Studio. The end result should be identical, or at least very similar, to source-sdk-vs2022-deferred, which is an implementation of deferred lighting for the source-sdk-vs2022 repo (both MP and SP).

Preparation

Using the right tools will make copying code from one repo to another a relatively quick and painless process.

  1. Download WinMerge or a similar diff/merge tool.
  2. Use git to clone the two repos used in this tutorial (or download them as ZIP files): source-sdk-vs2022 and source-sdk-vs2022-deferred.
  3. Make sure the source-sdk-vs2022 repo compiles successfully in your installation of Visual Studio 2022 before you modify it.

The repo also includes diff files which should allow you to easily apply all of the necessary changes per branch as long as you know how to use them. This tutorial will focus on the slightly more user-friendly WinMerge approach.

Implementation using WinMerge

You can copy all the necessary files and apply changes to the code by comparing both folders in WinMerge. The source-sdk-vs2022-deferred aims to modify as few files as possible, so the process should be relatively quick.

The process is very similar for both the MP and SP branches. In both cases, the new files that need to be transferred are contained in separate deferred folders in the client, server, and shared folders in /game, as well as in /materialsystem/stdshaders and /public. The files that need to be modified are contained in the same folders as well as in /tier1 and the root folder (creategameprojects.bat).

File modifications

Apply the modifications as shown by WinMerge. Consult the manual if you're unsure how to do this, though you could probably do this solely with the Ctrl and arrow keys if you wanted to. The majority of the modifications will involve files in /game/client and /materialsystem/stdshaders. All new entries for files related to deferred lighting are added in *_base.vpc files, and the creategameprojects.bat file is changed to generate the Visual Studio solution for the shader DLL as well.

Once you've applied all the changes shown by WinMerge, run the creategameprojects.bat file to recreate the Visual Studio solutions. At this stage, you should be able to compile the client and server DLLs.

Compiling shaders with ShaderCompile

In order to compile the shader DLL, you will need to generate the .inc shader include files. This tutorial uses SCell555's ShaderCompile tool to simplify the process.

  1. Download the ShaderCompile 7z file from the latest release on the GitHub releases page.
  2. Follow the "getting started" section in the readme of the repo (replace cshader.h and buildshaders.bat, modify game_shader_dx9_base.vpc and buildsdkshaders.bat, copy over ShaderCompile.exe and process_shaders.ps1).
  3. Run e.g. buildhl2mpshaders.bat to build the shaders.

If the batch file refuses to run, open it in a text editor and encase the paths for GAMEDIR and SOURCEDIR in quotation marks, i.e. so that it looks similar to this (SDKBINDIR is irrelevant here):

@echo off
setlocal

rem ================================
rem ==== MOD PATH CONFIGURATIONS ===

rem == Set the absolute path to your mod's game directory here ==
set GAMEDIR="%cd%\..\..\..\game\mod_hl2mp"

rem == Set the relative or absolute path to Source SDK Base 2013 Singleplayer\bin ==
set SDKBINDIR=

rem ==  Set the Path to your mod's root source code ==
rem This should already be correct, accepts relative paths only!
set SOURCEDIR="..\.."

rem ==== MOD PATH CONFIGURATIONS END ===
rem ====================================


call buildsdkshaders.bat

Once you've compiled all the shaders, launch the creategameprojects.bat file again. You should now be able to compile the shader DLL.

Notes

  • At this point, deferred lighting should be implemented. If not, either go through the steps again or simply use the source-sdk-vs2022-deferred repo as your starting point. Make sure to read through the notes in the readme.
  • Don't be discouraged by how janky it is - use it as motivation to become the first person to create a robust, stable deferred lighting implementation without making it closed source.

External links