source-engine/engine/gl_rmain.cpp

1255 lines
38 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "render_pch.h"
#include "client.h"
#include "sound.h"
#include "debug_leafvis.h"
#include "cdll_int.h"
#include "enginestats.h"
#include "ivrenderview.h"
#include "studio.h"
#include "l_studio.h"
#include "r_areaportal.h"
#include "materialsystem/materialsystem_config.h"
#include "materialsystem/itexture.h"
#include "cdll_engine_int.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "istudiorender.h"
#include "staticpropmgr.h"
#include "tier0/vprof.h"
#include "IOcclusionSystem.h"
#include "con_nprint.h"
#include "debugoverlay.h"
#include "demo.h"
#include "ivideomode.h"
#include "sys_dll.h"
#include "collisionutils.h"
#include "tier1/utlstack.h"
#include "r_decal.h"
#include "cl_main.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifndef _X360
extern ConVar r_waterforceexpensive;
#endif
ConVar r_aspectratio( "r_aspectratio", "0"
#if !defined( _X360 )
, FCVAR_CHEAT
#endif
);
ConVar r_dynamiclighting( "r_dynamiclighting", "1", FCVAR_CHEAT );
extern ConVar building_cubemaps;
extern float scr_demo_override_fov;
extern colorVec R_LightPoint (Vector& p);
CEngineStats g_EngineStats;
//-----------------------------------------------------------------------------
// view origin
//-----------------------------------------------------------------------------
extern Vector g_CurrentViewOrigin, g_CurrentViewForward, g_CurrentViewRight, g_CurrentViewUp;
extern Vector g_MainViewOrigin, g_MainViewForward, g_MainViewRight, g_MainViewUp;
bool g_bCanAccessCurrentView = false;
int d_lightstyleframe[256];
void ProjectPointOnPlane( Vector& dst, const Vector& p, const Vector& normal )
{
float d;
Vector n;
float inv_denom;
inv_denom = 1.0F / DotProduct( normal, normal );
d = DotProduct( normal, p ) * inv_denom;
n[0] = normal[0] * inv_denom;
n[1] = normal[1] * inv_denom;
n[2] = normal[2] * inv_denom;
dst[0] = p[0] - d * n[0];
dst[1] = p[1] - d * n[1];
dst[2] = p[2] - d * n[2];
}
/*
** assumes "src" is normalized
*/
void PerpendicularVector( Vector& dst, const Vector& src )
{
int pos;
int i;
float minelem = 1.0F;
Vector tempvec;
/*
** find the smallest magnitude axially aligned vector
*/
for ( pos = 0, i = 0; i < 3; i++ )
{
if ( fabs( src[i] ) < minelem )
{
pos = i;
minelem = fabs( src[i] );
}
}
tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
tempvec[pos] = 1.0F;
/*
** project the point onto the plane defined by src
*/
ProjectPointOnPlane( dst, tempvec, src );
/*
** normalize the result
*/
VectorNormalize( dst );
}
//-----------------------------------------------------------------------------
// Returns the aspect ratio of the screen
//-----------------------------------------------------------------------------
float GetScreenAspect( )
{
// use the override if set
if ( r_aspectratio.GetFloat() > 0.0f )
return r_aspectratio.GetFloat();
// mikesart: This is just sticking in unnecessary BeginRender/EndRender calls to the queue.
// CMatRenderContextPtr pRenderContext( materials );
IMatRenderContext *pRenderContext = g_pMaterialSystem->GetRenderContext();
int width, height;
pRenderContext->GetRenderTargetDimensions( width, height );
return (height != 0) ? ( (float)width / (float)height ) : 1.0f;
}
/*
====================
CalcFov
====================
*/
void R_DrawScreenRect( float left, float top, float right, float bottom )
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
IMaterial *pMaterial = materials->FindMaterial( "debug/debugportals", TEXTURE_GROUP_OTHER );
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
CMeshBuilder builder;
builder.Begin( pMesh, MATERIAL_LINE_LOOP, 4 );
Vector v1( left, bottom, 0.5 );
Vector v2( left, top, 0.5 );
Vector v3( right, top, 0.5 );
Vector v4( right, bottom, 0.5 );
builder.Position3fv( v1.Base() ); builder.AdvanceVertex();
builder.Position3fv( v2.Base() ); builder.AdvanceVertex();
builder.Position3fv( v3.Base() ); builder.AdvanceVertex();
builder.Position3fv( v4.Base() ); builder.AdvanceVertex();
builder.End( false, true );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
}
void R_DrawPortals()
{
// Draw the portals.
if( !r_DrawPortals.GetInt() )
return;
IMaterial *pMaterial = materials->FindMaterial( "debug/debugportals", TEXTURE_GROUP_OTHER );
CMatRenderContextPtr pRenderContext( materials );
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
worldbrushdata_t *pBrushData = host_state.worldbrush;
for( int i=0; i < pBrushData->m_nAreaPortals; i++ )
{
dareaportal_t *pAreaPortal = &pBrushData->m_pAreaPortals[i];
if( !R_IsAreaVisible( pAreaPortal->otherarea ) )
continue;
CMeshBuilder builder;
builder.Begin( pMesh, MATERIAL_LINES, pAreaPortal->m_nClipPortalVerts );
for( int j=0; j < pAreaPortal->m_nClipPortalVerts; j++ )
{
unsigned short iVert;
iVert = pAreaPortal->m_FirstClipPortalVert + j;
builder.Position3f( VectorExpand( pBrushData->m_pClipPortalVerts[iVert] ) );
builder.Color4f( 0, 0, 0, 1 );
builder.AdvanceVertex();
iVert = pAreaPortal->m_FirstClipPortalVert + (j+1) % pAreaPortal->m_nClipPortalVerts;
builder.Position3f( VectorExpand( pBrushData->m_pClipPortalVerts[iVert] ) );
builder.Color4f( 0, 0, 0, 1 );
builder.AdvanceVertex();
}
builder.End( false, true );
}
// Draw the clip rectangles.
for( int i=0; i < g_PortalRects.Size(); i++ )
{
CPortalRect *pRect = &g_PortalRects[i];
R_DrawScreenRect( pRect->left, pRect->top, pRect->right, pRect->bottom );
}
g_PortalRects.Purge();
}
//-----------------------------------------------------------------------------
//
// Loose collection of functions related to rendering the world in a particular view
//
//-----------------------------------------------------------------------------
class CRender : public IRender
{
public:
CRender();
void FrameBegin( void );
void FrameEnd( void );
void ViewSetupVis( bool novis, int numorigins, const Vector origin[] );
void ViewSetupVisEx( bool novis, int numorigins, const Vector origin[], unsigned int &returnFlags );
void ViewEnd( void );
void ViewDrawFade( byte *color, IMaterial* pMaterial );
IWorldRenderList * CreateWorldList();
void BuildWorldLists( IWorldRenderList *pList, WorldListInfo_t* pInfo, int iForceViewLeaf, const VisOverrideData_t* pVisData, bool bShadowDepth, float *pWaterReflectionHeight );
void DrawWorldLists( IWorldRenderList *pList, unsigned long flags, float waterZAdjust );
void DrawSceneBegin( void );
void DrawSceneEnd( void );
// utility functions
void ExtractMatrices( void );
void ExtractFrustumPlanes( Frustum frustumPlanes );
void OrthoExtractFrustumPlanes( Frustum frustumPlanes );
void OverrideViewFrustum( Frustum custom );
void SetViewport( int x, int y, int w, int h );
// UNDONE: these are temporary functions that will end up on the other
// side of this interface
const Vector &ViewOrigin( ) { return CurrentView().origin; }
const QAngle &ViewAngles( ) { return CurrentView().angles; }
const CViewSetup &ViewGetCurrent( void ) { return CurrentView(); }
const VMatrix &ViewMatrix( void );
const VMatrix &WorldToScreenMatrix( void );
float GetFramerate( void ) { return m_framerate; }
virtual float GetZNear( void ) { return m_zNear; }
virtual float GetZFar( void ) { return m_zFar; }
// Query current fov and view model fov
float GetFov( void ) { return CurrentView().fov; };
float GetFovY( void ) { return m_yFOV; };
float GetFovViewmodel( void ) { return CurrentView().fovViewmodel; };
virtual bool ClipTransformWithProjection ( const VMatrix& worldToScreen, const Vector& point, Vector* pClip );
virtual bool ClipTransform( const Vector& point, Vector* pClip );
virtual bool ScreenTransform( const Vector& point, Vector* pScreen );
virtual void Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes );
virtual void Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes, ITexture* pDepthTexture );
virtual void Push2DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes );
virtual void PopView( Frustum frustumPlanes );
virtual void SetMainView( const Vector &vecOrigin, const QAngle &angles );
virtual void UpdateBrushModelLightmap( model_t *model, IClientRenderable *Renderable );
virtual void BeginUpdateLightmaps( void );
virtual void EndUpdateLightmaps( void );
virtual bool InLightmapUpdate( void ) const;
private:
// Called when a particular view becomes active
void OnViewActive( Frustum frustumPlanes );
// Clear the view (assumes the render target has already been pushed)
void ClearView( CViewSetup &view, int nFlags, ITexture* pRenderTarget, ITexture* pDepthTexture = NULL );
const CViewSetup &CurrentView() const { return m_ViewStack.Top().m_View; }
CViewSetup &CurrentView() { return m_ViewStack.Top().m_View; }
// Stack of view info
struct ViewStack_t
{
CViewSetup m_View;
// matrices
VMatrix m_matrixView;
VMatrix m_matrixProjection;
VMatrix m_matrixWorldToScreen;
bool m_bIs2DView;
bool m_bNoDraw;
};
// Y field of view, calculated from X FOV and screen aspect ratio.
float m_yFOV;
// timing
double m_frameStartTime;
float m_framerate;
float m_zNear;
float m_zFar;
// matrices
VMatrix m_matrixView;
VMatrix m_matrixProjection;
VMatrix m_matrixWorldToScreen;
CUtlStack< ViewStack_t > m_ViewStack;
int m_iLightmapUpdateDepth;
};
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
static CRender gRender;
IRender *g_EngineRenderer = &gRender;
//-----------------------------------------------------------------------------
// Called when the engine is about to begin rendering for any reason
//-----------------------------------------------------------------------------
CRender::CRender()
{
// Make sure the stack isn't empty
int i = m_ViewStack.Push();
memset( &m_ViewStack[i], 0, sizeof( CViewSetup ) );
m_ViewStack[i].m_bIs2DView = true;
m_iLightmapUpdateDepth = 0;
}
//-----------------------------------------------------------------------------
// Called when the engine is about to begin rendering for any reason
//-----------------------------------------------------------------------------
void CRender::FrameBegin( void )
{
if ( host_state.worldmodel )
{
// This has to be before R_AnimateLight because it uses it to
// set the frame number of changed lightstyles
// FIXME: Why isn't this being done in DrawSceneBegin
// or some other client-side simulation of state?
r_framecount++;
R_AnimateLight ();
R_PushDlights();
if (!r_norefresh.GetInt())
{
m_frameStartTime = Sys_FloatTime ();
}
}
UpdateStudioRenderConfig();
g_pStudioRender->BeginFrame();
}
//-----------------------------------------------------------------------------
// Called when the engine has finished rendering
//-----------------------------------------------------------------------------
void CRender::FrameEnd( void )
{
// A debugging overlay that renders all raycasts.
// Why, or why is this being done here instead of
// where all the other debug overlays are being done in the client DLL?
EngineTraceRenderRayCasts();
m_framerate = cl.GetFrameTime();
if ( m_framerate > 0 )
{
m_framerate = 1 / m_framerate;
}
g_pStudioRender->EndFrame();
}
const VMatrix &CRender::ViewMatrix( )
{
// If we aren't in a valid view, then use the last value cached off into the global variable instead
if ( m_ViewStack.Count() > 1 )
{
return m_ViewStack.Top().m_matrixView;
}
return m_matrixView;
}
const VMatrix &CRender::WorldToScreenMatrix( void )
{
// If we aren't in a valid view, then use the last value cached off into the global variable instead
if ( m_ViewStack.Count() > 1 )
{
return m_ViewStack.Top().m_matrixWorldToScreen;
}
return m_matrixWorldToScreen;
}
void CRender::ViewSetupVis( bool novis, int numorigins, const Vector origin[] )
{
unsigned int returnFlags = 0;
ViewSetupVisEx( novis, numorigins, origin, returnFlags );
}
void CRender::ViewSetupVisEx( bool novis, int numorigins, const Vector origin[], unsigned int &returnFlags )
{
Map_VisSetup( host_state.worldmodel, numorigins, origin, novis, returnFlags );
}
//-----------------------------------------------------------------------------
// Called when a particular view becomes active
//-----------------------------------------------------------------------------
void CRender::OnViewActive( Frustum frustumPlanes )
{
const CViewSetup &view = CurrentView();
m_yFOV = CalcFovY( view.fov, view.m_flAspectRatio );
// build the transformation matrix for the given view angles
VectorCopy( view.origin, g_CurrentViewOrigin );
AngleVectors( view.angles, &g_CurrentViewForward, &g_CurrentViewRight, &g_CurrentViewUp );
// g_CurrentViewUp = -g_CurrentViewUp;
g_bCanAccessCurrentView = true;
if ( frustumPlanes )
{
if ( view.m_bOrtho )
{
OrthoExtractFrustumPlanes( frustumPlanes );
}
else
{
ExtractFrustumPlanes( frustumPlanes );
}
OcclusionSystem()->SetView( view.origin, view.fov, m_matrixView, m_matrixProjection, frustumPlanes[ FRUSTUM_NEARZ ] );
}
if ( !m_ViewStack.Top().m_bNoDraw )
{
R_SceneBegin( );
}
// debug, build leaf volume
// NOTE: This is pretty hacky, but I want the leaf based on the main view. The skybox view is reseting
// the g_LeafVis here because it is global. This need to be resolved more correctly some other way!
if ( VectorCompare( g_MainViewOrigin, view.origin ) )
{
LeafVisBuild( view.origin );
}
}
//-----------------------------------------------------------------------------
// Clear the view (assumes the render target has already been pushed)
//-----------------------------------------------------------------------------
void CRender::ClearView( CViewSetup &view, int nFlags, ITexture* pRenderTarget, ITexture* pDepthTexture /* = NULL */ )
{
bool bClearColor = (nFlags & VIEW_CLEAR_COLOR) != 0;
bool bClearDepth = (nFlags & VIEW_CLEAR_DEPTH) != 0;
bool bClearStencil = (nFlags & VIEW_CLEAR_STENCIL) != 0;
bool bForceClearWholeRenderTarget = (nFlags & VIEW_CLEAR_FULL_TARGET) != 0;
bool bObeyStencil = (nFlags & VIEW_CLEAR_OBEY_STENCIL) != 0;
// Handle an initial clear request if asked for
if ( !bClearColor && !bClearDepth && !bClearStencil )
return;
CMatRenderContextPtr pRenderContext( materials );
if ( !bForceClearWholeRenderTarget )
{
if( bObeyStencil )
pRenderContext->ClearBuffersObeyStencil( bClearColor, bClearDepth );
else
pRenderContext->ClearBuffers( bClearColor, bClearDepth, bClearStencil );
}
else
{
// Get the render target dimensions
int nWidth, nHeight;
if ( pRenderTarget )
{
nWidth = pRenderTarget->GetActualWidth();
nHeight = pRenderTarget->GetActualHeight();
}
else
{
materials->GetBackBufferDimensions( nWidth, nHeight );
}
pRenderContext->PushRenderTargetAndViewport( pRenderTarget, pDepthTexture, 0, 0, nWidth, nHeight );
if( bObeyStencil )
pRenderContext->ClearBuffersObeyStencil( bClearColor, bClearDepth );
else
pRenderContext->ClearBuffers( bClearColor, bClearDepth, bClearStencil );
pRenderContext->PopRenderTargetAndViewport( );
}
}
//-----------------------------------------------------------------------------
// Push, pop views
//-----------------------------------------------------------------------------
void CRender::Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes )
{
Push3DView( view, nFlags, pRenderTarget, frustumPlanes, NULL );
}
//-----------------------------------------------------------------------------
// Computes view matrices
//-----------------------------------------------------------------------------
float ComputeViewMatrices( VMatrix *pWorldToView, VMatrix *pViewToProjection, VMatrix *pWorldToProjection, const CViewSetup &viewSetup )
{
float flAspectRatio = viewSetup.m_flAspectRatio;
if ( flAspectRatio == 0.0f )
{
flAspectRatio = (viewSetup.height != 0) ? ( (float)viewSetup.width / (float)viewSetup.height ) : 1.0f;
}
ComputeViewMatrix( pWorldToView, viewSetup.origin, viewSetup.angles );
if ( viewSetup.m_bOrtho )
{
MatrixBuildOrtho( *pViewToProjection, viewSetup.m_OrthoLeft, viewSetup.m_OrthoTop,
viewSetup.m_OrthoRight, viewSetup.m_OrthoBottom, viewSetup.zNear, viewSetup.zFar );
}
else if ( viewSetup.m_bOffCenter ) // Off-center projection, useful for AA jitter and tiled output of posters
{
MatrixBuildPerspectiveOffCenterX( *pViewToProjection, viewSetup.fov, flAspectRatio,
viewSetup.zNear, viewSetup.zFar, viewSetup.m_flOffCenterBottom, viewSetup.m_flOffCenterTop,
viewSetup.m_flOffCenterLeft, viewSetup.m_flOffCenterRight );
}
else if ( viewSetup.m_bViewToProjectionOverride )
{
*pViewToProjection = viewSetup.m_ViewToProjection;
// ...but then override the Z range (needed for correct skybox rendering, etc).
MatrixBuildPerspectiveZRange ( *pViewToProjection, viewSetup.zNear, viewSetup.zFar );
}
else
{
MatrixBuildPerspectiveX( *pViewToProjection, viewSetup.fov, flAspectRatio, viewSetup.zNear, viewSetup.zFar );
}
MatrixMultiply( *pViewToProjection, *pWorldToView, *pWorldToProjection );
return flAspectRatio;
}
// Flip y, screen y goes down
static VMatrix g_ProjectionToOffset( 0.5f, 0.0f, 0.0f, 0.5f,
0.0f, -0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f );
// NOTE: Screen coordinates go from 0->w, 0->h
void ComputeWorldToScreenMatrix( VMatrix *pWorldToScreen, const VMatrix &worldToProjection, const CViewSetup &viewSetup )
{
// First need to transform -1 -> 1 to 0 -> 1 in x and y
// Then transform from 0->1 to x->w+x in x, and 0->1 to y->y+h in y.
VMatrix offsetToPixels( viewSetup.width, 0.0f, 0.0f, viewSetup.x,
0.0f, viewSetup.height, 0.0f, viewSetup.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f );
VMatrix projectionToPixels;
MatrixMultiply( offsetToPixels, g_ProjectionToOffset, projectionToPixels );
MatrixMultiply( projectionToPixels, worldToProjection, *pWorldToScreen );
}
//-----------------------------------------------------------------------------
// Push, pop views
//-----------------------------------------------------------------------------
void CRender::Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes, ITexture* pDepthTexture )
{
Assert( !IsX360() || (pDepthTexture == NULL) ); //Don't render to a depth texture on the 360. Instead, render using a normal depth buffer and use IDirect3DDevice9::Resolve()
int i = m_ViewStack.Push( );
m_ViewStack[i].m_View = view;
m_ViewStack[i].m_bIs2DView = false;
m_ViewStack[i].m_bNoDraw = ( ( nFlags & VIEW_NO_DRAW ) != 0 );
CViewSetup &topView = m_ViewStack[i].m_View;
// Compute aspect ratio if asked for
if ( topView.m_flAspectRatio == 0.0f )
{
topView.m_flAspectRatio = (topView.height != 0) ? ( (float)topView.width / (float)topView.height ) : 1.0f;
}
ViewStack_t &viewStack = m_ViewStack.Top();
topView.m_flAspectRatio = ComputeViewMatrices( &viewStack.m_matrixView,
&viewStack.m_matrixProjection, &viewStack.m_matrixWorldToScreen, topView );
m_zNear = topView.zNear;
m_zFar = topView.zFar; // cache this for queries
ExtractMatrices();
if ( !m_ViewStack[i].m_bNoDraw )
{
CMatRenderContextPtr pRenderContext( materials );
if ( !pRenderTarget )
{
pRenderTarget = pRenderContext->GetRenderTarget();
}
// Push render target and viewport
pRenderContext->PushRenderTargetAndViewport( pRenderTarget, pDepthTexture, topView.x, topView.y, topView.width, topView.height );
// Handle an initial clear request if asked for
ClearView( topView, nFlags, pRenderTarget, pDepthTexture );
pRenderContext->DepthRange( 0, 1 );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadMatrix( m_matrixProjection );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadMatrix( m_matrixView );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
OnViewActive( frustumPlanes );
}
}
void CRender::Push2DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes )
{
int i = m_ViewStack.Push( );
m_ViewStack[i].m_View = view;
m_ViewStack[i].m_bIs2DView = true;
m_ViewStack[i].m_bNoDraw = ( ( nFlags & VIEW_NO_DRAW ) != 0 );
m_ViewStack[i].m_matrixView = m_matrixView;
m_ViewStack[i].m_matrixProjection = m_matrixProjection;
m_ViewStack[i].m_matrixWorldToScreen = m_matrixWorldToScreen;
CViewSetup &topView = m_ViewStack[i].m_View;
g_bCanAccessCurrentView = false;
CMatRenderContextPtr pRenderContext( materials );
if ( !pRenderContext )
{
pRenderTarget = pRenderContext->GetRenderTarget();
}
// Push render target and viewport
pRenderContext->PushRenderTargetAndViewport( pRenderTarget, topView.x, topView.y, topView.width, topView.height );
// Handle an initial clear request if asked for
ClearView( topView, nFlags, pRenderTarget );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Scale( 1, -1, 1 );
pRenderContext->Ortho( 0, 0, topView.width, topView.height, -99999, 99999 );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
}
void CRender::PopView( Frustum frustumPlanes )
{
if ( !m_ViewStack.Top().m_bNoDraw )
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->PopRenderTargetAndViewport( );
}
bool bReset = ( m_ViewStack.Count() > 1 ) ? true : false;
m_ViewStack.Pop();
// Don't pop off the very last view
g_bCanAccessCurrentView = false;
if ( bReset )
{
if ( !m_ViewStack.Top().m_bIs2DView )
{
ExtractMatrices();
OnViewActive( frustumPlanes );
}
}
}
//-----------------------------------------------------------------------------
// Sets the main 3D view (for console commands, sound, etc.)
//-----------------------------------------------------------------------------
void CRender::SetMainView( const Vector &vecOrigin, const QAngle &angles )
{
VectorCopy( vecOrigin, g_MainViewOrigin );
AngleVectors( angles, &g_MainViewForward, &g_MainViewRight, &g_MainViewUp );
}
CUtlVector<LightmapUpdateInfo_t> g_LightmapUpdateList;
CUtlVector<LightmapTransformInfo_t> g_LightmapTransformList;
int __cdecl LightmapPageCompareFunc( const void *pElem0, const void *pElem1 )
{
const LightmapUpdateInfo_t *pSurf0 = (const LightmapUpdateInfo_t *)pElem0;
const LightmapUpdateInfo_t *pSurf1 = (const LightmapUpdateInfo_t *)pElem1;
int page0 = materialSortInfoArray[MSurf_MaterialSortID( (pSurf0->m_SurfHandle) )].lightmapPageID;
int page1 = materialSortInfoArray[MSurf_MaterialSortID( (pSurf1->m_SurfHandle) )].lightmapPageID;
return page0 - page1;
}
void CRender::BeginUpdateLightmaps( void )
{
if ( ++m_iLightmapUpdateDepth == 1)
{
Assert( g_LightmapUpdateList.Count() == 0 );
materials->BeginUpdateLightmaps();
// UNDONE: Move this to an init or constructor?
g_LightmapTransformList.RemoveAll();
int index = g_LightmapTransformList.AddToTail();
g_LightmapTransformList[index].pModel = host_state.worldmodel;
SetIdentityMatrix( g_LightmapTransformList[index].xform );
}
}
void CRender::UpdateBrushModelLightmap( model_t *model, IClientRenderable *pRenderable )
{
AssertOnce( m_iLightmapUpdateDepth );
if( !r_drawbrushmodels.GetBool() || !m_iLightmapUpdateDepth )
return;
R_MarkDlightsOnBrushModel( model, pRenderable );
if ( model->flags & MODELFLAG_HAS_DLIGHT )
{
int transformIndex = g_LightmapTransformList.AddToTail();
LightmapTransformInfo_t &transform = g_LightmapTransformList[transformIndex];
transform.pModel = model;
AngleMatrix( pRenderable->GetRenderAngles(), pRenderable->GetRenderOrigin(), transform.xform );
SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface, model->brush.pShared );
bool bLight = false;
for (int i=0 ; i<model->brush.nummodelsurfaces ; i++, surfID++)
{
if ( MSurf_Flags(surfID) & (SURFDRAW_HASDLIGHT|SURFDRAW_HASLIGHTSYTLES) )
{
LightmapUpdateInfo_t tmp;
tmp.m_SurfHandle = surfID;
tmp.transformIndex = transformIndex;
g_LightmapUpdateList.AddToTail( tmp );
bLight = true;
}
}
if ( !bLight )
{
model->flags &= ~MODELFLAG_HAS_DLIGHT; // don't need to check again unless a dlight hits us
}
}
}
void CRender::EndUpdateLightmaps( void )
{
Assert( m_iLightmapUpdateDepth > 0 );
if ( --m_iLightmapUpdateDepth == 0 )
{
VPROF_BUDGET( "EndUpdateLightmaps", VPROF_BUDGETGROUP_DLIGHT_RENDERING );
if ( g_LightmapUpdateList.Count() && r_dynamiclighting.GetBool() && !r_unloadlightmaps.GetBool() )
{
CMatRenderContextPtr pRenderContext( materials );
ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
dlight_t *pLights = &cl_dlights[0];
// only do the copy when there are valid dlights to process and threading is on
if ( g_bActiveDlights && pCallQueue )
{
// keep a copy of the current dlight state around for the thread to work on
// in parallel. This way the main thread can continue to modify this state without
// generating any bad results
static dlight_t threadDlights[MAX_DLIGHTS*2];
static int threadFrameCount = 0;
pLights = &threadDlights[MAX_DLIGHTS*threadFrameCount];
Q_memcpy( pLights, cl_dlights, sizeof(dlight_t) * MAX_DLIGHTS );
threadFrameCount = (threadFrameCount+1) & 1;
}
qsort( g_LightmapUpdateList.Base(), g_LightmapUpdateList.Count(), sizeof(g_LightmapUpdateList.Element(0)), LightmapPageCompareFunc );
int i;
for ( i = g_LightmapUpdateList.Count()-1; i >= 0; --i )
{
const LightmapUpdateInfo_t &lightmapUpdateInfo = g_LightmapUpdateList.Element(i);
// a surface can get queued more than once if it's visible in multiple views (e.g. water reflection can do this)
// so check frame to make sure we only recompute once
if ( SurfaceLighting(lightmapUpdateInfo.m_SurfHandle)->m_nLastComputedFrame != r_framecount )
{
R_RenderDynamicLightmaps( pLights, pCallQueue, lightmapUpdateInfo.m_SurfHandle, g_LightmapTransformList[lightmapUpdateInfo.transformIndex].xform );
}
}
}
materials->EndUpdateLightmaps();
g_LightmapUpdateList.RemoveAll();
g_LightmapTransformList.RemoveAll();
}
}
bool CRender::InLightmapUpdate( void ) const
{
return ( m_iLightmapUpdateDepth != 0 );
}
//-----------------------------------------------------------------------------
// Compute the scene coordinates of a point in 3D
//-----------------------------------------------------------------------------
bool CRender::ClipTransformWithProjection ( const VMatrix& worldToScreen, const Vector& point, Vector* pClip )
{
// UNDONE: Clean this up some, handle off-screen vertices
float w;
pClip->x = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3];
pClip->y = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3];
// z = worldToScreen[2][0] * point[0] + worldToScreen[2][1] * point[1] + worldToScreen[2][2] * point[2] + worldToScreen[2][3];
w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3];
// Just so we have something valid here
pClip->z = 0.0f;
bool behind;
if( w < 0.001f )
{
behind = true;
pClip->x *= 100000;
pClip->y *= 100000;
}
else
{
behind = false;
float invw = 1.0f / w;
pClip->x *= invw;
pClip->y *= invw;
}
return behind;
}
//-----------------------------------------------------------------------------
// Compute the scene coordinates of a point in 3D using the current engine's projection
//-----------------------------------------------------------------------------
bool CRender::ClipTransform ( const Vector& point, Vector* pClip )
{
const VMatrix &worldToScreen = g_EngineRenderer->WorldToScreenMatrix();
return CRender::ClipTransformWithProjection ( worldToScreen, point, pClip );
}
//-----------------------------------------------------------------------------
// Purpose: Given a point, return the screen position in pixels
//-----------------------------------------------------------------------------
bool CRender::ScreenTransform( const Vector& point, Vector* pScreen )
{
bool retval = ClipTransform( point, pScreen );
pScreen->x = 0.5f * ( pScreen->x + 1.0f ) * CurrentView().width + CurrentView().x;
pScreen->y = 0.5f * ( pScreen->y + 1.0f ) * CurrentView().height + CurrentView().y;
return retval;
}
void CRender::ViewDrawFade( byte *color, IMaterial* pFadeMaterial )
{
if ( !color || !color[3] )
return;
if( !pFadeMaterial )
return;
const CViewSetup &view = CurrentView();
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( pFadeMaterial );
pFadeMaterial->AlphaModulate( color[3] * ( 1.0f / 255.0f ) );
pFadeMaterial->ColorModulate( color[0] * ( 1.0f / 255.0f ),
color[1] * ( 1.0f / 255.0f ),
color[2] * ( 1.0f / 255.0f ) );
bool bOldIgnoreZ = pFadeMaterial->GetMaterialVarFlag( MATERIAL_VAR_IGNOREZ );
pFadeMaterial->SetMaterialVarFlag( MATERIAL_VAR_IGNOREZ, true );
int nTexWidth, nTexHeight;
nTexWidth = pFadeMaterial->GetMappingWidth();
nTexHeight = pFadeMaterial->GetMappingHeight();
float flUOffset = 0.5f / nTexWidth;
float flVOffset = 0.5f / nTexHeight;
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Scale( 1, -1, 1 );
pRenderContext->Ortho( 0, 0, view.width, view.height, -99999, 99999 );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
IMesh* pMesh = pRenderContext->GetDynamicMesh();
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
float flOffset = 0.5f;
// Note - the viewport has already adjusted the origin
float x1=0.0f - flOffset;
float x2=view.width - flOffset;
float y1=0.0f - flOffset;
float y2=view.height - flOffset;
// adjust nominal uvs to reflect adjusted xys
float u1=FLerp(flUOffset, 1-flUOffset,view.x,view.x+view.width,x1);
float u2=FLerp(flUOffset, 1-flUOffset,view.x,view.x+view.width,x2);
float v1=FLerp(flVOffset, 1-flVOffset,view.y,view.y+view.height,y1);
float v2=FLerp(flVOffset, 1-flVOffset,view.y,view.y+view.height,y2);
for ( int corner=0; corner<4; corner++ )
{
bool left=(corner==0) || (corner==3);
meshBuilder.Position3f( (left) ? x1 : x2, (corner & 2) ? y2 : y1, 0.0f );
meshBuilder.TexCoord2f( 0, (left) ? u1 : u2, (corner & 2) ? v2 : v1 );
meshBuilder.AdvanceVertex();
}
meshBuilder.End();
pMesh->Draw();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
pFadeMaterial->SetMaterialVarFlag( MATERIAL_VAR_IGNOREZ, bOldIgnoreZ );
}
void CRender::ExtractFrustumPlanes( Frustum frustumPlanes )
{
const CViewSetup &view = CurrentView();
GeneratePerspectiveFrustum( CurrentViewOrigin(),
CurrentViewForward(), CurrentViewRight(), CurrentViewUp(),
view.zNear, view.zFar, view.fov, m_yFOV, g_Frustum );
// Copy out to the planes that the engine renderer uses.
for( int i=0; i < FRUSTUM_NUMPLANES; i++ )
{
frustumPlanes[i].m_Normal = g_Frustum.GetPlane(i)->normal;
frustumPlanes[i].m_Dist = g_Frustum.GetPlane(i)->dist;
}
}
void CRender::OrthoExtractFrustumPlanes( Frustum frustumPlanes )
{
const CViewSetup &view = CurrentView();
// Setup the near and far planes.
float orgOffset = DotProduct(CurrentViewOrigin(), CurrentViewForward());
frustumPlanes[FRUSTUM_FARZ].m_Normal = -CurrentViewForward();
frustumPlanes[FRUSTUM_FARZ].m_Dist = -view.zFar - orgOffset;
frustumPlanes[FRUSTUM_NEARZ].m_Normal = CurrentViewForward();
frustumPlanes[FRUSTUM_NEARZ].m_Dist = view.zNear + orgOffset;
// Left and right planes...
orgOffset = DotProduct(CurrentViewOrigin(), CurrentViewRight());
frustumPlanes[FRUSTUM_LEFT].m_Normal = CurrentViewRight();
frustumPlanes[FRUSTUM_LEFT].m_Dist = view.m_OrthoLeft + orgOffset;
frustumPlanes[FRUSTUM_RIGHT].m_Normal = -CurrentViewRight();
frustumPlanes[FRUSTUM_RIGHT].m_Dist = -view.m_OrthoRight - orgOffset;
// Top and buttom planes...
orgOffset = DotProduct(CurrentViewOrigin(), CurrentViewUp());
frustumPlanes[FRUSTUM_TOP].m_Normal = CurrentViewUp();
frustumPlanes[FRUSTUM_TOP].m_Dist = view.m_OrthoTop + orgOffset;
frustumPlanes[FRUSTUM_BOTTOM].m_Normal = -CurrentViewUp();
frustumPlanes[FRUSTUM_BOTTOM].m_Dist = -view.m_OrthoBottom - orgOffset;
// Copy out to the planes that the engine renderer uses.
for(int i=0; i < FRUSTUM_NUMPLANES; i++)
{
/*
if (fabs(frustumPlanes[i].m_Normal.x) - 1.0f > -1e-3)
frustum[i].type = PLANE_X;
else if (fabs(frustumPlanes[i].m_Normal.y) - 1.0f > -1e-3)
frustum[i].type = PLANE_Y;
else if (fabs(frustumPlanes[i].m_Normal.z) - 1.0f > -1e-3)
frustum[i].type = PLANE_Z;
else
*/
g_Frustum.SetPlane( i, PLANE_ANYZ, frustumPlanes[i].m_Normal, frustumPlanes[i].m_Dist );
}
}
void CRender::OverrideViewFrustum( Frustum custom )
{
// Copy out to the planes that the engine renderer uses.
for( int i = 0; i != FRUSTUM_NUMPLANES; ++i )
{
g_Frustum.SetPlane( i, PLANE_ANYZ, custom[i].m_Normal, custom[i].m_Dist );
}
}
void CRender::ExtractMatrices( void )
{
m_matrixView = m_ViewStack.Top().m_matrixView;
m_matrixProjection = m_ViewStack.Top().m_matrixProjection;
m_matrixWorldToScreen = m_ViewStack.Top().m_matrixWorldToScreen;
}
void ComputeViewMatrix( VMatrix *pViewMatrix, const Vector &origin, const QAngle &angles )
{
static VMatrix baseRotation;
static bool bDidInit;
if ( !bDidInit )
{
MatrixBuildRotationAboutAxis( baseRotation, Vector( 1, 0, 0 ), -90 );
MatrixRotate( baseRotation, Vector( 0, 0, 1 ), 90 );
bDidInit = true;
}
*pViewMatrix = baseRotation;
MatrixRotate( *pViewMatrix, Vector( 1, 0, 0 ), -angles[2] );
MatrixRotate( *pViewMatrix, Vector( 0, 1, 0 ), -angles[0] );
MatrixRotate( *pViewMatrix, Vector( 0, 0, 1 ), -angles[1] );
MatrixTranslate( *pViewMatrix, -origin );
}
void CRender::SetViewport( int x, int y, int w, int h )
{
int x2, y2;
int windowWidth = w, windowHeight = h;
CMatRenderContextPtr pRenderContext( materials );
// set the viewport to be out to the size of the render target, unless explicitly told not to
if (!CurrentView().m_bRenderToSubrectOfLargerScreen)
{
pRenderContext->GetRenderTargetDimensions( windowWidth, windowHeight );
}
x2 = (x + w);
y2 = (windowHeight - (y + h));
y = (windowHeight - y);
// fudge around because of frac screen scale
if (x > 0)
x--;
if (x2 < windowWidth)
x2++;
if (y2 < 0)
y2--;
if (y < windowHeight)
y++;
w = x2 - x;
h = y - y2;
pRenderContext->Viewport( x, y2, w, h );
}
void DrawLightmapPage( int lightmapPageID )
{
// assumes that we are already in ortho mode.
int lightmapPageWidth, lightmapPageHeight;
CMatRenderContextPtr pRenderContext( materials );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, g_materialDebugLightmap );
// pRenderContext->Bind( g_materialWireframe );
// IMesh* pMesh = pRenderContext->GetDynamicMesh( g_materialWireframe );
materials->GetLightmapPageSize( lightmapPageID, &lightmapPageWidth, &lightmapPageHeight );
pRenderContext->BindLightmapPage( lightmapPageID );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
#ifndef _XBOX
int x = 0;
int y = 0;
#else
// xboxissue - border safe
int x = 32;
int y = 32;
#endif
float s = 1.0f;
float t = 1.0f;
// texcoord 1 is lightmaptexcoord for fixed function.
meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
meshBuilder.Position3f( x, y, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, s, 0.0f );
meshBuilder.Position3f( x+lightmapPageWidth, y, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, s, t );
meshBuilder.Position3f( x+lightmapPageWidth, y+lightmapPageHeight, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, 0.0f, t );
meshBuilder.Position3f( x, y+lightmapPageHeight, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
//hack
extern void DebugDrawLightmapAtCrossHair();
void R_DrawLightmaps( IWorldRenderList *pList, int pageId )
{
#ifdef USE_CONVARS
if ( pageId != -1 )
{
DrawLightmapPage( pageId );
Shader_DrawLightmapPageChains( pList, pageId );
}
#endif
}
void R_CheckForLightingConfigChanges()
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
UpdateStudioRenderConfig();
UpdateMaterialSystemConfig();
if( MaterialConfigLightingChanged() || g_RebuildLightmaps )
{
ClearMaterialConfigLightingChanged();
ConMsg( "Redownloading all lightmaps\n" );
BuildGammaTable( 2.2f, 2.2f, 0.0f, OVERBRIGHT );
R_RedownloadAllLightmaps();
StaticPropMgr()->RecomputeStaticLighting();
}
}
void CRender::DrawSceneBegin( void )
{
R_CheckForLightingConfigChanges();
}
void CRender::DrawSceneEnd( void )
{
R_SceneEnd();
LeafVisDraw();
}
IWorldRenderList * CRender::CreateWorldList()
{
return AllocWorldRenderList();
}
// JasonM TODO: optimize in the case of shadow depth mapping (i.e. don't update lightmaps)
void CRender::BuildWorldLists( IWorldRenderList *pList, WorldListInfo_t* pInfo, int iForceViewLeaf, const VisOverrideData_t* pVisData, bool bShadowDepth, float *pWaterReflectionHeight )
{
Assert( pList );
Assert( m_iLightmapUpdateDepth > 0 || g_LightmapUpdateList.Count() == 0 );
if ( !bShadowDepth )
{
BeginUpdateLightmaps();
}
R_BuildWorldLists( pList, pInfo, iForceViewLeaf, pVisData, bShadowDepth, pWaterReflectionHeight );
if ( !bShadowDepth )
{
EndUpdateLightmaps();
}
Assert( m_iLightmapUpdateDepth > 0 || g_LightmapUpdateList.Count() == 0 );
}
void CRender::DrawWorldLists( IWorldRenderList *pList, unsigned long flags, float flWaterZAdjust )
{
Assert( pList );
R_DrawWorldLists( pList, flags, flWaterZAdjust );
}