diff options
Diffstat (limited to 'materialsystem/cmatrendercontext.cpp')
| -rw-r--r-- | materialsystem/cmatrendercontext.cpp | 3157 |
1 files changed, 3157 insertions, 0 deletions
diff --git a/materialsystem/cmatrendercontext.cpp b/materialsystem/cmatrendercontext.cpp new file mode 100644 index 0000000..c8f217f --- /dev/null +++ b/materialsystem/cmatrendercontext.cpp @@ -0,0 +1,3157 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pch_materialsystem.h" + +#define MATSYS_INTERNAL + +#include <math.h> +#include "cmatrendercontext.h" +#include "tier2/renderutils.h" +#include "cmaterialsystem.h" +#include "occlusionquerymgr.h" +#include "texturemanager.h" +#include "IHardwareConfigInternal.h" +#include "ctype.h" + +#include "tier1/fmtstr.h" +#include "togl/rendermechanism.h" + +// NOTE: This must be the last file included!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- + +// FIXME: right now, always keeping shader API in sync, because debug overlays don't seem to work 100% with the delayed matrix loading +#define FORCE_MATRIX_SYNC 1 + +#ifdef VALIDATE_MATRICES +#define ShouldValidateMatrices() true +#else +#define ShouldValidateMatrices() false +#endif + +#ifdef VALIDATE_MATRICES +#define AllowLazyMatrixSync() false +#define ForceSync() ((void)(0)) +#elif defined(FORCE_MATRIX_SYNC) +#define AllowLazyMatrixSync() false +#define ForceSync() ForceSyncMatrix( m_MatrixMode ) +#else +#define AllowLazyMatrixSync() true +#define ForceSync() ((void)(0)) +#endif + +#ifdef _X360 +static bool s_bDirtyDisk = false; +#endif + + +void ValidateMatrices( const VMatrix &m1, const VMatrix &m2, float eps = .001 ) +{ + if ( !ShouldValidateMatrices() ) + return; + + for ( int i = 0; i < 16; i++ ) + { + AssertFloatEquals( m1.Base()[i], m1.Base()[i], eps ); + } +} + + +//----------------------------------------------------------------------------- +// The dirty disk error report function (NOTE: Could be called from any thread!) +//----------------------------------------------------------------------------- +#ifdef _X360 +unsigned ThreadedDirtyDiskErrorDisplay( void *pParam ) +{ + XShowDirtyDiscErrorUI( XBX_GetPrimaryUserId() ); +} +#endif + + +void SpinPresent() +{ + while ( true ) + { + g_pShaderAPI->ClearColor3ub( 0, 0, 0 ); + g_pShaderAPI->ClearBuffers( true, true, true, -1, -1 ); + g_pShaderDevice->Present(); + } +} + +void ReportDirtyDisk() +{ +#ifdef _X360 + s_bDirtyDisk = true; + ThreadHandle_t h = CreateSimpleThread( ThreadedDirtyDiskErrorDisplay, NULL ); + ThreadSetPriority( h, THREAD_PRIORITY_HIGHEST ); + + // If this is being called from the render thread, immediately swap + if ( ( ThreadGetCurrentId() == MaterialSystem()->GetRenderThreadId() ) || + ( ThreadInMainThread() && g_pMaterialSystem->GetThreadMode() != MATERIAL_QUEUED_THREADED ) ) + { + SpinPresent(); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Install dirty disk error reporting function (call after SetMode) +//----------------------------------------------------------------------------- +void SetupDirtyDiskReportFunc() +{ + g_pFullFileSystem->InstallDirtyDiskReportFunc( ReportDirtyDisk ); +} + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +CMemoryStack CMatRenderContextBase::sm_RenderData[2]; +int CMatRenderContextBase::sm_nRenderLockCount = 0; +int CMatRenderContextBase::sm_nRenderStack = 0; +int CMatRenderContextBase::sm_nInitializeCount = 0; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CMatRenderContextBase::CMatRenderContextBase() : + m_pMaterialSystem( NULL ), m_RenderTargetStack( 16, 32 ), m_MatrixMode( NUM_MATRIX_MODES ) +{ + int i; + + m_bDirtyViewState = true; + + // Put a special element at the top of the RT stack (indicating back buffer is current top of stack) + // NULL indicates back buffer, -1 indicates full-size viewport +#if !defined( _X360 ) + RenderTargetStackElement_t initialElement = { {NULL, NULL, NULL, NULL}, NULL, 0, 0, -1, -1 }; +#else + RenderTargetStackElement_t initialElement = { {NULL}, NULL, 0, 0, -1, -1 }; +#endif + + + m_RenderTargetStack.Push( initialElement ); + + for ( i = 0; i < MAX_FB_TEXTURES; i++ ) + { + m_pCurrentFrameBufferCopyTexture[i] = NULL; + } + + m_pCurrentMaterial = NULL; + m_pCurrentProxyData = NULL; + m_pUserDefinedLightmap = NULL; + m_HeightClipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE; + m_HeightClipZ = 0.0f; + m_bEnableClipping = true; + m_bFlashlightEnable = false; + m_bFullFrameDepthIsValid = false; + + for ( i = 0; i < NUM_MATRIX_MODES; i++ ) + { + m_MatrixStacks[i].Push(); + m_MatrixStacks[i].Top().matrix.Identity(); + m_MatrixStacks[i].Top().flags |= ( MSF_DIRTY| MSF_IDENTITY ); + } + m_pCurMatrixItem = &m_MatrixStacks[0].Top(); + + m_Viewport.Init( 0, 0, 0, 0 ); + + m_LastSetToneMapScale=Vector(1,1,1); + m_CurToneMapScale=1.0; + m_GoalToneMapScale = 1.0f; +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +InitReturnVal_t CMatRenderContextBase::Init( ) +{ + MEM_ALLOC_CREDIT(); + if ( !sm_nInitializeCount ) + { + int nSize = 2200 * 1024; + int nCommitSize = 32 * 1024; + +#ifdef SWDS + nSize = nCommitSize = 1024; +#endif + + const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) ); + if ( gamedir && !Q_stricmp( "garrysmod", gamedir ) ) + { + nSize = 4400 * 1024; + } + + sm_RenderData[0].Init( nSize, nCommitSize, 0, 32 ); + sm_RenderData[1].Init( nSize, nCommitSize, 0, 32 ); + sm_nRenderStack = 0; + sm_nRenderLockCount = 0; + } + ++sm_nInitializeCount; + return INIT_OK; +} + +void CMatRenderContextBase::Shutdown( ) +{ + Assert( sm_nInitializeCount >= 0 ); + if ( --sm_nInitializeCount == 0 ) + { + sm_RenderData[0].Term(); + sm_RenderData[1].Term(); + } +} + +void CMatRenderContextBase::CompactMemory() +{ + if ( sm_nRenderLockCount ) + { + DevWarning( "CMatRenderContext: Trying to compact with render data still locked!\n" ); + sm_nRenderLockCount = 0; + } + sm_RenderData[0].FreeAll(); + sm_RenderData[1].FreeAll(); +} + +void CMatRenderContextBase::MarkRenderDataUnused( bool bFrameBegin ) +{ + if ( sm_nRenderLockCount ) + { + DevWarning( "CMatRenderContext: Trying to clear render data with render data still locked (%d)!\n", sm_nRenderLockCount ); + sm_nRenderLockCount = 0; + } + + + // JAY: DO NOT MERGE FROM TF2 - L4D HAS CHANGED THE UNDERLYING INTERFACE IN A WAY THAT DOESN'T REQUIRE THIS +#if 0 + // Switch stacks + if ( bFrameBegin ) + { + sm_nRenderStack = 1 - sm_nRenderStack; + } + + // Clear the new stack +#ifdef _DEBUG + memset( sm_RenderData[sm_nRenderStack].GetBase(), 0xFF, RenderDataSizeUsed() ); +#endif + sm_RenderData[ sm_nRenderStack ].FreeAll( false ); +#else + // Just for TF2, don't free the stack until the end of frame. TF2 Allocates render data and holds it over the lock + // period because we haven't revised the studiorender interface yet to change patterns. + // Switch stacks + if ( bFrameBegin ) + { + sm_nRenderStack = 1 - sm_nRenderStack; + // Clear the new stack +#ifdef _DEBUG + memset( sm_RenderData[sm_nRenderStack].GetBase(), 0xFF, RenderDataSizeUsed() ); +#endif + sm_RenderData[ sm_nRenderStack ].FreeAll( false ); + } +#endif + + +} + +int CMatRenderContextBase::RenderDataSizeUsed() const +{ + return sm_RenderData[sm_nRenderStack].GetUsed(); +} + +bool CMatRenderContextBase::IsRenderData( const void *pData ) const +{ + intp nData = (intp)pData; + intp nBaseAddress = (intp)sm_RenderData[sm_nRenderStack].GetBase(); + intp nLastAddress = nBaseAddress + RenderDataSizeUsed(); + return ( nData == 0 ) || ( nData >= nBaseAddress && nData < nLastAddress ); +} + + +//----------------------------------------------------------------------------- +// debug logging - empty in base class +//----------------------------------------------------------------------------- + +void CMatRenderContextBase::PrintfVA( char *fmt, va_list vargs ) +{ +} + +void CMatRenderContextBase::Printf( const char *fmt, ... ) +{ +} + +float CMatRenderContextBase::Knob( char *knobname, float *setvalue ) +{ + return 0.0f; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define g_pShaderAPI Cannot_use_ShaderAPI_in_CMatRenderContextBase + +void CMatRenderContextBase::InitializeFrom( CMatRenderContextBase *pInitialState ) +{ + int i; + + m_pCurrentMaterial = pInitialState->m_pCurrentMaterial; + m_pCurrentProxyData = pInitialState->m_pCurrentProxyData; + m_lightmapPageID = pInitialState->m_lightmapPageID; + m_pUserDefinedLightmap = pInitialState->m_pUserDefinedLightmap; + m_pLocalCubemapTexture = pInitialState->m_pLocalCubemapTexture; + + memcpy( m_pCurrentFrameBufferCopyTexture, pInitialState->m_pCurrentFrameBufferCopyTexture, MAX_FB_TEXTURES * sizeof(ITexture *) ); + + m_bEnableClipping = pInitialState->m_bEnableClipping; + + m_HeightClipMode = pInitialState->m_HeightClipMode; + m_HeightClipZ = pInitialState->m_HeightClipZ; + + m_pBoundMorph = pInitialState->m_pBoundMorph; // not reference counted? + + m_RenderTargetStack.Clear(); + m_RenderTargetStack.EnsureCapacity( pInitialState->m_RenderTargetStack.Count() ); + + for ( i = 0; i < pInitialState->m_RenderTargetStack.Count(); i++ ) + { + m_RenderTargetStack.Push( pInitialState->m_RenderTargetStack[i] ); + } + + m_MatrixMode = pInitialState->m_MatrixMode; + for ( i = 0; i < NUM_MATRIX_MODES; i++ ) + { + m_MatrixStacks[i].CopyFrom( pInitialState->m_MatrixStacks[i] ); + } + + m_bFlashlightEnable = pInitialState->m_bFlashlightEnable; + + m_FrameTime = pInitialState->m_FrameTime; + m_GoalToneMapScale = pInitialState->m_GoalToneMapScale; + m_CurToneMapScale = pInitialState->m_CurToneMapScale; + m_LastSetToneMapScale = pInitialState->m_LastSetToneMapScale; +} + +void CMatRenderContextBase::Bind( IMaterial *iMaterial, void *proxyData ) +{ + IMaterialInternal *material = static_cast<IMaterialInternal *>( iMaterial ); + + if ( !material ) + { + Warning( "Programming error: CMatRenderContext::Bind: NULL material\n" ); + material = static_cast<IMaterialInternal *>( g_pErrorMaterial ); + } + material = material->GetRealTimeVersion(); //always work with the real time versions of materials internally + + if ( GetCurrentMaterialInternal() != material ) + { + if( !material->IsPrecached() ) + { + DevWarning( "Binding uncached material \"%s\", artificially incrementing refcount\n", material->GetName() ); + material->ArtificialAddRef(); + material->Precache(); + } + SetCurrentMaterialInternal(material); + } + + SetCurrentProxy( proxyData ); +} + +void CMatRenderContextBase::BindLightmapPage( int lightmapPageID ) +{ + m_lightmapPageID = lightmapPageID; + +} + +void CMatRenderContextBase::SetRenderTargetEx( int nRenderTargetID, ITexture *pNewTarget ) +{ + // Verify valid top of RT stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Reset the top of stack to the new target with old viewport + RenderTargetStackElement_t newTOS = m_RenderTargetStack.Top(); + newTOS.m_pRenderTargets[nRenderTargetID] = pNewTarget; + m_RenderTargetStack.Pop( ); + m_RenderTargetStack.Push( newTOS ); +} + +void CMatRenderContextBase::BindLocalCubemap( ITexture *pTexture ) +{ + if( pTexture ) + { + m_pLocalCubemapTexture = pTexture; + } + else + { + m_pLocalCubemapTexture = TextureManager()->ErrorTexture(); + } +} + +ITexture *CMatRenderContextBase::GetRenderTarget( void ) +{ + if (m_RenderTargetStack.Count() > 0) + { + return m_RenderTargetStack.Top().m_pRenderTargets[0]; + } + else + { + return NULL; // should this be something else, since NULL indicates back buffer? + } +} + +ITexture *CMatRenderContextBase::GetRenderTargetEx( int nRenderTargetID ) +{ + // Verify valid top of stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Top of render target stack + ITexture *pTexture = m_RenderTargetStack.Top().m_pRenderTargets[ nRenderTargetID ]; + return pTexture; +} + +void CMatRenderContextBase::SetFrameBufferCopyTexture( ITexture *pTexture, int textureIndex ) +{ + if( textureIndex < 0 || textureIndex > MAX_FB_TEXTURES ) + { + Assert( 0 ); + return; + } + + // FIXME: Do I need to increment/decrement ref counts here, or assume that the app is going to do it? + m_pCurrentFrameBufferCopyTexture[textureIndex] = pTexture; +} + +ITexture *CMatRenderContextBase::GetFrameBufferCopyTexture( int textureIndex ) +{ + if( textureIndex < 0 || textureIndex > MAX_FB_TEXTURES ) + { + Assert( 0 ); + return NULL; // FIXME! This should return the error texture. + } + return m_pCurrentFrameBufferCopyTexture[textureIndex]; +} + + +void CMatRenderContextBase::MatrixMode( MaterialMatrixMode_t mode ) +{ + Assert( m_MatrixStacks[mode].Count() ); + m_MatrixMode = mode; + m_pCurMatrixItem = &m_MatrixStacks[mode].Top(); +} + +void CMatRenderContextBase::CurrentMatrixChanged() +{ + if ( m_MatrixMode == MATERIAL_VIEW ) + { + m_bDirtyViewState = true; + m_bDirtyViewProjState = true; + } + else if ( m_MatrixMode == MATERIAL_PROJECTION ) + { + m_bDirtyViewProjState = true; + } +} + +void CMatRenderContextBase::PushMatrix() +{ + CUtlStack<MatrixStackItem_t> &curStack = m_MatrixStacks[ m_MatrixMode ]; + Assert( curStack.Count() ); + int iNew = curStack.Push(); + curStack[ iNew ] = curStack[ iNew - 1 ]; + m_pCurMatrixItem = &curStack.Top(); + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::PopMatrix() +{ + Assert( m_MatrixStacks[m_MatrixMode].Count() > 1 ); + m_MatrixStacks[ m_MatrixMode ].Pop(); + m_pCurMatrixItem = &m_MatrixStacks[m_MatrixMode].Top(); + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::LoadMatrix( const VMatrix& matrix ) +{ + m_pCurMatrixItem->matrix = matrix; + m_pCurMatrixItem->flags = MSF_DIRTY; // clearing identity implicitly + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::LoadMatrix( const matrix3x4_t& matrix ) +{ + m_pCurMatrixItem->matrix = matrix; + m_pCurMatrixItem->flags = MSF_DIRTY; // clearing identity implicitly + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::MultMatrix( const VMatrix& matrix ) +{ + VMatrix result; + + MatrixMultiply( matrix, m_pCurMatrixItem->matrix, result ); + m_pCurMatrixItem->matrix = result; + m_pCurMatrixItem->flags = MSF_DIRTY; // clearing identity implicitly + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::MultMatrix( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrix( VMatrix( matrix ) ); +} + +void CMatRenderContextBase::MultMatrixLocal( const VMatrix& matrix ) +{ + VMatrix result; + MatrixMultiply( m_pCurMatrixItem->matrix, matrix, result ); + m_pCurMatrixItem->matrix = result; + m_pCurMatrixItem->flags = MSF_DIRTY; // clearing identity implicitly + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::MultMatrixLocal( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrixLocal( VMatrix( matrix ) ); +} + +void CMatRenderContextBase::LoadIdentity() +{ + // FIXME: possibly track is identity so can call shader API LoadIdentity() later instead of LoadMatrix()? + m_pCurMatrixItem->matrix.Identity(); + m_pCurMatrixItem->flags = ( MSF_DIRTY | MSF_IDENTITY ); + CurrentMatrixChanged(); +} + +void CMatRenderContextBase::Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) +{ + MatrixOrtho( m_pCurMatrixItem->matrix, left, top, right, bottom, zNear, zFar ); + m_pCurMatrixItem->flags = MSF_DIRTY; +} + +void CMatRenderContextBase::PerspectiveX( double flFovX, double flAspect, double flZNear, double flZFar ) +{ + MatrixPerspectiveX( m_pCurMatrixItem->matrix, flFovX, flAspect, flZNear, flZFar ); + m_pCurMatrixItem->flags = MSF_DIRTY; +} + +void CMatRenderContextBase::PerspectiveOffCenterX( double flFovX, double flAspect, double flZNear, double flZFar, double bottom, double top, double left, double right ) +{ + MatrixPerspectiveOffCenterX( m_pCurMatrixItem->matrix, flFovX, flAspect, flZNear, flZFar, bottom, top, left, right ); + m_pCurMatrixItem->flags = MSF_DIRTY; +} + +void CMatRenderContextBase::PickMatrix( int x, int y, int nWidth, int nHeight ) +{ + int vx, vy, vwidth, vheight; + GetViewport( vx, vy, vwidth, vheight ); + + // Compute the location of the pick region in projection space... + float px = 2.0 * (float)(x - vx) / (float)vwidth - 1; + float py = 2.0 * (float)(y - vy)/ (float)vheight - 1; + float pw = 2.0 * (float)nWidth / (float)vwidth; + float ph = 2.0 * (float)nHeight / (float)vheight; + + // we need to translate (px, py) to the origin + // and scale so (pw,ph) -> (2, 2) + VMatrix mat; + MatrixSetIdentity( mat ); + mat.m[0][0] = 2.0 / pw; + mat.m[1][1] = 2.0 / ph; + mat.m[0][3] = -2.0 * px / pw; + mat.m[1][3] = -2.0 * py / ph; + + CMatRenderContextBase::MultMatrixLocal( mat ); +} + +void CMatRenderContextBase::Rotate( float flAngle, float x, float y, float z ) +{ + MatrixRotate( m_pCurMatrixItem->matrix, Vector( x, y, z ), flAngle ); + m_pCurMatrixItem->flags = MSF_DIRTY; +} + +void CMatRenderContextBase::Translate( float x, float y, float z ) +{ + MatrixTranslate( m_pCurMatrixItem->matrix, Vector( x, y, z ) ); + m_pCurMatrixItem->flags = MSF_DIRTY; +} + +void CMatRenderContextBase::Scale( float x, float y, float z ) +{ + VMatrix mat; + MatrixBuildScale( mat, x, y, z ); + CMatRenderContextBase::MultMatrixLocal( mat ); +} + +void CMatRenderContextBase::GetMatrix( MaterialMatrixMode_t matrixMode, VMatrix *pMatrix ) +{ + CUtlStack<MatrixStackItem_t> &stack = m_MatrixStacks[ matrixMode ]; + + if ( !stack.Count() ) + { + pMatrix->Identity(); + return; + } + + *pMatrix = stack.Top().matrix; +} + +void CMatRenderContextBase::GetMatrix( MaterialMatrixMode_t matrixMode, matrix3x4_t *pMatrix ) +{ + CUtlStack<MatrixStackItem_t> &stack = m_MatrixStacks[ matrixMode ]; + + if ( !stack.Count() ) + { + SetIdentityMatrix( *pMatrix ); + return; + } + + *pMatrix = stack.Top().matrix.As3x4(); +} + +void CMatRenderContextBase::RecomputeViewState() +{ + if ( !m_bDirtyViewState ) + return; + m_bDirtyViewState = false; + + // FIXME: Cache this off to make it less expensive? + matrix3x4_t viewMatrix; + GetMatrix( MATERIAL_VIEW, &viewMatrix ); + m_vecViewOrigin.x = + -( viewMatrix[0][3] * viewMatrix[0][0] + + viewMatrix[1][3] * viewMatrix[1][0] + + viewMatrix[2][3] * viewMatrix[2][0] ); + m_vecViewOrigin.y = + -( viewMatrix[0][3] * viewMatrix[0][1] + + viewMatrix[1][3] * viewMatrix[1][1] + + viewMatrix[2][3] * viewMatrix[2][1] ); + m_vecViewOrigin.z = + -( viewMatrix[0][3] * viewMatrix[0][2] + + viewMatrix[1][3] * viewMatrix[1][2] + + viewMatrix[2][3] * viewMatrix[2][2] ); + + // FIXME Implement computation of m_vecViewForward, etc + m_vecViewForward.Init(); + m_vecViewRight.Init(); + + // FIXME: Is this correct? + m_vecViewUp.Init( viewMatrix[1][0], viewMatrix[1][1], viewMatrix[1][2] ); +} + +void CMatRenderContextBase::GetWorldSpaceCameraPosition( Vector *pCameraPos ) +{ + RecomputeViewState(); + VectorCopy( m_vecViewOrigin, *pCameraPos ); +} + +void CMatRenderContextBase::GetWorldSpaceCameraVectors( Vector *pVecForward, Vector *pVecRight, Vector *pVecUp ) +{ + RecomputeViewState(); + + // FIXME Implement computation of m_vecViewForward + Assert( 0 ); + + if ( pVecForward ) + { + VectorCopy( m_vecViewForward, *pVecForward ); + } + if ( pVecRight ) + { + VectorCopy( m_vecViewRight, *pVecRight ); + } + if ( pVecUp ) + { + VectorCopy( m_vecViewUp, *pVecUp ); + } +} + +void *CMatRenderContextBase::LockRenderData( int nSizeInBytes ) +{ + MEM_ALLOC_CREDIT(); + void *pDest = sm_RenderData[ sm_nRenderStack ].Alloc( nSizeInBytes, false ); + if ( !pDest ) + { + ExecuteNTimes( 10, Warning("MaterialSystem: Out of memory in render data!\n") ); + } + AddRefRenderData(); + return pDest; +} + +void CMatRenderContextBase::UnlockRenderData( void *pData ) +{ + ReleaseRenderData(); +} + +void CMatRenderContextBase::AddRefRenderData() +{ + ++sm_nRenderLockCount; +} + +void CMatRenderContextBase::ReleaseRenderData() +{ + --sm_nRenderLockCount; + Assert( sm_nRenderLockCount >= 0 ); + if ( sm_nRenderLockCount == 0 ) + { + OnRenderDataUnreferenced(); + } +} + +void CMatRenderContextBase::SyncMatrices() +{ +} + +void CMatRenderContextBase::SyncMatrix( MaterialMatrixMode_t mode ) +{ +} + +void CMatRenderContextBase::SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode ) +{ + if( m_HeightClipMode != heightClipMode ) + { + m_HeightClipMode = heightClipMode; + UpdateHeightClipUserClipPlane(); + /*if ( HardwareConfig()->MaxUserClipPlanes() >= 1 && !HardwareConfig()->UseFastClipping()) + { + UpdateHeightClipUserClipPlane(); + } + else + { + g_pShaderAPI->SetHeightClipMode( heightClipMode ); + }*/ + } +} + +void CMatRenderContextBase::SetHeightClipZ( float z ) +{ + if( z != m_HeightClipZ ) + { + m_HeightClipZ = z; + UpdateHeightClipUserClipPlane(); + } + + // FIXME! : Need to move user clip plane support back to pre-dx9 cards (all of the pixel shaders + // have texkill in them. . blich.) + + /*if ( HardwareConfig()->MaxUserClipPlanes() >= 1 && !HardwareConfig()->UseFastClipping() ) + { + UpdateHeightClipUserClipPlane(); + } + else + { + g_pShaderAPI->SetHeightClipZ( z ); + }*/ +} + +bool CMatRenderContextBase::EnableClipping( bool bEnable ) +{ + if( bEnable != m_bEnableClipping ) + { + m_bEnableClipping = bEnable; + ApplyCustomClipPlanes(); + + return !bEnable; + } + return bEnable; +} + +void CMatRenderContextBase::Viewport( int x, int y, int width, int height ) +{ + // Verify valid top of RT stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Reset the top of stack to the new viewport + RenderTargetStackElement_t newTOS; + memcpy(&newTOS,&(m_RenderTargetStack.Top()),sizeof(newTOS)); + newTOS.m_nViewX = x; + newTOS.m_nViewY = y; + newTOS.m_nViewW = width; + newTOS.m_nViewH = height; + + m_RenderTargetStack.Pop( ); + m_RenderTargetStack.Push( newTOS ); +} + + +//----------------------------------------------------------------------------- +// This version will push the current rendertarget + current viewport onto the stack +//----------------------------------------------------------------------------- +void CMatRenderContextBase::PushRenderTargetAndViewport( ) +{ + // Necessary to push the stack top onto itself; realloc could happen otherwise + m_RenderTargetStack.EnsureCapacity( m_RenderTargetStack.Count() + 1 ); + m_RenderTargetStack.Push( m_RenderTargetStack.Top() ); + CommitRenderTargetAndViewport(); +} + + +//----------------------------------------------------------------------------- +// Pushes a render target on the render target stack. Without a specific +// viewport also being pushed, this function uses dummy values which indicate +// that the viewport should span the the full render target and pushes +// the RenderTargetStackElement_t onto the stack +// +// The push and pop methods also implicitly set the render target to the new top of stack +// +// NULL for pTexture indicates rendering to the back buffer +//----------------------------------------------------------------------------- +void CMatRenderContextBase::PushRenderTargetAndViewport( ITexture *pTexture ) +{ + // Just blindly push the data on the stack with flags indicating full bounds +#if !defined( _X360 ) + RenderTargetStackElement_t element = { {pTexture, NULL, NULL, NULL}, 0, 0, -1, -1 }; +#else + RenderTargetStackElement_t element = { {pTexture}, 0, 0, -1, -1 }; +#endif + m_RenderTargetStack.Push( element ); + CommitRenderTargetAndViewport(); +} + + +//----------------------------------------------------------------------------- +// Pushes a render target on the render target stack and sets the viewport +// +// NULL for pTexture indicates rendering to the back buffer +// +// The push and pop methods also implicitly set the render target to the new top of stack +//----------------------------------------------------------------------------- +void CMatRenderContextBase::PushRenderTargetAndViewport( ITexture *pTexture, int nViewX, int nViewY, int nViewW, int nViewH ) +{ + CMatRenderContextBase::PushRenderTargetAndViewport( pTexture, NULL, nViewX, nViewY, nViewW, nViewH ); +} + + +//----------------------------------------------------------------------------- +// Pushes a render target on the render target stack and sets the viewport +// The push and pop methods also implicitly set the render target to the new top of stack +//----------------------------------------------------------------------------- +void CMatRenderContextBase::PushRenderTargetAndViewport( ITexture *pTexture, ITexture *pDepthTexture, int nViewX, int nViewY, int nViewW, int nViewH ) +{ + // Just blindly push the data on the stack +#if !defined( _X360 ) + RenderTargetStackElement_t element = { {pTexture, NULL, NULL, NULL}, pDepthTexture, nViewX, nViewY, nViewW, nViewH }; +#else + RenderTargetStackElement_t element = { {pTexture}, pDepthTexture, nViewX, nViewY, nViewW, nViewH }; +#endif + m_RenderTargetStack.Push( element ); + CommitRenderTargetAndViewport(); +} + + +//----------------------------------------------------------------------------- +// Pops from the render target stack +// Also implicitly sets the render target to the new top of stack +//----------------------------------------------------------------------------- +void CMatRenderContextBase::PopRenderTargetAndViewport( void ) +{ + // Check for underflow + if ( m_RenderTargetStack.Count() == 0 ) + { + Assert( !"CMatRenderContext::PopRenderTargetAndViewport: Stack is empty!!!" ); + return; + } + + // Changelist #266217 added this to main/src/materialsystem. + Flush(); + + // Remove the top of stack + m_RenderTargetStack.Pop( ); + CommitRenderTargetAndViewport(); +} + +void CMatRenderContextBase::RecomputeViewProjState() +{ + if ( m_bDirtyViewProjState ) + { + VMatrix viewMatrix, projMatrix; + + // FIXME: Should consider caching this upon change for projection or view matrix. + GetMatrix( MATERIAL_VIEW, &viewMatrix ); + GetMatrix( MATERIAL_PROJECTION, &projMatrix ); + m_viewProjMatrix = projMatrix * viewMatrix; + m_bDirtyViewProjState = false; + } +} + +//----------------------------------------------------------------------------- +// This returns the diameter of the sphere in pixels based on +// the current model, view, + projection matrices and viewport. +//----------------------------------------------------------------------------- +float CMatRenderContextBase::ComputePixelDiameterOfSphere( const Vector& vecAbsOrigin, float flRadius ) +{ + RecomputeViewState(); + RecomputeViewProjState(); + // This is sort of faked, but it's faster that way + // FIXME: Also, there's a much faster way to do this with similar triangles + // but I want to make sure it exactly matches the current matrices, so + // for now, I do it this conservative way + Vector4D testPoint1, testPoint2; + VectorMA( vecAbsOrigin, flRadius, m_vecViewUp, testPoint1.AsVector3D() ); + VectorMA( vecAbsOrigin, -flRadius, m_vecViewUp, testPoint2.AsVector3D() ); + testPoint1.w = testPoint2.w = 1.0f; + + Vector4D clipPos1, clipPos2; + Vector4DMultiply( m_viewProjMatrix, testPoint1, clipPos1 ); + Vector4DMultiply( m_viewProjMatrix, testPoint2, clipPos2 ); + if (clipPos1.w >= 0.001f) + { + clipPos1.y /= clipPos1.w; + } + else + { + clipPos1.y *= 1000; + } + if (clipPos2.w >= 0.001f) + { + clipPos2.y /= clipPos2.w; + } + else + { + clipPos2.y *= 1000; + } + int vx, vy, vwidth, vheight; + GetViewport( vx, vy, vwidth, vheight ); + + // The divide-by-two here is because y goes from -1 to 1 in projection space + return vheight * fabs( clipPos2.y - clipPos1.y ) / 2.0f; +} + +ConVar mat_accelerate_adjust_exposure_down( "mat_accelerate_adjust_exposure_down", "3.0", FCVAR_CHEAT ); +ConVar mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate", "1.0" ); +ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT ); +ConVar mat_tonemap_algorithm( "mat_tonemap_algorithm", "1", FCVAR_CHEAT, "0 = Original Algorithm 1 = New Algorithm" ); + +void CMatRenderContextBase::TurnOnToneMapping(void) +{ + if ( ( HardwareConfig()->GetHDRType() != HDR_TYPE_NONE ) && ( m_FrameTime > 0.0f ) ) + { + float elapsed_time = m_FrameTime; + float goalScale = m_GoalToneMapScale; + float rate = mat_hdr_manual_tonemap_rate.GetFloat(); + + if ( mat_tonemap_algorithm.GetInt() == 1 ) + { + rate *= 2.0f; // Default 2x for the new tone mapping algorithm so it feels the same as the original + } + + if ( rate == 0.0f ) // Zero indicates instantaneous tonemap scaling + { + m_CurToneMapScale = goalScale; + } + else + { + if ( goalScale < m_CurToneMapScale ) + { + float acc_exposure_adjust = mat_accelerate_adjust_exposure_down.GetFloat(); + + // Adjust at up to 4x rate when over-exposed. + rate = min( ( acc_exposure_adjust * rate ), FLerp( rate, ( acc_exposure_adjust * rate ), 0.0f, 1.5f, ( m_CurToneMapScale - goalScale ) ) ); + } + + float flRateTimesTime = rate * elapsed_time; + if ( mat_tonemap_algorithm.GetInt() == 1 ) + { + // For the new tone mapping algorithm, limit the rate based on the number of bins to + // help reduce the tone map scalar "riding the wave" of the histogram re-building + + //Warning( "flRateTimesTime = %.4f", flRateTimesTime ); + flRateTimesTime = min( flRateTimesTime, ( 1.0f / 16.0f ) * 0.25f ); // 16 is number of HDR sample bins defined in viewpostprocess.cpp + //Warning( " --> %.4f\n", flRateTimesTime ); + } + + float alpha = max( 0.0f, min( 1.0f, flRateTimesTime ) ); + m_CurToneMapScale = ( goalScale * alpha ) + ( m_CurToneMapScale * ( 1.0f - alpha ) ); + + if ( !IsFinite( m_CurToneMapScale ) ) + { + Assert( 0 ); + m_CurToneMapScale = goalScale; + } + } + + SetToneMappingScaleLinear( Vector( m_CurToneMapScale, m_CurToneMapScale, m_CurToneMapScale ) ); + m_LastSetToneMapScale = Vector( m_CurToneMapScale, m_CurToneMapScale, m_CurToneMapScale ); + } +} + +void CMatRenderContextBase::ResetToneMappingScale(float sc) +{ + m_CurToneMapScale = sc; + SetToneMappingScaleLinear( Vector( m_CurToneMapScale, m_CurToneMapScale, m_CurToneMapScale ) ); + m_LastSetToneMapScale = Vector( m_CurToneMapScale, m_CurToneMapScale, m_CurToneMapScale ); + // mat_hdr_tonemapscale.SetValue(1.0f); + m_GoalToneMapScale = 1; +} + +void CMatRenderContextBase::SetGoalToneMappingScale( float monoscale) +{ + Assert( IsFinite( monoscale ) ); + if( IsFinite( monoscale ) ) + { + m_GoalToneMapScale = monoscale; + } +} + +Vector CMatRenderContextBase::GetToneMappingScaleLinear( void ) +{ + if ( HardwareConfig()->GetHDRType() == HDR_TYPE_NONE ) + return Vector( 1.0f, 1.0f, 1.0f ); + else + return m_LastSetToneMapScale; +} + +void CMatRenderContextBase::OnAsyncCreateTextureFromRenderTarget( ITexture* pSrcRt, const char** ppDstName, IAsyncTextureOperationReceiver* pRecipient ) +{ + Assert( pSrcRt != NULL ); + Assert( pRecipient != NULL ); + Assert( ppDstName != NULL && *ppDstName != NULL); + + // Bump the ref count on the recipient before handing it off. This ensures the receiver won't go away before we have completed our work. + pSrcRt->AddRef(); + pRecipient->AddRef(); + + // Also, need to allocate a copy of the string and use that one s.t. the caller doesn't have to worry about it. + char* pDstNameCopy = new char[ V_strlen( *ppDstName ) + 1 ]; + V_strcpy( pDstNameCopy, *ppDstName ); + ( *ppDstName ) = pDstNameCopy; +} + +// Map and unmap a texture. The pRecipient->OnAsyncMapComplete is called when complete. +void CMatRenderContextBase::OnAsyncMap( ITextureInternal* pTexToMap, IAsyncTextureOperationReceiver* pRecipient, void* pExtraArgs ) +{ + Assert( pTexToMap != NULL ); + Assert( pRecipient != NULL ); + + pTexToMap->AddRef(); + pRecipient->AddRef(); +} + +void CMatRenderContextBase::OnAsyncUnmap( ITextureInternal* pTexToUnmap ) +{ + Assert( pTexToUnmap != NULL ); + + pTexToUnmap->AddRef(); +} + +void CMatRenderContextBase::OnAsyncCopyRenderTargetToStagingTexture( ITexture* pDst, ITexture* pSrc, IAsyncTextureOperationReceiver* pRecipient ) +{ + Assert( pDst != NULL ); + Assert( pSrc != NULL ); + Assert( pRecipient != NULL ); + + pDst->AddRef(); + pSrc->AddRef(); + pRecipient->AddRef(); +} + +#undef g_pShaderAPI + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CMatRenderContext::CMatRenderContext() +{ + g_FrameNum = 0; + m_pBatchIndices = NULL; + m_pBatchMesh = NULL; + m_pCurrentIndexBuffer = NULL; + m_pMorphRenderContext = NULL; + m_NonInteractiveMode = MATERIAL_NON_INTERACTIVE_MODE_NONE; +} + +InitReturnVal_t CMatRenderContext::Init( CMaterialSystem *pMaterialSystem ) +{ + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + m_pMaterialSystem = pMaterialSystem; + + m_pBoundMorph = NULL; + + // Create some lovely textures + m_pLocalCubemapTexture = TextureManager()->ErrorTexture(); + m_pMorphRenderContext = g_pMorphMgr->AllocateRenderContext(); + + return INIT_OK; +} + +void CMatRenderContext::Shutdown( ) +{ + if ( m_pUserDefinedLightmap ) + { + m_pUserDefinedLightmap->DecrementReferenceCount(); + m_pUserDefinedLightmap = NULL; + } + + if ( m_pMorphRenderContext ) + { + g_pMorphMgr->FreeRenderContext( m_pMorphRenderContext ); + m_pMorphRenderContext = NULL; + } + + BaseClass::Shutdown(); +} + +void CMatRenderContext::OnReleaseShaderObjects() +{ + // alt-tab unbinds the morph + m_pBoundMorph = NULL; +} + +#ifdef DX_TO_GL_ABSTRACTION +void CMatRenderContext::DoStartupShaderPreloading( void ) +{ + g_pShaderDevice->DoStartupShaderPreloading(); +} +#endif + +void CMatRenderContext::TextureManagerUpdate() +{ + TextureManager()->Update(); +} + + +inline IMaterialInternal *CMatRenderContext::GetMaterialInternal( MaterialHandle_t h ) const +{ + return GetMaterialSystem()->GetMaterialInternal( h ); +} + +inline IMaterialInternal *CMatRenderContext::GetDrawFlatMaterial() +{ + return GetMaterialSystem()->GetDrawFlatMaterial(); +} + +inline IMaterialInternal *CMatRenderContext::GetBufferClearObeyStencil( int i ) +{ + return GetMaterialSystem()->GetBufferClearObeyStencil(i ); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetFullbrightLightmapTextureHandle() const +{ + return GetMaterialSystem()->GetFullbrightLightmapTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetFullbrightBumpedLightmapTextureHandle() const +{ + return GetMaterialSystem()->GetFullbrightBumpedLightmapTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetBlackTextureHandle() const +{ + return GetMaterialSystem()->GetBlackTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetFlatNormalTextureHandle() const +{ + return GetMaterialSystem()->GetFlatNormalTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetGreyTextureHandle() const +{ + return GetMaterialSystem()->GetGreyTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetGreyAlphaZeroTextureHandle() const +{ + return GetMaterialSystem()->GetGreyAlphaZeroTextureHandle(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetWhiteTextureHandle() const +{ + return GetMaterialSystem()->GetWhiteTextureHandle(); +} + +inline const CMatLightmaps *CMatRenderContext::GetLightmaps() const +{ + return GetMaterialSystem()->GetLightmaps(); +} + +inline CMatLightmaps *CMatRenderContext::GetLightmaps() +{ + return GetMaterialSystem()->GetLightmaps(); +} + +inline ShaderAPITextureHandle_t CMatRenderContext::GetMaxDepthTextureHandle() const +{ + return GetMaterialSystem()->GetMaxDepthTextureHandle(); +} + +void CMatRenderContext::BeginRender() +{ +#if 1 // Rick's optimization: not sure this is needed anymore + if ( GetMaterialSystem()->GetThreadMode() != MATERIAL_SINGLE_THREADED ) + { + VPROF_INCREMENT_GROUP_COUNTER( "render/CMatBeginRender", COUNTER_GROUP_TELEMETRY, 1 ); + + TelemetrySetLockName( TELEMETRY_LEVEL1, (char const *)&g_MatSysMutex, "MatSysMutex" ); + + tmTryLock( TELEMETRY_LEVEL1, (char const *)&g_MatSysMutex, "BeginRender" ); + g_MatSysMutex.Lock(); + tmEndTryLock( TELEMETRY_LEVEL1, (char const *)&g_MatSysMutex, TMLR_SUCCESS ); + tmSetLockState( TELEMETRY_LEVEL1, (char const *)&g_MatSysMutex, TMLS_LOCKED, "BeginRender" ); + } +#endif +} + +void CMatRenderContext::EndRender() +{ +#if 1 // Rick's optimization: not sure this is needed anymore + if ( GetMaterialSystem()->GetThreadMode() != MATERIAL_SINGLE_THREADED ) + { + g_MatSysMutex.Unlock(); + tmSetLockState( TELEMETRY_LEVEL1, (char const *)&g_MatSysMutex, TMLS_RELEASED, "EndRender" ); + } +#endif +} + +void CMatRenderContext::Flush( bool flushHardware ) +{ + VPROF( "CMatRenderContextBase::Flush" ); + + g_pShaderAPI->FlushBufferedPrimitives(); + if ( IsPC() && flushHardware ) + { + g_pShaderAPI->FlushBufferedPrimitives(); + } +} + +bool CMatRenderContext::TestMatrixSync( MaterialMatrixMode_t mode ) +{ + if ( !ShouldValidateMatrices() ) + { + return true; + } + + VMatrix transposeMatrix, matrix; + g_pShaderAPI->GetMatrix( mode, (float*)transposeMatrix.m ); + MatrixTranspose( transposeMatrix, matrix ); + + ValidateMatrices( matrix, m_MatrixStacks[mode].Top().matrix ); + + return true; +} + +void CMatRenderContext::MatrixMode( MaterialMatrixMode_t mode ) +{ + CMatRenderContextBase::MatrixMode( mode ); + g_pShaderAPI->MatrixMode( mode ); + if ( ShouldValidateMatrices() ) + { + TestMatrixSync( mode ); + } + +} +void CMatRenderContext::PushMatrix() +{ + if ( ShouldValidateMatrices() ) + { + TestMatrixSync( m_MatrixMode ); + } + + CMatRenderContextBase::PushMatrix(); + g_pShaderAPI->PushMatrix(); + + if ( ShouldValidateMatrices() ) + { + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::PopMatrix() +{ + if ( ShouldValidateMatrices() ) + { + TestMatrixSync( m_MatrixMode ); + } + + CMatRenderContextBase::PopMatrix(); + g_pShaderAPI->PopMatrix(); + + if ( ShouldValidateMatrices() ) + { + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::LoadMatrix( const VMatrix& matrix ) +{ + CMatRenderContextBase::LoadMatrix( matrix ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( matrix, transposeMatrix ); + g_pShaderAPI->LoadMatrix( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::LoadMatrix( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::LoadMatrix( matrix ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( VMatrix(matrix), transposeMatrix ); + g_pShaderAPI->LoadMatrix( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::MultMatrix( const VMatrix& matrix ) +{ + CMatRenderContextBase::MultMatrix( matrix ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( matrix, transposeMatrix ); + g_pShaderAPI->MultMatrix( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::MultMatrix( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrix( VMatrix( matrix ) ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( VMatrix(matrix), transposeMatrix ); + g_pShaderAPI->MultMatrix( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::MultMatrixLocal( const VMatrix& matrix ) +{ + CMatRenderContextBase::MultMatrixLocal( matrix ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( matrix, transposeMatrix ); + g_pShaderAPI->MultMatrixLocal( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::MultMatrixLocal( const matrix3x4_t& matrix ) +{ + CMatRenderContextBase::MultMatrixLocal( VMatrix( matrix ) ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix transposeMatrix; + MatrixTranspose( VMatrix(matrix), transposeMatrix ); + g_pShaderAPI->MultMatrixLocal( transposeMatrix.Base() ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::LoadIdentity() +{ + CMatRenderContextBase::LoadIdentity(); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->LoadIdentity(); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) +{ + CMatRenderContextBase::Ortho( left, top, right, bottom, zNear, zFar ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->Ortho( left, top, right, bottom, zNear, zFar ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::PerspectiveX( double flFovX, double flAspect, double flZNear, double flZFar ) +{ + CMatRenderContextBase::PerspectiveX( flFovX, flAspect, flZNear, flZFar ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->PerspectiveX( flFovX, flAspect, flZNear, flZFar ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::PerspectiveOffCenterX( double flFovX, double flAspect, double flZNear, double flZFar, double bottom, double top, double left, double right ) +{ + CMatRenderContextBase::PerspectiveOffCenterX( flFovX, flAspect, flZNear, flZFar, bottom, top, left, right ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->PerspectiveOffCenterX( flFovX, flAspect, flZNear, flZFar, bottom, top, left, right ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::PickMatrix( int x, int y, int nWidth, int nHeight ) +{ + CMatRenderContextBase::PickMatrix( x, y, nWidth, nHeight ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->PickMatrix( x, y, nWidth, nHeight ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::Rotate( float flAngle, float x, float y, float z ) +{ + CMatRenderContextBase::Rotate( flAngle, x, y, z ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->Rotate( flAngle, x, y, z ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::Translate( float x, float y, float z ) +{ + CMatRenderContextBase::Translate( x, y, z ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->Translate( x, y, z ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::Scale( float x, float y, float z ) +{ + CMatRenderContextBase::Scale( x, y, z ); + ForceSync(); + if ( ShouldValidateMatrices() ) + { + g_pShaderAPI->Scale( x, y, z ); + TestMatrixSync( m_MatrixMode ); + } +} + +void CMatRenderContext::GetMatrix( MaterialMatrixMode_t matrixMode, VMatrix *pMatrix ) +{ + CMatRenderContextBase::GetMatrix( matrixMode, pMatrix ); + + ForceSync(); + if ( ShouldValidateMatrices() ) + { + VMatrix testMatrix; + VMatrix transposeMatrix; + g_pShaderAPI->GetMatrix( matrixMode, (float*)transposeMatrix.m ); + MatrixTranspose( transposeMatrix, testMatrix ); + + ValidateMatrices( testMatrix, *pMatrix ); + } +} + +void CMatRenderContext::GetMatrix( MaterialMatrixMode_t matrixMode, matrix3x4_t *pMatrix ) +{ + if ( !ShouldValidateMatrices() ) + { + CMatRenderContextBase::GetMatrix( matrixMode, pMatrix ); + } + else + { + VMatrix matrix; + CMatRenderContext::GetMatrix( matrixMode, &matrix ); + *pMatrix = matrix.As3x4(); + } +} + +void CMatRenderContext::SyncMatrices() +{ + if ( !ShouldValidateMatrices() && AllowLazyMatrixSync() ) + { + for( int i = 0; i < NUM_MATRIX_MODES; i++ ) + { + MatrixStackItem_t &top = m_MatrixStacks[i].Top(); + if ( top.flags & MSF_DIRTY ) + { + g_pShaderAPI->MatrixMode( (MaterialMatrixMode_t)i ); + if ( !( top.flags & MSF_IDENTITY ) ) + { + VMatrix transposeTop; + MatrixTranspose( top.matrix, transposeTop ); + g_pShaderAPI->LoadMatrix( transposeTop.Base() ); + } + else + { + g_pShaderAPI->LoadIdentity(); + } + + top.flags &= ~MSF_DIRTY; + } + } + } +} + +void CMatRenderContext::ForceSyncMatrix( MaterialMatrixMode_t mode ) +{ + MatrixStackItem_t &top = m_MatrixStacks[mode].Top(); + if ( top.flags & MSF_DIRTY ) + { + bool bSetMode = ( m_MatrixMode != mode ); + if ( bSetMode ) + { + g_pShaderAPI->MatrixMode( (MaterialMatrixMode_t)mode ); + } + + if ( !( top.flags & MSF_IDENTITY ) ) + { + VMatrix transposeTop; + MatrixTranspose( top.matrix, transposeTop ); + g_pShaderAPI->LoadMatrix( transposeTop.Base() ); + } + else + { + g_pShaderAPI->LoadIdentity(); + } + + if ( bSetMode ) + { + g_pShaderAPI->MatrixMode( (MaterialMatrixMode_t)mode ); + } + + top.flags &= ~MSF_DIRTY; + } +} + +void CMatRenderContext::SyncMatrix( MaterialMatrixMode_t mode ) +{ + if ( !ShouldValidateMatrices() && AllowLazyMatrixSync() ) + { + ForceSyncMatrix( mode ); + } +} + + +//----------------------------------------------------------------------------- +// Swap buffers +//----------------------------------------------------------------------------- +void CMatRenderContext::SwapBuffers() +{ + g_pMorphMgr->AdvanceFrame(); + g_pOcclusionQueryMgr->AdvanceFrame(); + g_pShaderDevice->Present(); + +#ifdef _X360 + if ( s_bDirtyDisk ) + { + SpinPresent(); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Clears the render data after we're done with it +//----------------------------------------------------------------------------- +void CMatRenderContext::OnRenderDataUnreferenced() +{ + MarkRenderDataUnused( false ); +} + + +//----------------------------------------------------------------------------- +// Custom clip planes +//----------------------------------------------------------------------------- +void CMatRenderContext::PushCustomClipPlane( const float *pPlane ) +{ + PlaneStackElement psePlane; + memcpy( psePlane.fValues, pPlane, sizeof( float ) * 4 ); + psePlane.bHack_IsHeightClipPlane = false; + m_CustomClipPlanes.AddToTail( psePlane ); //we're doing this as a UtlVector so height clip planes never change their cached index + ApplyCustomClipPlanes(); +} + +void CMatRenderContext::PopCustomClipPlane( void ) +{ + Assert( m_CustomClipPlanes.Count() ); + + //remove the endmost non-height plane found + int i; + for( i = m_CustomClipPlanes.Count(); --i >= 0; ) + { + if( m_CustomClipPlanes[i].bHack_IsHeightClipPlane == false ) + { + m_CustomClipPlanes.Remove(i); + break; + } + } + Assert( i != -1 ); //only the height clip plane was found, which means this pop had no associated push + ApplyCustomClipPlanes(); +} + +void CMatRenderContext::ApplyCustomClipPlanes( void ) +{ + int iMaxClipPlanes = HardwareConfig()->MaxUserClipPlanes(); + int iCustomPlanes; + + if( m_bEnableClipping ) + iCustomPlanes = m_CustomClipPlanes.Count(); + else + iCustomPlanes = 0; + + float fFakePlane[4]; + unsigned int iFakePlaneVal = 0xFFFFFFFF; + fFakePlane[0] = fFakePlane[1] = fFakePlane[2] = fFakePlane[3] = *((float *)&iFakePlaneVal); + + SyncMatrices(); + + if( iMaxClipPlanes >= 1 && !HardwareConfig()->UseFastClipping() ) + { + //yay, we get to be the cool clipping club + if( iMaxClipPlanes >= iCustomPlanes ) + { + int i; + for( i = 0; i < iCustomPlanes; ++i ) + { + g_pShaderAPI->SetClipPlane( i, m_CustomClipPlanes[i].fValues ); + g_pShaderAPI->EnableClipPlane( i, true ); + } + for( ; i < iMaxClipPlanes; ++i ) //disable unused planes + { + g_pShaderAPI->EnableClipPlane( i, false ); + g_pShaderAPI->SetClipPlane( i, fFakePlane ); + } + } + else + { + int iCustomPlaneOffset = iCustomPlanes - iMaxClipPlanes; + + //can't enable them all + for( int i = iCustomPlaneOffset; i < iCustomPlanes; ++i ) + { + g_pShaderAPI->SetClipPlane( i % iMaxClipPlanes, m_CustomClipPlanes[i].fValues ); + g_pShaderAPI->EnableClipPlane( i % iMaxClipPlanes, true ); + } + } + } + else + { + //hmm, at most we can make 1 clip plane work + if( iCustomPlanes == 0 ) + { + //no planes at all + g_pShaderAPI->EnableFastClip( false ); + g_pShaderAPI->SetFastClipPlane( fFakePlane ); + } + else + { + //we have to wire the topmost plane into the fast clipping scheme + g_pShaderAPI->EnableFastClip( true ); + g_pShaderAPI->SetFastClipPlane( m_CustomClipPlanes[iCustomPlanes - 1].fValues ); + } + } +} + + +//----------------------------------------------------------------------------- +// Creates/destroys morph data associated w/ a particular material +//----------------------------------------------------------------------------- +IMorph *CMatRenderContext::CreateMorph( MorphFormat_t format, const char *pDebugName ) +{ + Assert( format != 0 ); + IMorphInternal *pMorph = g_pMorphMgr->CreateMorph( ); + pMorph->Init( format, pDebugName ); + return pMorph; +} + +void CMatRenderContext::DestroyMorph( IMorph *pMorph ) +{ + g_pMorphMgr->DestroyMorph( static_cast<IMorphInternal*>(pMorph) ); +} + +void CMatRenderContext::BindMorph( IMorph *pMorph ) +{ + IMorphInternal *pMorphInternal = static_cast<IMorphInternal*>(pMorph); + + if ( m_pBoundMorph != pMorphInternal ) + { + g_pShaderAPI->FlushBufferedPrimitives(); + + m_pBoundMorph = pMorphInternal; + + bool bEnableHWMorph = false; + if ( pMorphInternal == MATERIAL_MORPH_DECAL ) + { + bEnableHWMorph = true; + } + else if ( pMorphInternal ) + { + bEnableHWMorph = true; + pMorphInternal->Bind( m_pMorphRenderContext ); + } + g_pShaderAPI->EnableHWMorphing( bEnableHWMorph ); + } +} + +IMesh* CMatRenderContext::GetDynamicMesh( bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterial *pAutoBind ) +{ + VPROF_ASSERT_ACCOUNTED( "CMatRenderContext::GetDynamicMesh" ); + if( pAutoBind ) + { + Bind( pAutoBind, NULL ); + } + + if ( pVertexOverride ) + { + if ( CompressionType( pVertexOverride->GetVertexFormat() ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + DebuggerBreak(); + return NULL; + } + } + + // For anything more than 1 bone, imply the last weight from the 1 - the sum of the others. + int nCurrentBoneCount = GetCurrentNumBones(); + Assert( nCurrentBoneCount <= 4 ); + if ( nCurrentBoneCount > 1 ) + { + --nCurrentBoneCount; + } + + return g_pShaderAPI->GetDynamicMesh( GetCurrentMaterialInternal(), nCurrentBoneCount, + buffered, pVertexOverride, pIndexOverride); +} + +IMesh* CMatRenderContext::GetDynamicMeshEx( VertexFormat_t vertexFormat, bool bBuffered, IMesh* pVertexOverride, IMesh* pIndexOverride, IMaterial *pAutoBind ) +{ + VPROF_ASSERT_ACCOUNTED( "CMatRenderContext::GetDynamicMesh" ); + if( pAutoBind ) + { + Bind( pAutoBind, NULL ); + } + + if ( pVertexOverride ) + { + if ( CompressionType( pVertexOverride->GetVertexFormat() ) != VERTEX_COMPRESSION_NONE ) + { + // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) + DebuggerBreak(); + return NULL; + } + } + + // For anything more than 1 bone, imply the last weight from the 1 - the sum of the others. + // FIXME: this seems wrong - in common_vs_fxc.h, we only infer the last weight if we have 3 (not 2) + int nCurrentBoneCount = GetCurrentNumBones(); + Assert( nCurrentBoneCount <= 4 ); + if ( nCurrentBoneCount > 1 ) + { + --nCurrentBoneCount; + } + + return g_pShaderAPI->GetDynamicMeshEx( GetCurrentMaterialInternal(), vertexFormat, nCurrentBoneCount, + bBuffered, pVertexOverride, pIndexOverride ); +} + + +//----------------------------------------------------------------------------- +// Deals with depth range +//----------------------------------------------------------------------------- +void CMatRenderContext::DepthRange( float zNear, float zFar ) +{ + m_Viewport.m_flMinZ = zNear; + m_Viewport.m_flMaxZ = zFar; + g_pShaderAPI->SetViewports( 1, &m_Viewport ); +} + + +//----------------------------------------------------------------------------- +// Private utility function to actually commit the top of the RT/Viewport stack +// to the device. Only called by the push and pop routines above. +//----------------------------------------------------------------------------- +void CMatRenderContext::CommitRenderTargetAndViewport( void ) +{ + Assert( m_RenderTargetStack.Count() > 0 ); + + const RenderTargetStackElement_t &element = m_RenderTargetStack.Top( ); + + for( int rt=0; rt<NELEMS(element.m_pRenderTargets); rt++ ) + { + // If we're dealing with the back buffer + if ( element.m_pRenderTargets[rt] == NULL ) + { + g_pShaderAPI->SetRenderTargetEx(rt); // No texture parameter here indicates back buffer + + if ( IsPC() ) + { + Assert( ImageLoader::SizeInBytes( g_pShaderDevice->GetBackBufferFormat() ) <= 4 ); + g_pShaderAPI->EnableLinearColorSpaceFrameBuffer( false ); + } + + if (rt == 0) // the first rt sets the viewport + { + // If either dimension is negative, set to full bounds of back buffer + if ( (element.m_nViewW < 0) || (element.m_nViewH < 0) ) + { + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + g_pShaderAPI->GetBackBufferDimensions( m_Viewport.m_nWidth, m_Viewport.m_nHeight ); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + else // use the bounds in the element + { + m_Viewport.m_nTopLeftX = element.m_nViewX; + m_Viewport.m_nTopLeftY = element.m_nViewY; + m_Viewport.m_nWidth = element.m_nViewW; + m_Viewport.m_nHeight = element.m_nViewH; + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + } + } + else // We're dealing with a texture + { + ITextureInternal *pTexInt = static_cast<ITextureInternal*>(element.m_pRenderTargets[rt]); + pTexInt->SetRenderTarget( rt, element.m_pDepthTexture ); + + if (rt == 0) + { + if ( IsPC() ) + { + if( element.m_pRenderTargets[rt]->GetImageFormat() == IMAGE_FORMAT_RGBA16161616F ) + { + g_pShaderAPI->EnableLinearColorSpaceFrameBuffer( true ); + } + else + { + g_pShaderAPI->EnableLinearColorSpaceFrameBuffer( false ); + } + } + + // If either dimension is negative, set to full bounds of target + if ( (element.m_nViewW < 0) || (element.m_nViewH < 0) ) + { + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + m_Viewport.m_nWidth = element.m_pRenderTargets[rt]->GetActualWidth(); + m_Viewport.m_nHeight = element.m_pRenderTargets[rt]->GetActualHeight(); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + else // use the bounds passed in + { + m_Viewport.m_nTopLeftX = element.m_nViewX; + m_Viewport.m_nTopLeftY = element.m_nViewY; + m_Viewport.m_nWidth = element.m_nViewW; + m_Viewport.m_nHeight = element.m_nViewH; + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + } + } + } +} + + +void CMatRenderContext::SetFrameBufferCopyTexture( ITexture *pTexture, int textureIndex ) +{ + if( textureIndex < 0 || textureIndex > MAX_FB_TEXTURES ) + { + Assert( 0 ); + return; + } + if( m_pCurrentFrameBufferCopyTexture[textureIndex] != pTexture ) + { + g_pShaderAPI->FlushBufferedPrimitives(); + } + // FIXME: Do I need to increment/decrement ref counts here, or assume that the app is going to do it? + m_pCurrentFrameBufferCopyTexture[textureIndex] = pTexture; +} + +void CMatRenderContext::BindLocalCubemap( ITexture *pTexture ) +{ + ITexture *pPreviousTexture = m_pLocalCubemapTexture; + + CMatRenderContextBase::BindLocalCubemap( pTexture ); + + if( m_pLocalCubemapTexture != pPreviousTexture ) + { + g_pShaderAPI->FlushBufferedPrimitives(); + } +} + +void CMatRenderContext::SetNonInteractivePacifierTexture( ITexture *pTexture, float flNormalizedX, float flNormalizedY, float flNormalizedSize ) +{ + m_pNonInteractivePacifier.Init( pTexture ); + m_flNormalizedX = flNormalizedX; + m_flNormalizedY = flNormalizedY; + m_flNormalizedSize = flNormalizedSize; +} + +void CMatRenderContext::SetNonInteractiveTempFullscreenBuffer( ITexture *pTexture, MaterialNonInteractiveMode_t mode ) +{ + if ( mode != MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + m_pNonInteractiveTempFullscreenBuffer[mode].Init( pTexture ); + } +} + +void CMatRenderContext::RefreshFrontBufferNonInteractive() +{ + g_pShaderDevice->RefreshFrontBufferNonInteractive(); +#ifdef _X360 + if ( s_bDirtyDisk ) + { + if ( m_NonInteractiveMode == MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + SpinPresent(); + } + else + { + while ( true ) + { + g_pShaderDevice->RefreshFrontBufferNonInteractive(); + } + } + } +#endif +} + +void CMatRenderContext::EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode ) +{ + m_NonInteractiveMode = mode; + if ( mode == MATERIAL_NON_INTERACTIVE_MODE_NONE ) + { + g_pShaderDevice->EnableNonInteractiveMode( mode ); + } + else + { + ShaderNonInteractiveInfo_t info; + info.m_flNormalizedX = m_flNormalizedX; + info.m_flNormalizedY = m_flNormalizedY; + info.m_flNormalizedSize = m_flNormalizedSize; + + ITextureInternal *pTexInternal = static_cast<ITextureInternal*>( (ITexture*)m_pNonInteractiveTempFullscreenBuffer[mode] ); + info.m_hTempFullscreenTexture = pTexInternal ? + pTexInternal->GetTextureHandle(0) : INVALID_SHADERAPI_TEXTURE_HANDLE; + ITextureInternal *pTexPacifierInternal = static_cast<ITextureInternal*>( (ITexture*)m_pNonInteractivePacifier ); + info.m_nPacifierCount = pTexPacifierInternal ? pTexPacifierInternal->GetNumAnimationFrames() : 0; + for ( int i = 0; i < info.m_nPacifierCount; ++i ) + { + info.m_pPacifierTextures[i] = pTexPacifierInternal->GetTextureHandle( i ); + } + g_pShaderDevice->EnableNonInteractiveMode( mode, &info ); + } +} + +void CMatRenderContext::SetRenderTargetEx( int nRenderTargetID, ITexture *pNewTarget ) +{ + // Verify valid top of RT stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Grab the old target + ITexture *pOldTarget = m_RenderTargetStack.Top().m_pRenderTargets[nRenderTargetID]; + + CMatRenderContextBase::SetRenderTargetEx( nRenderTargetID, pNewTarget ); + + // If we're actually changing render targets + if( pNewTarget != pOldTarget ) + { + // If we're going to render to the back buffer + if ( pNewTarget == NULL ) + { + if ( nRenderTargetID == 0) // reset viewport on set of rt 0 + { + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + g_pShaderAPI->GetBackBufferDimensions( m_Viewport.m_nWidth, m_Viewport.m_nHeight ); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + g_pShaderAPI->SetRenderTargetEx( nRenderTargetID ); // No parameter here indicates back buffer + } + else + { + // If we're going to render to a texture + // Make sure the texture is a render target... + bool reset = true; + if (nRenderTargetID==0) + { + // reset vp on change of rt#0 + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + m_Viewport.m_nWidth = pNewTarget->GetActualWidth(); + m_Viewport.m_nHeight = pNewTarget->GetActualHeight(); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + ITextureInternal *pTexInt = static_cast<ITextureInternal*>(pNewTarget); + if ( pTexInt ) + { + reset = !pTexInt->SetRenderTarget( nRenderTargetID ); + if ( reset ) + { + g_pShaderAPI->SetRenderTargetEx( nRenderTargetID ); + } + } + + if( pNewTarget && pNewTarget->GetImageFormat() == IMAGE_FORMAT_RGBA16161616F ) + { + g_pShaderAPI->EnableLinearColorSpaceFrameBuffer( true ); + } + else + { + g_pShaderAPI->EnableLinearColorSpaceFrameBuffer( false ); + } + } + } + CommitRenderTargetAndViewport(); +} + + +void CMatRenderContext::GetRenderTargetDimensions( int &width, int &height ) const +{ + // Target at top of stack + ITexture *pTOS = m_RenderTargetStack.Top().m_pRenderTargets[0]; + + // If top of stack isn't the back buffer, get dimensions from the texture + if ( pTOS != NULL ) + { + width = pTOS->GetActualWidth(); + height = pTOS->GetActualHeight(); + } + else // otherwise, get them from the shader API + { + g_pShaderAPI->GetBackBufferDimensions( width, height ); + } +} + + +//----------------------------------------------------------------------------- +// What are the lightmap dimensions? +//----------------------------------------------------------------------------- +void CMatRenderContext::GetLightmapDimensions( int *w, int *h ) +{ + *w = GetMaterialSystem()->GetLightmapWidth( GetLightmapPage() ); + *h = GetMaterialSystem()->GetLightmapHeight( GetLightmapPage() ); +} + +void CMatRenderContext::DrawScreenSpaceQuad( IMaterial* pMaterial ) +{ + // Despite saying we render a full screen quad, this actually renders a single triangle + // that covers the whole screen. + int w, h; + + GetRenderTargetDimensions( w, h ); + if ( ( w == 0 ) || ( h == 0 ) ) + return; + + // DX9 disagrees about (0, 0) in a render target and (0, 0) in the texture. + // Fix that here by doing a half-pixel offset for the pixel. + // Because we are working in clip space which is 2 units across, the adjustment factor is 1. + float flOffsetW = 1.0f / w; + float flOffsetH = 1.0f / h; + + Bind( pMaterial ); + IMesh* pMesh = GetDynamicMesh( true ); + + CMeshBuilder meshBuilder;; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 1 ); + + enum { TL, BL, TR, COORDS_COUNT }; + + struct CoordSSQ_t + { + float x, y; + float u, v; + }; + + CoordSSQ_t coords[] = { + { -1.0f - 1.0f * flOffsetW, 1.0f + 1.0f * flOffsetH, 0.0f, 0.0f }, // TL + { -1.0f - 1.0f * flOffsetW, -3.0f + 1.0f * flOffsetH, 0.0f, 2.0f }, // BL + { 3.0f - 1.0f * flOffsetW, 1.0f + 1.0f * flOffsetH, 2.0f, 0.0f }, // TR + }; + + static_assert( ARRAYSIZE( coords ) == COORDS_COUNT, "Unexpected number of coords in triangle, should match enum." ); + + MatrixMode( MATERIAL_VIEW ); + PushMatrix(); + LoadIdentity(); + + MatrixMode( MATERIAL_PROJECTION ); + PushMatrix(); + LoadIdentity(); + + for ( int i = 0; i < COORDS_COUNT; ++i ) + { + meshBuilder.Position3f( coords[ i ].x, coords[ i ].y, 0.0f ); + meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f ); + meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f ); + meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f ); + meshBuilder.TexCoord2f( 0, coords[ i ].u, coords[ i ].v ); + + meshBuilder.AdvanceVertex(); + } + + meshBuilder.End(); + pMesh->Draw(); + + MatrixMode( MATERIAL_VIEW ); + PopMatrix(); + + MatrixMode( MATERIAL_PROJECTION ); + PopMatrix(); +} + +void CMatRenderContext::DrawScreenSpaceRectangle( + IMaterial *pMaterial, + int destx, int desty, + int width, int height, + float src_texture_x0, float src_texture_y0, // which texel you want to appear at + // destx/y + float src_texture_x1, float src_texture_y1, // which texel you want to appear at + // destx+width-1, desty+height-1 + int src_texture_width, int src_texture_height, // needed for fixup + void *pClientRenderable, + int nXDice, int nYDice ) // Amount to tessellate the quad +{ + pMaterial = ((IMaterialInternal *)pMaterial)->GetRealTimeVersion(); + + ::DrawScreenSpaceRectangle( pMaterial, destx, desty, width, height, + src_texture_x0, src_texture_y0, src_texture_x1, src_texture_y1, + src_texture_width, src_texture_height, pClientRenderable, nXDice, nYDice ); + return; +} + +static int CompareVertexFormats( VertexFormat_t Fmt1, VertexFormat_t Fmt2 ) +{ + if ( Fmt1 != Fmt2 ) + { + if ( Fmt1 > Fmt2 ) + return 1; + else + return -1; + } + else + return 0; +} + +int CMatRenderContext::CompareMaterialCombos( IMaterial *pMaterial1, IMaterial *pMaterial2, int lightMapID1, int lightMapID2 ) +{ + pMaterial1 = ((IMaterialInternal *)pMaterial1)->GetRealTimeVersion(); //always work with the real time version of materials internally. + pMaterial2 = ((IMaterialInternal *)pMaterial2)->GetRealTimeVersion(); //always work with the real time version of materials internally. + + IMaterialInternal *pMat1 = (IMaterialInternal *)pMaterial1; + IMaterialInternal *pMat2 = (IMaterialInternal *)pMaterial2; + ShaderRenderState_t *pState1 = pMat1->GetRenderState(); + ShaderRenderState_t *pState2 = pMat2->GetRenderState(); + int dPass = pState2->m_pSnapshots->m_nPassCount - pState1->m_pSnapshots->m_nPassCount; + if ( dPass ) + return dPass; + + if ( pState1->m_pSnapshots->m_nPassCount > 1 ) + { + int dFormat = CompareVertexFormats( pMat1->GetVertexFormat(), pMat2->GetVertexFormat() ); + if ( dFormat ) + return dFormat; + } + + for ( int i = 0; i < pState1->m_pSnapshots->m_nPassCount; i++ ) + { + // UNDONE: Compare snapshots in the shaderapi? + int dSnapshot = pState1->m_pSnapshots->m_Snapshot[i] - pState2->m_pSnapshots->m_Snapshot[i]; + if ( dSnapshot ) + { + dSnapshot = g_pShaderAPI->CompareSnapshots( pState1->m_pSnapshots->m_Snapshot[i], pState2->m_pSnapshots->m_Snapshot[i] ); + if ( dSnapshot ) + return dSnapshot; + } + } + + int dFormat = CompareVertexFormats( pMat1->GetVertexFormat(), pMat2->GetVertexFormat() ); + if ( dFormat ) + return dFormat; + + IMaterialVar **pParams1 = pMat1->GetShaderParams(); + IMaterialVar **pParams2 = pMat2->GetShaderParams(); + int nParams1 = pMat1->ShaderParamCount(); + int nParams2 = pMat2->ShaderParamCount(); + int nBaseTexParamType1 = pParams1 && nParams1 > BASETEXTURE ? pParams1[BASETEXTURE]->GetType() : -1; + int nBaseTexParamType2 = pParams2 && nParams2 > BASETEXTURE ? pParams2[BASETEXTURE]->GetType() : -1; + if( nBaseTexParamType1 == MATERIAL_VAR_TYPE_TEXTURE || nBaseTexParamType2 == MATERIAL_VAR_TYPE_TEXTURE ) + { + if( nBaseTexParamType1 != nBaseTexParamType2 ) + { + return nBaseTexParamType2 - nBaseTexParamType1; + } + int dBaseTexture = Q_stricmp( pParams1[BASETEXTURE]->GetTextureValue()->GetName(), pParams2[BASETEXTURE]->GetTextureValue()->GetName() ); + if ( dBaseTexture ) + return dBaseTexture; + } + + int dLightmap = lightMapID1 - lightMapID2; + if ( dLightmap ) + return dLightmap; + + return (int)pMat1 - (int)pMat2; +} + + +void CMatRenderContext::Bind( IMaterial *iMaterial, void *proxyData ) +{ + if ( !iMaterial ) + { + if ( !g_pErrorMaterial ) + return; + Warning( "Programming error: CMatRenderContext::Bind: NULL material\n" ); + iMaterial = g_pErrorMaterial; + } + else + { + iMaterial = iMaterial->CheckProxyReplacement( proxyData ); + } + + IMaterialInternal *material = static_cast<IMaterialInternal *>( iMaterial ); + material = material->GetRealTimeVersion(); //always work with the real time versions of materials internally + if ( material->GetReferenceCount() <= 0 ) + { + static ConVarRef matTextureListConVar( "mat_texture_list" ); + static ConVarRef matShowWaterTextureConVar( "mat_showwatertextures" ); + + if ( ( !matTextureListConVar.IsValid() || !matTextureListConVar.GetBool() ) && + ( !matShowWaterTextureConVar.IsValid() || !matShowWaterTextureConVar.GetBool() )) + { + Warning( "Material %s has bad reference count %d when being bound\n", material->GetName(), material->GetReferenceCount() ); + // The usual solution for this for global materials that really don't need refcounting is to do material->AddRef(); + Assert( 0 ); + iMaterial = g_pErrorMaterial; + } + } + + if (g_config.bDrawFlat && !material->NoDebugOverride()) + { + material = static_cast<IMaterialInternal *>( GetDrawFlatMaterial() ); + } + + CMatRenderContextBase::Bind( iMaterial, proxyData ); + + // We've always gotta call the bind proxy + SyncMatrices(); + if ( GetMaterialSystem()->GetThreadMode() == MATERIAL_SINGLE_THREADED ) + { + GetCurrentMaterialInternal()->CallBindProxy( proxyData ); + } + g_pShaderAPI->Bind( GetCurrentMaterialInternal() ); +} + +void CMatRenderContext::CopyRenderTargetToTextureEx( ITexture *pTexture, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + if ( !pTexture ) + { + Assert( 0 ); + return; + } + + GetMaterialSystem()->Flush( false ); + ITextureInternal *pTextureInternal = (ITextureInternal *)pTexture; + + if ( IsPC() || !IsX360() ) + { + pTextureInternal->CopyFrameBufferToMe( nRenderTargetID, pSrcRect, pDstRect ); + } + else + { + // X360 only does 1:1 resolves. So we can do full resolves to textures of size + // equal or greater than the viewport trivially. Downsizing is nasty. + Rect_t srcRect; + if ( !pSrcRect ) + { + // build out source rect + pSrcRect = &srcRect; + int x, y, w, h; + GetViewport( x, y, w, h ); + + pSrcRect->x = 0; + pSrcRect->y = 0; + pSrcRect->width = w; + pSrcRect->height = h; + } + + Rect_t dstRect; + if ( !pDstRect ) + { + // build out target rect + pDstRect = &dstRect; + + pDstRect->x = 0; + pDstRect->y = 0; + pDstRect->width = pTexture->GetActualWidth(); + pDstRect->height = pTexture->GetActualHeight(); + } + + if ( pSrcRect->width == pDstRect->width && pSrcRect->height == pDstRect->height ) + { + // 1:1 mapping, no stretching needed, use direct path + pTextureInternal->CopyFrameBufferToMe( nRenderTargetID, pSrcRect, pDstRect ); + return; + } + + if( (pDstRect->x == 0) && (pDstRect->y == 0) && + (pDstRect->width == pTexture->GetActualWidth()) && (pDstRect->height == pTexture->GetActualHeight()) && + (pDstRect->width >= pSrcRect->width) && (pDstRect->height >= pSrcRect->height) ) + { + // Resolve takes up the whole texture, and the texture is large enough to hold the resolve. + // This is turned into a 1:1 resolve within shaderapi by making D3D think the texture is smaller from now on. (Until it resolves from a bigger source) + pTextureInternal->CopyFrameBufferToMe( nRenderTargetID, pSrcRect, pDstRect ); + return; + } + + // currently assuming disparate copies are only for FB blits + // ensure active render target is actually the back buffer + Assert( m_RenderTargetStack.Top().m_pRenderTargets[0] == NULL ); + + // nasty sequence: + // resolve FB surface to matching clone DDR texture + // gpu draw from clone DDR FB texture to disparate RT target surface + // resolve to its matching DDR clone texture + ITextureInternal *pFullFrameFB = (ITextureInternal*)GetMaterialSystem()->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); + pFullFrameFB->CopyFrameBufferToMe( nRenderTargetID, NULL, NULL ); + + // target texture must be a render target + PushRenderTargetAndViewport( pTexture ); + + // blit FB source to render target + DrawScreenSpaceRectangle( + GetMaterialSystem()->GetRenderTargetBlitMaterial(), + pDstRect->x, pDstRect->y, pDstRect->width, pDstRect->height, + pSrcRect->x, pSrcRect->y, pSrcRect->x+pSrcRect->width-1, pSrcRect->y+pSrcRect->height-1, + pFullFrameFB->GetActualWidth(), pFullFrameFB->GetActualHeight() ); + + // resolve render target to texture + ((ITextureInternal *)pTexture)->CopyFrameBufferToMe( 0, NULL, NULL ); + + // restore render target and viewport + PopRenderTargetAndViewport(); + } +} + +void CMatRenderContext::CopyRenderTargetToTexture( ITexture *pTexture ) +{ + CopyRenderTargetToTextureEx( pTexture, NULL, NULL ); +} + + +void CMatRenderContext::CopyTextureToRenderTargetEx( int nRenderTargetID, ITexture *pTexture, Rect_t *pSrcRect, Rect_t *pDstRect ) +{ + if ( !pTexture ) + { + Assert( 0 ); + return; + } + + GetMaterialSystem()->Flush( false ); + ITextureInternal *pTextureInternal = (ITextureInternal *)pTexture; + + if ( IsPC() || !IsX360() ) + { + pTextureInternal->CopyMeToFrameBuffer( nRenderTargetID, pSrcRect, pDstRect ); + } + else + { + Assert( 0 ); + } +} + + +void CMatRenderContext::ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil ) +{ + int width, height; + GetRenderTargetDimensions( width, height ); + g_pShaderAPI->ClearBuffers( bClearColor, bClearDepth, bClearStencil, width, height ); +} + +void CMatRenderContext::DrawClearBufferQuad( unsigned char r, unsigned char g, unsigned char b, unsigned char a, bool bClearColor, bool bClearAlpha, bool bClearDepth ) +{ + IMaterialInternal *pClearMaterial = GetBufferClearObeyStencil( bClearColor + ( bClearAlpha << 1 ) + ( bClearDepth << 2 ) ); + Bind( pClearMaterial ); + + IMesh* pMesh = GetDynamicMesh( true ); + + MatrixMode( MATERIAL_MODEL ); + PushMatrix(); + LoadIdentity(); + + MatrixMode( MATERIAL_VIEW ); + PushMatrix(); + LoadIdentity(); + + MatrixMode( MATERIAL_PROJECTION ); + PushMatrix(); + LoadIdentity(); + + float flDepth = GetMaterialSystem()->GetConfig().bReverseDepth ? 0.0f : 1.0f; + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); + + //1.1 instead of 1.0 to fix small borders around the edges in full screen with anti-aliasing enabled + meshBuilder.Position3f( -1.1f, -1.1f, flDepth ); + meshBuilder.Color4ub( r, g, b, a ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( -1.1f, 1.1f, flDepth ); + meshBuilder.Color4ub( r, g, b, a ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( 1.1f, 1.1f, flDepth ); + meshBuilder.Color4ub( r, g, b, a ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( 1.1f, -1.1f, flDepth ); + meshBuilder.Color4ub( r, g, b, a ); + meshBuilder.AdvanceVertex(); + + meshBuilder.End(); + pMesh->Draw(); + + MatrixMode( MATERIAL_MODEL ); + PopMatrix(); + + MatrixMode( MATERIAL_VIEW ); + PopMatrix(); + + MatrixMode( MATERIAL_PROJECTION ); + PopMatrix(); +} + +//----------------------------------------------------------------------------- +// Should really be called SetViewport +//----------------------------------------------------------------------------- +void CMatRenderContext::Viewport( int x, int y, int width, int height ) +{ + CMatRenderContextBase::Viewport( x, y, width, height ); + + // If either dimension is negative, set to full bounds of current target + if ( (width < 0) || (height < 0) ) + { + ITexture *pTarget = m_RenderTargetStack.Top().m_pRenderTargets[0]; + + // If target is the back buffer + if ( pTarget == NULL ) + { + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + g_pShaderAPI->GetBackBufferDimensions( m_Viewport.m_nWidth, m_Viewport.m_nHeight ); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + else // target is a texture + { + m_Viewport.m_nTopLeftX = 0; + m_Viewport.m_nTopLeftY = 0; + m_Viewport.m_nWidth = pTarget->GetActualWidth(); + m_Viewport.m_nHeight = pTarget->GetActualHeight(); + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } + } + else // use the bounds passed in + { + m_Viewport.m_nTopLeftX = x; + m_Viewport.m_nTopLeftY = y; + m_Viewport.m_nWidth = width; + m_Viewport.m_nHeight = height; + g_pShaderAPI->SetViewports( 1, &m_Viewport ); + } +} + +void CMatRenderContext::GetViewport( int& x, int& y, int& width, int& height ) const +{ + // Verify valid top of RT stack + Assert ( m_RenderTargetStack.Count() > 0 ); + + // Grab the top of stack + const RenderTargetStackElement_t& element = m_RenderTargetStack.Top(); + + // If either dimension is not positive, set to full bounds of current target + if ( (element.m_nViewW <= 0) || (element.m_nViewH <= 0) ) + { + // Viewport origin at target origin + x = y = 0; + + // If target is back buffer + if ( element.m_pRenderTargets[0] == NULL ) + { + g_pShaderAPI->GetBackBufferDimensions( width, height ); + } + else // if target is texture + { + width = element.m_pRenderTargets[0]->GetActualWidth(); + height = element.m_pRenderTargets[0]->GetActualHeight(); + } + } + else // use the bounds from the stack directly + { + x = element.m_nViewX; + y = element.m_nViewY; + width = element.m_nViewW; + height = element.m_nViewH; + } +} + + + +//----------------------------------------------------------------------------- +// Methods related to user clip planes +//----------------------------------------------------------------------------- +void CMatRenderContext::UpdateHeightClipUserClipPlane( void ) +{ + PlaneStackElement pse; + pse.bHack_IsHeightClipPlane = true; + + int iExistingHeightClipPlaneIndex; + for( iExistingHeightClipPlaneIndex = m_CustomClipPlanes.Count(); --iExistingHeightClipPlaneIndex >= 0; ) + { + if( m_CustomClipPlanes[iExistingHeightClipPlaneIndex].bHack_IsHeightClipPlane ) + break; + } + + switch( m_HeightClipMode ) + { + case MATERIAL_HEIGHTCLIPMODE_DISABLE: + if( iExistingHeightClipPlaneIndex != -1 ) + m_CustomClipPlanes.Remove( iExistingHeightClipPlaneIndex ); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT: + pse.fValues[0] = 0.0f; + pse.fValues[1] = 0.0f; + pse.fValues[2] = 1.0f; + pse.fValues[3] = m_HeightClipZ; + if( iExistingHeightClipPlaneIndex != -1 ) + { + memcpy( m_CustomClipPlanes.Base() + iExistingHeightClipPlaneIndex, &pse, sizeof( PlaneStackElement ) ); + } + else + { + m_CustomClipPlanes.AddToTail( pse ); + } + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT: + pse.fValues[0] = 0.0f; + pse.fValues[1] = 0.0f; + pse.fValues[2] = -1.0f; + pse.fValues[3] = -m_HeightClipZ; + if( iExistingHeightClipPlaneIndex != -1 ) + { + memcpy( m_CustomClipPlanes.Base() + iExistingHeightClipPlaneIndex, &pse, sizeof( PlaneStackElement ) ); + } + else + { + m_CustomClipPlanes.AddToTail( pse ); + } + break; + }; + + ApplyCustomClipPlanes(); + + /*switch( m_HeightClipMode ) + { + case MATERIAL_HEIGHTCLIPMODE_DISABLE: + g_pShaderAPI->EnableClipPlane( 0, false ); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = 1.0f; + plane[3] = m_HeightClipZ; + g_pShaderAPI->SetClipPlane( 0, plane ); + g_pShaderAPI->EnableClipPlane( 0, true ); + break; + case MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT: + plane[0] = 0.0f; + plane[1] = 0.0f; + plane[2] = -1.0f; + plane[3] = -m_HeightClipZ; + g_pShaderAPI->SetClipPlane( 0, plane ); + g_pShaderAPI->EnableClipPlane( 0, true ); + break; + }*/ +} + + +//----------------------------------------------------------------------------- +// Lightmap stuff +//----------------------------------------------------------------------------- +void CMatRenderContext::BindLightmapPage( int lightmapPageID ) +{ + if ( m_lightmapPageID == lightmapPageID ) + return; + + // We gotta make sure there's no buffered primitives 'cause this'll + // change the render state. + g_pShaderAPI->FlushBufferedPrimitives(); + + CMatRenderContextBase::BindLightmapPage( lightmapPageID ); +} + +void CMatRenderContext::BindLightmapTexture( ITexture *pLightmapTexture ) +{ + if ( ( m_lightmapPageID == MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED ) && ( m_pUserDefinedLightmap == pLightmapTexture ) ) + return; + + // We gotta make sure there's no buffered primitives 'cause this'll + // change the render state. + g_pShaderAPI->FlushBufferedPrimitives(); + + m_lightmapPageID = MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED; + if ( pLightmapTexture ) + { + pLightmapTexture->IncrementReferenceCount(); + } + if ( m_pUserDefinedLightmap ) + { + m_pUserDefinedLightmap->DecrementReferenceCount(); + } + m_pUserDefinedLightmap = static_cast<ITextureInternal*>( pLightmapTexture ); +} + + +void CMatRenderContext::BindLightmap( Sampler_t sampler ) +{ + switch ( m_lightmapPageID ) + { + default: + Assert( ( m_lightmapPageID == 0 && GetLightmaps()->GetNumLightmapPages() == 0 ) || ( m_lightmapPageID >= 0 && m_lightmapPageID < GetLightmaps()->GetNumLightmapPages() ) ); + if( m_lightmapPageID >= 0 && m_lightmapPageID < GetLightmaps()->GetNumLightmapPages() ) + { + g_pShaderAPI->BindTexture( sampler, GetLightmaps()->GetLightmapPageTextureHandle( m_lightmapPageID ) ); + } + break; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED: + AssertOnce( m_pUserDefinedLightmap ); + g_pShaderAPI->BindTexture( sampler, m_pUserDefinedLightmap->GetTextureHandle( 0 ) ); + break; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE: + BindFullbrightLightmap( sampler ); + break; + + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP: + BindBumpedFullbrightLightmap( sampler ); + break; + } +} + +void CMatRenderContext::BindBumpLightmap( Sampler_t sampler ) +{ + switch ( m_lightmapPageID ) + { + default: + Assert( m_lightmapPageID >= 0 && m_lightmapPageID < GetLightmaps()->GetNumLightmapPages() ); + if( m_lightmapPageID >= 0 && m_lightmapPageID < GetLightmaps()->GetNumLightmapPages() ) + { + g_pShaderAPI->BindTexture( sampler, GetLightmaps()->GetLightmapPageTextureHandle( m_lightmapPageID ) ); + g_pShaderAPI->BindTexture( (Sampler_t)(sampler+1), GetLightmaps()->GetLightmapPageTextureHandle( m_lightmapPageID ) ); + g_pShaderAPI->BindTexture( (Sampler_t)(sampler+2), GetLightmaps()->GetLightmapPageTextureHandle( m_lightmapPageID ) ); + } + break; + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED: + AssertOnce( m_pUserDefinedLightmap ); + g_pShaderAPI->BindTexture( sampler, m_pUserDefinedLightmap->GetTextureHandle( 0 ) ); + g_pShaderAPI->BindTexture( (Sampler_t)(sampler+1), m_pUserDefinedLightmap->GetTextureHandle( 0 ) ); + g_pShaderAPI->BindTexture( (Sampler_t)(sampler+2), m_pUserDefinedLightmap->GetTextureHandle( 0 ) ); + break; + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP: + BindBumpedFullbrightLightmap( sampler ); + BindBumpedFullbrightLightmap( (Sampler_t)(sampler+1) ); + BindBumpedFullbrightLightmap( (Sampler_t)(sampler+2) ); + break; + case MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE: + BindBumpedFullbrightLightmap( sampler ); + BindBumpedFullbrightLightmap( (Sampler_t)(sampler+1) ); + BindBumpedFullbrightLightmap( (Sampler_t)(sampler+2) ); + break; + } +} + +void CMatRenderContext::BindFullbrightLightmap( Sampler_t sampler ) +{ + g_pShaderAPI->BindTexture( sampler, GetFullbrightLightmapTextureHandle() ); +} + +void CMatRenderContext::BindBumpedFullbrightLightmap( Sampler_t sampler ) +{ + g_pShaderAPI->BindTexture( sampler, GetFullbrightBumpedLightmapTextureHandle() ); +} + + +//----------------------------------------------------------------------------- +// Bind standard textures +//----------------------------------------------------------------------------- +void CMatRenderContext::BindStandardTexture( Sampler_t sampler, StandardTextureId_t id ) +{ + switch ( id ) + { + case TEXTURE_LIGHTMAP: + BindLightmap( sampler ); + return; + + case TEXTURE_LIGHTMAP_BUMPED: + BindBumpLightmap( sampler ); + return; + + case TEXTURE_LIGHTMAP_FULLBRIGHT: + BindFullbrightLightmap( sampler ); + return; + + case TEXTURE_LIGHTMAP_BUMPED_FULLBRIGHT: + BindBumpedFullbrightLightmap( sampler ); + return; + + case TEXTURE_WHITE: + g_pShaderAPI->BindTexture( sampler, GetWhiteTextureHandle() ); + return; + + case TEXTURE_BLACK: + g_pShaderAPI->BindTexture( sampler, GetBlackTextureHandle() ); + return; + + case TEXTURE_GREY: + g_pShaderAPI->BindTexture( sampler, GetGreyTextureHandle() ); + return; + + case TEXTURE_GREY_ALPHA_ZERO: + g_pShaderAPI->BindTexture( sampler, GetGreyAlphaZeroTextureHandle() ); + return; + + case TEXTURE_NORMALMAP_FLAT: + g_pShaderAPI->BindTexture( sampler, GetFlatNormalTextureHandle() ); + return; + + case TEXTURE_NORMALIZATION_CUBEMAP: + TextureManager()->NormalizationCubemap()->Bind( sampler ); + return; + + case TEXTURE_NORMALIZATION_CUBEMAP_SIGNED: + TextureManager()->SignedNormalizationCubemap()->Bind( sampler ); + return; + + case TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0: + case TEXTURE_FRAME_BUFFER_FULL_TEXTURE_1: + { + int nTextureIndex = id - TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0; + if( m_pCurrentFrameBufferCopyTexture[ nTextureIndex ] ) + { + ( ( ITextureInternal * )m_pCurrentFrameBufferCopyTexture[ nTextureIndex ] )->Bind( sampler ); + } + } + return; + + case TEXTURE_COLOR_CORRECTION_VOLUME_0: + case TEXTURE_COLOR_CORRECTION_VOLUME_1: + case TEXTURE_COLOR_CORRECTION_VOLUME_2: + case TEXTURE_COLOR_CORRECTION_VOLUME_3: + { + ITextureInternal *pTexture = TextureManager()->ColorCorrectionTexture( id - TEXTURE_COLOR_CORRECTION_VOLUME_0 ); + if( pTexture ) + { + pTexture->Bind( sampler ); + } + } + return; + + case TEXTURE_SHADOW_NOISE_2D: + TextureManager()->ShadowNoise2D()->Bind( sampler ); + return; + + case TEXTURE_IDENTITY_LIGHTWARP: + TextureManager()->IdentityLightWarp()->Bind( sampler ); + return; + + case TEXTURE_MORPH_ACCUMULATOR: + g_pMorphMgr->MorphAccumulator()->Bind( sampler ); + return; + + case TEXTURE_MORPH_WEIGHTS: + g_pMorphMgr->MorphWeights()->Bind( sampler ); + return; + + case TEXTURE_FRAME_BUFFER_FULL_DEPTH: + if( m_bFullFrameDepthIsValid ) + TextureManager()->FullFrameDepthTexture()->Bind( sampler ); + else + g_pShaderAPI->BindTexture( sampler, GetMaxDepthTextureHandle() ); + return; + + case TEXTURE_DEBUG_LUXELS: + TextureManager()->DebugLuxels2D()->Bind( sampler ); + return; + + default: + Assert(0); + } +} + +void CMatRenderContext::BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id ) +{ + switch ( id ) + { + case TEXTURE_MORPH_ACCUMULATOR: + g_pMorphMgr->MorphAccumulator()->BindVertexTexture( sampler ); + return; + + case TEXTURE_MORPH_WEIGHTS: + g_pMorphMgr->MorphWeights()->BindVertexTexture( sampler ); + return; + + default: + Assert(0); + } +} + +void CMatRenderContext::GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) +{ + ITexture *pTexture = NULL; + switch ( id ) + { + case TEXTURE_LIGHTMAP: + case TEXTURE_LIGHTMAP_BUMPED: + case TEXTURE_LIGHTMAP_FULLBRIGHT: + case TEXTURE_LIGHTMAP_BUMPED_FULLBRIGHT: + // NOTE: Doesn't exactly work since we may be in fullbright mode +// GetLightmapDimensions( pWidth, pHeight ); +// break; + + case TEXTURE_WHITE: + case TEXTURE_BLACK: + case TEXTURE_GREY: + case TEXTURE_GREY_ALPHA_ZERO: + case TEXTURE_NORMALMAP_FLAT: + default: + Assert( 0 ); + Warning( "GetStandardTextureDimensions: still unimplemented for this type!\n" ); + *pWidth = *pHeight = -1; + break; + + case TEXTURE_NORMALIZATION_CUBEMAP: + pTexture = TextureManager()->NormalizationCubemap(); + break; + + case TEXTURE_NORMALIZATION_CUBEMAP_SIGNED: + pTexture = TextureManager()->SignedNormalizationCubemap(); + break; + + case TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0: + case TEXTURE_FRAME_BUFFER_FULL_TEXTURE_1: + { + int nTextureIndex = id - TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0; + pTexture = m_pCurrentFrameBufferCopyTexture[ nTextureIndex ]; + } + break; + + case TEXTURE_COLOR_CORRECTION_VOLUME_0: + case TEXTURE_COLOR_CORRECTION_VOLUME_1: + case TEXTURE_COLOR_CORRECTION_VOLUME_2: + case TEXTURE_COLOR_CORRECTION_VOLUME_3: + pTexture = TextureManager()->ColorCorrectionTexture( id - TEXTURE_COLOR_CORRECTION_VOLUME_0 ); + break; + + case TEXTURE_SHADOW_NOISE_2D: + pTexture = TextureManager()->ShadowNoise2D(); + break; + + case TEXTURE_IDENTITY_LIGHTWARP: + pTexture = TextureManager()->IdentityLightWarp(); + return; + + case TEXTURE_MORPH_ACCUMULATOR: + pTexture = g_pMorphMgr->MorphAccumulator(); + break; + + case TEXTURE_MORPH_WEIGHTS: + pTexture = g_pMorphMgr->MorphWeights(); + break; + + case TEXTURE_DEBUG_LUXELS: + pTexture = TextureManager()->DebugLuxels2D(); + break; + } + + if ( pTexture ) + { + *pWidth = pTexture->GetActualWidth(); + *pHeight = pTexture->GetActualHeight(); + } + else + { + Warning( "GetStandardTextureDimensions: Couldn't find the texture to get the dimensions!\n" ); + *pWidth = *pHeight = -1; + } +} + +void CMatRenderContext::FogColor3f( float r, float g, float b ) +{ + unsigned char fogColor[3]; + fogColor[0] = clamp( (int)(r * 255.0f), 0, 255 ); + fogColor[1] = clamp( (int)(g * 255.0f), 0, 255 ); + fogColor[2] = clamp( (int)(b * 255.0f), 0, 255 ); + g_pShaderAPI->SceneFogColor3ub( fogColor[0], fogColor[1], fogColor[2] ); +} + +void CMatRenderContext::FogColor3fv( const float* rgb ) +{ + unsigned char fogColor[3]; + fogColor[0] = clamp( (int)(rgb[0] * 255.0f), 0, 255 ); + fogColor[1] = clamp( (int)(rgb[1] * 255.0f), 0, 255 ); + fogColor[2] = clamp( (int)(rgb[2] * 255.0f), 0, 255 ); + g_pShaderAPI->SceneFogColor3ub( fogColor[0], fogColor[1], fogColor[2] ); +} + + + +void CMatRenderContext::SetFlashlightMode( bool bEnable ) +{ + if( bEnable != m_bFlashlightEnable ) + { + g_pShaderAPI->FlushBufferedPrimitives(); + m_bFlashlightEnable = bEnable; + } +} + +bool CMatRenderContext::GetFlashlightMode( ) const +{ + return m_bFlashlightEnable; +} + +void CMatRenderContext::SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ) +{ + g_pShaderAPI->SetFlashlightStateEx( state, worldToTexture, pFlashlightDepthTexture ); + if ( IsPC() && g_config.dxSupportLevel <= 70 ) + { + // Going to go ahead and set a single hardware light here to do all lighting except for + // the spotlight falloff function, which is done with a texture. + SetAmbientLight( 0.0f, 0.0f, 0.0f ); + static Vector4D blackCube[6]; + int i; + for( i = 0; i < 6; i++ ) + { + blackCube[i].Init( 0.0f, 0.0f, 0.0f, 0.0f ); + } + SetAmbientLightCube( blackCube ); + + // Disable all the lights except for the first one. + for( i = 1; i < HardwareConfig()->MaxNumLights(); ++i ) + { + LightDesc_t desc; + desc.m_Type = MATERIAL_LIGHT_DISABLE; + SetLight( i, desc ); + } + + LightDesc_t desc; + desc.m_Type = MATERIAL_LIGHT_POINT; + desc.m_Attenuation0 = state.m_fConstantAtten; + desc.m_Attenuation1 = state.m_fLinearAtten; + desc.m_Attenuation2 = state.m_fQuadraticAtten; + // flashlightfixme: I don't know why this scale has to be here to get fixed function lighting to work. + desc.m_Color.x = state.m_Color[0] * 17000.0f; + desc.m_Color.y = state.m_Color[1] * 17000.0f; + desc.m_Color.z = state.m_Color[2] * 17000.0f; + desc.m_Position = state.m_vecLightOrigin; + + QAngle angles; + QuaternionAngles( state.m_quatOrientation, angles ); + AngleVectors( angles, &desc.m_Direction ); + + desc.m_Range = state.m_FarZ; + desc.m_Falloff = 0.0f; + SetLight( 0, desc ); + } +} + +void CMatRenderContext::SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ) +{ + g_pShaderAPI->SetScissorRect( nLeft, nTop, nRight, nBottom, bEnableScissor ); +} + +void CMatRenderContext::SetToneMappingScaleLinear( const Vector &scale ) +{ + g_pShaderAPI->SetToneMappingScaleLinear( scale ); +} + +void CMatRenderContext::BeginBatch( IMesh* pIndices ) +{ + Assert( !m_pBatchMesh && !m_pBatchIndices); + m_pBatchIndices = pIndices; +} + +void CMatRenderContext::BindBatch( IMesh* pVertices, IMaterial *pAutoBind ) +{ + m_pBatchMesh = GetDynamicMesh( false, pVertices, m_pBatchIndices, pAutoBind ); +} + +void CMatRenderContext::DrawBatch(int firstIndex, int numIndices ) +{ + Assert( m_pBatchMesh ); + m_pBatchMesh->Draw( firstIndex, numIndices ); +} + +void CMatRenderContext::EndBatch() +{ + m_pBatchIndices = NULL; + m_pBatchMesh = NULL; +} + +bool CMatRenderContext::OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices ) +{ + SyncMatrices(); + return true; +} + +bool CMatRenderContext::OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists ) +{ + SyncMatrices(); + return true; +} + +void CMatRenderContext::AsyncCreateTextureFromRenderTarget( ITexture* pSrcRt, const char* pDstName, ImageFormat dstFmt, bool bGenMips, int nAdditionalCreationFlags, IAsyncTextureOperationReceiver* pRecipient, void* pExtraArgs ) +{ + if ( g_pMaterialSystem->GetThreadMode() == MATERIAL_SINGLE_THREADED ) + { + OnAsyncCreateTextureFromRenderTarget( pSrcRt, &pDstName, pRecipient ); + } + + TextureManager()->AsyncCreateTextureFromRenderTarget( pSrcRt, pDstName, dstFmt, bGenMips, nAdditionalCreationFlags, pRecipient, pExtraArgs ); +} + +void CMatRenderContext::AsyncMap( ITextureInternal* pTexToMap, IAsyncTextureOperationReceiver* pRecipient, void* pExtraArgs ) +{ + if ( g_pMaterialSystem->GetThreadMode() == MATERIAL_SINGLE_THREADED ) + { + OnAsyncMap( pTexToMap, pRecipient, pExtraArgs ); + } + + void* pMemory = NULL; + int nPitch = NULL; + + pTexToMap->Map( &pMemory, &nPitch ); + + pRecipient->OnAsyncMapComplete( pTexToMap, pExtraArgs, pMemory, nPitch ); + + // Release references held earlier in OnAsyncMap + SafeRelease( &pRecipient ); + SafeRelease( &pTexToMap ); +} + +void CMatRenderContext::AsyncUnmap( ITextureInternal* pTexToUnmap ) +{ + if ( g_pMaterialSystem->GetThreadMode() == MATERIAL_SINGLE_THREADED ) + { + OnAsyncUnmap( pTexToUnmap ); + } + + pTexToUnmap->Unmap(); + SafeRelease( &pTexToUnmap ); // Matches AddRef from OnAsyncUnmap +} + +void CMatRenderContext::AsyncCopyRenderTargetToStagingTexture( ITexture* pDst, ITexture* pSrc, IAsyncTextureOperationReceiver* pRecipient, void* pExtraArgs ) +{ + if ( g_pMaterialSystem->GetThreadMode() == MATERIAL_SINGLE_THREADED ) + { + OnAsyncCopyRenderTargetToStagingTexture( pDst, pSrc, pRecipient ); + } + + pSrc->CopyToStagingTexture( pDst ); + pRecipient->OnAsyncReadbackBegin( pDst, pSrc, pExtraArgs ); + + SafeRelease( &pDst ); + SafeRelease( &pSrc ); + SafeRelease( &pRecipient ); +} + + +//----------------------------------------------------------------------------- +// Methods related to morph accumulation +//----------------------------------------------------------------------------- +void CMatRenderContext::BeginMorphAccumulation() +{ + g_pMorphMgr->BeginMorphAccumulation( m_pMorphRenderContext ); +} + +void CMatRenderContext::EndMorphAccumulation() +{ + g_pMorphMgr->EndMorphAccumulation( m_pMorphRenderContext ); +} + +void CMatRenderContext::AccumulateMorph( IMorph* pMorph, int nMorphCount, const MorphWeight_t* pWeights ) +{ + g_pMorphMgr->AccumulateMorph( m_pMorphRenderContext, pMorph, nMorphCount, pWeights ); +} + +bool CMatRenderContext::GetMorphAccumulatorTexCoord( Vector2D *pTexCoord, IMorph *pMorph, int nVertex ) +{ + return g_pMorphMgr->GetMorphAccumulatorTexCoord( m_pMorphRenderContext, pTexCoord, pMorph, nVertex ); +} + + +//----------------------------------------------------------------------------- +// Occlusion query support +//----------------------------------------------------------------------------- +OcclusionQueryObjectHandle_t CMatRenderContext::CreateOcclusionQueryObject() +{ + OcclusionQueryObjectHandle_t h = g_pOcclusionQueryMgr->CreateOcclusionQueryObject(); + g_pOcclusionQueryMgr->OnCreateOcclusionQueryObject( h ); + return h; +} + +int CMatRenderContext::OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t h ) +{ + return g_pOcclusionQueryMgr->OcclusionQuery_GetNumPixelsRendered( h, true ); +} + + + + +void CMatRenderContext::SetFullScreenDepthTextureValidityFlag( bool bIsValid ) +{ + m_bFullFrameDepthIsValid = bIsValid; +} + +//----------------------------------------------------------------------------- +// Debug logging +//----------------------------------------------------------------------------- + +void CMatRenderContext::PrintfVA( char *fmt, va_list vargs ) +{ + #if GLMDEBUG + g_pShaderAPI->PrintfVA( fmt, vargs ); + #endif +} + +void CMatRenderContext::Printf( const char *fmt, ... ) +{ + #if GLMDEBUG + va_list vargs; + + va_start(vargs, fmt); + + g_pShaderAPI->PrintfVA( (char *)fmt, vargs ); + + va_end( vargs ); + #endif +} + +float CMatRenderContext::Knob( char *knobname, float *setvalue ) +{ + #if GLMDEBUG + return g_pShaderAPI->Knob( knobname, setvalue ); + #else + return 0.0f; + #endif +} + |