You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1254 lines
38 KiB
1254 lines
38 KiB
//========= 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 ); |
|
}
|
|
|