diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/gl_rmain.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'engine/gl_rmain.cpp')
| -rw-r--r-- | engine/gl_rmain.cpp | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/engine/gl_rmain.cpp b/engine/gl_rmain.cpp new file mode 100644 index 0000000..63707e4 --- /dev/null +++ b/engine/gl_rmain.cpp @@ -0,0 +1,1254 @@ +//========= 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 ); +} |