//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: Responsible for drawing the scene // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "view.h" #include "iviewrender.h" #include "view_shared.h" #include "ivieweffects.h" #include "iinput.h" #include "model_types.h" #include "clientsideeffects.h" #include "particlemgr.h" #include "viewrender.h" #include "iclientmode.h" #include "voice_status.h" #include "glow_overlay.h" #include "materialsystem/imesh.h" #include "materialsystem/ITexture.h" #include "materialsystem/IMaterial.h" #include "materialsystem/IMaterialVar.h" #include "materialsystem/IColorCorrection.h" #include "DetailObjectSystem.h" #include "tier0/vprof.h" #include "datacache/imdlcache.h" #include "engine/IEngineTrace.h" #include "engine/ivmodelinfo.h" #include "vstdlib/icommandline.h" #include "view_scene.h" #include "particles_ez.h" #include "engine/IStaticPropMgr.h" #include "engine/ivdebugoverlay.h" #include "smoke_fog_overlay.h" #include "c_pixel_visibility.h" #include "ClientEffectPrecacheSystem.h" #include "c_rope.h" #include "c_effects.h" #include "smoke_fog_overlay.h" #include "materialsystem/imaterialsystemhardwareconfig.h" #include "vgui_int.h" #include "ienginevgui.h" #include "datacache/imdlcache.h" #include "ScreenSpaceEffects.h" #if defined( HL2_CLIENT_DLL ) || defined( CSTRIKE_DLL ) #define USE_MONITORS #endif // GR #include "rendertexture.h" #ifdef _XBOX #include "tier0/mem.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern void ComputeCameraVariables( const Vector &vecOrigin, const QAngle &vecAngles, Vector *pVecForward, Vector *pVecRight, Vector *pVecUp, VMatrix *pMatCamInverse ); static ConVar cl_drawmonitors( "cl_drawmonitors", "1" ); static ConVar cl_overdraw_test( "cl_overdraw_test", "0", FCVAR_CHEAT | FCVAR_NEVER_AS_STRING ); static ConVar r_eyewaterepsilon( "r_eyewaterepsilon", "7.0f", FCVAR_CHEAT ); static void SetClearColorToFogColor( unsigned char ucFogColor[3] ) { materials->GetFogColor( ucFogColor ); if( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) { float scale = LinearToGammaFullRange( materials->GetToneMappingScaleLinear().x ); ucFogColor[0] *= scale; ucFogColor[1] *= scale; ucFogColor[2] *= scale; } materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); } // Used to verify frame syncing. void GenerateOverdrawForTesting() { if ( !cl_overdraw_test.GetInt() ) return; for ( int i=0; i < 40; i++ ) { g_SmokeFogOverlayAlpha = 20 / 255.0; DrawSmokeFogOverlay(); } g_SmokeFogOverlayAlpha = 0; } //----------------------------------------------------------------------------- // Convars related to controlling rendering //----------------------------------------------------------------------------- static ConVar cl_maxrenderable_dist("cl_maxrenderable_dist", "3000", FCVAR_CHEAT, "Max distance from the camera at which things will be rendered" ); ConVar r_updaterefracttexture( "r_updaterefracttexture", "1" ); ConVar r_entityclips( "r_entityclips", "1" ); //FIXME: Nvidia drivers before 81.94 on cards that support user clip planes will have problems with this, require driver update? Detect and disable? // Matches the version in the engine static ConVar r_drawopaqueworld( "r_drawopaqueworld", "1", FCVAR_CHEAT ); static ConVar r_drawtranslucentworld( "r_drawtranslucentworld", "1", FCVAR_CHEAT ); static ConVar r_3dsky( "r_3dsky","1", 0, "Enable the rendering of 3d sky boxes" ); static ConVar r_skybox( "r_skybox","1", FCVAR_CHEAT, "Enable the rendering of sky boxes" ); ConVar r_drawviewmodel( "r_drawviewmodel","1", FCVAR_CHEAT ); static ConVar r_drawtranslucentrenderables( "r_drawtranslucentrenderables", "1", FCVAR_CHEAT ); static ConVar r_drawopaquerenderables( "r_drawopaquerenderables", "1", FCVAR_CHEAT ); static ConVar r_flashlightdrawdepth( "r_flashlightdrawdepth", "0" ); ConVar r_DrawDetailProps( "r_DrawDetailProps", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" ); ConVar mat_bloomscale( "mat_bloomscale", "1" ); //----------------------------------------------------------------------------- // Convars related to fog color //----------------------------------------------------------------------------- static ConVar fog_override( "fog_override", "0", FCVAR_CHEAT ); // set any of these to use the maps fog static ConVar fog_start( "fog_start", "-1" ); static ConVar fog_end( "fog_end", "-1" ); static ConVar fog_color( "fog_color", "-1 -1 -1" ); static ConVar fog_enable( "fog_enable", "1" ); static ConVar fog_startskybox( "fog_startskybox", "-1" ); static ConVar fog_endskybox( "fog_endskybox", "-1" ); static ConVar fog_colorskybox( "fog_colorskybox", "-1 -1 -1" ); static ConVar fog_enableskybox( "fog_enableskybox", "1" ); //----------------------------------------------------------------------------- // Water-related convars //----------------------------------------------------------------------------- static ConVar r_debugcheapwater( "r_debugcheapwater", "0", FCVAR_CHEAT ); static ConVar r_waterforceexpensive( "r_waterforceexpensive", "0" ); static ConVar r_waterforcereflectentities( "r_waterforcereflectentities", "0" ); static ConVar r_WaterDrawRefraction( "r_WaterDrawRefraction", "1", 0, "Enable water refraction" ); static ConVar r_WaterDrawReflection( "r_WaterDrawReflection", "1", 0, "Enable water reflection" ); static ConVar r_ForceWaterLeaf( "r_ForceWaterLeaf", "1", 0, "Enable for optimization to water - considers view in leaf under water for purposes of culling" ); static ConVar mat_drawwater( "mat_drawwater", "1", FCVAR_CHEAT ); static ConVar mat_clipz( "mat_clipz", "1" ); //----------------------------------------------------------------------------- // debugging //----------------------------------------------------------------------------- static ConVar r_visocclusion( "r_visocclusion", "0", FCVAR_CHEAT ); // (the engine owns this cvar). ConVar mat_wireframe( "mat_wireframe", "0", FCVAR_CHEAT ); // sv_cheats is replicated and lives in the engine. re-declaring it here without FCVAR_REPLICATED // breaks the linkage, and declaring it here with FCVAR_REPLICATED wants it to be declared in the server also. const ConVar *sv_cheats = NULL; //----------------------------------------------------------------------------- // debugging overlays //----------------------------------------------------------------------------- static ConVar cl_drawmaterial( "cl_drawmaterial", "", FCVAR_CHEAT, "Draw a particular material over the frame" ); static ConVar cl_drawshadowtexture( "cl_drawshadowtexture", "0", FCVAR_CHEAT ); static ConVar mat_showwatertextures( "mat_showwatertextures", "0", FCVAR_CHEAT ); static ConVar mat_wateroverlaysize( "mat_wateroverlaysize", "128" ); static ConVar mat_showframebuffertexture( "mat_showframebuffertexture", "0", FCVAR_CHEAT ); static ConVar mat_framebuffercopyoverlaysize( "mat_framebuffercopyoverlaysize", "128" ); static ConVar mat_showcamerarendertarget( "mat_showcamerarendertarget", "0", FCVAR_CHEAT ); static ConVar mat_camerarendertargetoverlaysize( "mat_camerarendertargetoverlaysize", "128", FCVAR_CHEAT ); static ConVar mat_hsv( "mat_hsv", "0" ); static ConVar mat_yuv( "mat_yuv", "0" ); #ifdef _XBOX static ConVar mat_viewTextureEnable("mat_viewTextureEnable", "", 0, "Enable view texture"); static ConVar mat_viewTextureName("mat_viewTextureName", "", 0, "Set the view texture name"); static ConVar mat_viewTextureScale("mat_viewTextureScale", "1", 0, "Set the view texture scale"); #endif //----------------------------------------------------------------------------- // Other convars //----------------------------------------------------------------------------- ConVar r_DoCovertTransitions("r_DoCovertTransitions", "1", FCVAR_NEVER_AS_STRING ); // internally used by game code and engine to choose when to allow LOD transitions. ConVar r_TransitionSensitivity("r_TransitionSensitivity", "6", 0, "Controls when LODs are changed. Lower numbers cause more overt LOD transitions."); static ConVar r_screenfademinsize( "r_screenfademinsize", "0" ); static ConVar r_screenfademaxsize( "r_screenfademaxsize", "0" ); //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- static Vector g_vecCurrentRenderOrigin(0,0,0); static QAngle g_vecCurrentRenderAngles(0,0,0); static Vector g_vecCurrentVForward(0,0,0), g_vecCurrentVRight(0,0,0), g_vecCurrentVUp(0,0,0); static VMatrix g_matCurrentCamInverse; static bool s_bCanAccessCurrentView = false; IntroData_t *g_pIntroData = NULL; static bool g_bRenderingView = false; // For debugging... static int g_CurrentViewID = VIEW_NONE; bool g_bRenderingScreenshot = false; int g_viewscene_refractUpdateFrame = 0; // mapmaker controlled autoexposure bool g_bUseCustomAutoExposureMin = false; bool g_bUseCustomAutoExposureMax = false; bool g_bUseCustomBloomScale = false; float g_flCustomAutoExposureMin = 0; float g_flCustomAutoExposureMax = 0; float g_flCustomBloomScale = 0.0f; float g_flCustomBloomScaleMinimum = 0.0f; //----------------------------------------------------------------------------- // Precache of necessary materials //----------------------------------------------------------------------------- #ifdef HL2_CLIENT_DLL CLIENTEFFECT_REGISTER_BEGIN( PrecacheViewRender ) CLIENTEFFECT_MATERIAL( "scripted/intro_screenspaceeffect" ) CLIENTEFFECT_REGISTER_END() #endif #ifndef _XBOX CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffects ) CLIENTEFFECT_MATERIAL( "dev/downsample" ) CLIENTEFFECT_MATERIAL( "dev/downsample_non_hdr" ) CLIENTEFFECT_MATERIAL( "dev/blurfiltery_and_add_nohdr" ) CLIENTEFFECT_MATERIAL( "dev/no_pixel_write" ) CLIENTEFFECT_MATERIAL( "dev/lumcompare" ) CLIENTEFFECT_MATERIAL( "dev/blurfilterx" ) CLIENTEFFECT_MATERIAL( "dev/blurfilterx_nohdr" ) CLIENTEFFECT_MATERIAL( "dev/floattoscreen_combine" ) CLIENTEFFECT_MATERIAL( "dev/copyfullframefb_vanilla" ) CLIENTEFFECT_MATERIAL( "dev/copyfullframefb" ) CLIENTEFFECT_MATERIAL( "dev/blurfiltery" ) CLIENTEFFECT_REGISTER_END_CONDITIONAL( engine->GetDXSupportLevel() >= 90) #endif //----------------------------------------------------------------------------- // Accessors to return the current view being rendered //----------------------------------------------------------------------------- const Vector &CurrentViewOrigin() { Assert( s_bCanAccessCurrentView ); return g_vecCurrentRenderOrigin; } const QAngle &CurrentViewAngles() { Assert( s_bCanAccessCurrentView ); return g_vecCurrentRenderAngles; } const Vector &CurrentViewForward() { Assert( s_bCanAccessCurrentView ); return g_vecCurrentVForward; } const Vector &CurrentViewRight() { Assert( s_bCanAccessCurrentView ); return g_vecCurrentVRight; } const Vector &CurrentViewUp() { Assert( s_bCanAccessCurrentView ); return g_vecCurrentVUp; } const VMatrix &CurrentWorldToViewMatrix() { Assert( s_bCanAccessCurrentView ); return g_matCurrentCamInverse; } //----------------------------------------------------------------------------- // Methods to set the current view/guard access to view parameters //----------------------------------------------------------------------------- void AllowCurrentViewAccess( bool allow ) { s_bCanAccessCurrentView = allow; } bool IsCurrentViewAccessAllowed() { return s_bCanAccessCurrentView; } void SetupCurrentView( const Vector &vecOrigin, const QAngle &angles, view_id_t viewID ) { // Store off view origin and angles g_vecCurrentRenderOrigin = vecOrigin; g_vecCurrentRenderAngles = angles; // Compute the world->main camera transform ComputeCameraVariables( vecOrigin, angles, &g_vecCurrentVForward, &g_vecCurrentVRight, &g_vecCurrentVUp, &g_matCurrentCamInverse ); g_CurrentViewID = viewID; s_bCanAccessCurrentView = true; // Cache off fade distances float flScreenFadeMinSize, flScreenFadeMaxSize; view->GetScreenFadeDistances( &flScreenFadeMinSize, &flScreenFadeMaxSize ); modelinfo->SetViewScreenFadeRange( flScreenFadeMinSize, flScreenFadeMaxSize ); } view_id_t CurrentViewID() { return ( view_id_t )g_CurrentViewID; } void FinishCurrentView() { s_bCanAccessCurrentView = false; } //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CViewRender::CViewRender() { m_AnglesHistoryCounter = 0; memset(m_AnglesHistory, 0, sizeof(m_AnglesHistory)); m_flCheapWaterStartDistance = 0.0f; m_flCheapWaterEndDistance = 0.1f; m_BaseDrawFlags = m_DrawFlags = 0; m_bOverrideVisData = false; m_iForceViewLeaf = -1; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- inline bool CViewRender::ShouldDrawEntities( void ) { return ( !m_pDrawEntities || (m_pDrawEntities->GetInt() != 0) ); } //----------------------------------------------------------------------------- // Purpose: Check all conditions which would prevent drawing the view model // Input : drawViewmodel - // *viewmodel - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CViewRender::ShouldDrawViewModel( bool bDrawViewmodel ) { if ( !bDrawViewmodel ) return false; if ( !r_drawviewmodel.GetBool() ) return false; if ( C_BasePlayer::ShouldDrawLocalPlayer() ) return false; if ( !ShouldDrawEntities() ) return false; if ( render->GetViewEntity() > gpGlobals->maxClients ) return false; return true; } void CViewRender::DrawRenderablesInList( CUtlVector< IClientRenderable * > &list ) { int nCount = list.Count(); for( int i=0; i < nCount; ++i ) { IClientUnknown *pUnk = list[i]->GetIClientUnknown(); Assert( pUnk ); C_BaseEntity *ent = pUnk->GetBaseEntity(); Assert( ent ); // Non-view models wanting to render in view model list... if ( ent->ShouldDraw() ) { ent->DrawModel( STUDIO_RENDER ); } } } //----------------------------------------------------------------------------- // Purpose: Actually draw the view model // Input : drawViewModel - //----------------------------------------------------------------------------- void CViewRender::DrawViewModels( const CViewSetup &view, bool drawViewmodel ) { VPROF( "CViewRender::DrawViewModel" ); if ( !ShouldDrawViewModel( drawViewmodel ) ) return; // Restore the matrices materials->MatrixMode( MATERIAL_PROJECTION ); materials->PushMatrix(); // Set up for drawing the view model render->SetProjectionMatrix( view.fovViewmodel, view.zNearViewmodel, view.zFarViewmodel ); const bool bUseDepthHack = true; // FIXME: Add code to read the current depth range float depthmin = 0.0f; float depthmax = 1.0f; // HACK HACK: Munge the depth range to prevent view model from poking into walls, etc. // Force clipped down range if( bUseDepthHack ) materials->DepthRange( 0.0f, 0.1f ); CUtlVector< IClientRenderable * > opaqueViewModelList( 32 ); CUtlVector< IClientRenderable * > translucentViewModelList( 32 ); ClientLeafSystem()->CollateViewModelRenderables( opaqueViewModelList, translucentViewModelList ); DrawRenderablesInList( opaqueViewModelList ); DrawRenderablesInList( translucentViewModelList ); // Reset the depth range to the original values if( bUseDepthHack ) materials->DepthRange( depthmin, depthmax ); // Restore the matrices materials->MatrixMode( MATERIAL_PROJECTION ); materials->PopMatrix(); } //----------------------------------------------------------------------------- // Purpose: // Input : *pEnt - // Output : int //----------------------------------------------------------------------------- VPlane* CViewRender::GetFrustum() { // The frustum is only valid while in a RenderView call. Assert(g_bRenderingView || g_bRenderingCameraView || g_bRenderingScreenshot); return m_Frustum; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CViewRender::ShouldDrawBrushModels( void ) { if ( m_pDrawBrushModels && !m_pDrawBrushModels->GetInt() ) return false; return true; } //----------------------------------------------------------------------------- // Sort entities in a back-to-front ordering //----------------------------------------------------------------------------- void SortEntities( CRenderList::CEntry *pEntities, int nEntities ) { // Don't sort if we only have 1 entity if ( nEntities <= 1 ) return; float dists[CRenderList::MAX_GROUP_ENTITIES]; const Vector &vecRenderOrigin = CurrentViewOrigin(); const Vector &vecRenderForward = CurrentViewForward(); // First get a distance for each entity. int i; for( i=0; i < nEntities; i++ ) { IClientRenderable *pRenderable = pEntities[i].m_pRenderable; // Compute the center of the object (needed for translucent brush models) Vector boxcenter; Vector mins,maxs; pRenderable->GetRenderBounds( mins, maxs ); VectorAdd( mins, maxs, boxcenter ); VectorMA( pRenderable->GetRenderOrigin(), 0.5f, boxcenter, boxcenter ); // Compute distance... Vector delta; VectorSubtract( boxcenter, vecRenderOrigin, delta ); dists[i] = DotProduct( delta, vecRenderForward ); } // H-sort. int stepSize = 4; while( stepSize ) { int end = nEntities - stepSize; for( i=0; i < end; i += stepSize ) { if( dists[i] > dists[i+stepSize] ) { swap( pEntities[i], pEntities[i+stepSize] ); swap( dists[i], dists[i+stepSize] ); if( i == 0 ) { i = -stepSize; } else { i -= stepSize << 1; } } } stepSize >>= 1; } } void CViewRender::SetupRenderList( const CViewSetup *pView, ClientWorldListInfo_t& info, CRenderList &renderList ) { VPROF( "CViewRender::SetupRenderList" ); // Clear the list. int i; for( i=0; i < RENDER_GROUP_COUNT; i++ ) { renderList.m_RenderGroupCounts[i] = 0; } // Precache information used commonly in CollateRenderables SetupRenderInfo_t setupInfo; setupInfo.m_nRenderFrame = m_BuildRenderableListsNumber; setupInfo.m_nDetailBuildFrame = m_BuildWorldListsNumber; setupInfo.m_pRenderList = &renderList; setupInfo.m_bDrawDetailObjects = g_pClientMode->ShouldDrawDetailObjects() && r_DrawDetailProps.GetInt(); if (pView) { setupInfo.m_vecRenderOrigin = pView->origin; setupInfo.m_flRenderDistSq = cl_maxrenderable_dist.GetFloat(); setupInfo.m_flRenderDistSq *= setupInfo.m_flRenderDistSq; } else { setupInfo.m_flRenderDistSq = 0.0f; } // Now collate the entities in the leaves. if( ShouldDrawEntities() ) { IClientLeafSystem *pClientLeafSystem = ClientLeafSystem(); for( i=0; i < info.m_LeafCount; i++ ) { int nTranslucent = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY]; // Add renderables from this leaf... pClientLeafSystem->CollateRenderablesInLeaf( info.m_pLeafList[i], i, setupInfo ); int nNewTranslucent = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - nTranslucent; if( nNewTranslucent ) { // Sort the new translucent entities. SortEntities( &renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY][nTranslucent], nNewTranslucent ); } } } } static void OverlayWaterTexture( IMaterial *pMaterial, int xOffset, int yOffset, bool bFlip ) { #ifndef _XBOX float xBaseOffset = 0; float yBaseOffset = 0; #else // screen safe float xBaseOffset = 32; float yBaseOffset = 32; #endif float offsetS = ( 0.5f / 256.0f ); float offsetT = ( 0.5f / 256.0f ); float fFlip0 = bFlip ? 1.0f : 0.0f; float fFlip1 = bFlip ? 0.0f : 1.0f; if( !IsErrorMaterial( pMaterial ) ) { materials->Bind( pMaterial ); IMesh* pMesh = materials->GetDynamicMesh( true ); float w = mat_wateroverlaysize.GetFloat(); float h = mat_wateroverlaysize.GetFloat(); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( xBaseOffset + xOffset * w, yBaseOffset + yOffset * h, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, fFlip1 + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( xBaseOffset + ( xOffset + 1 ) * w, yBaseOffset + yOffset * h, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, fFlip1 + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( xBaseOffset + ( xOffset + 1 ) * w, yBaseOffset + ( yOffset + 1 ) * h, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, fFlip0 + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( xBaseOffset + xOffset * w, yBaseOffset + ( yOffset + 1 ) * h, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, fFlip0 + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } } static void OverlayWaterTextures( void ) { OverlayWaterTexture( materials->FindMaterial( "debug/debugreflect", NULL ), 0, 0, false ); OverlayWaterTexture( materials->FindMaterial( "debug/debugrefract", NULL ), 0, 1, true ); } void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float flY, float w, float h ) { float offsetS = ( 0.5f / 256.0f ); float offsetT = ( 0.5f / 256.0f ); IMaterial *pMaterial; pMaterial = materials->FindMaterial( pszMaterialName, TEXTURE_GROUP_OTHER, true ); if( !IsErrorMaterial( pMaterial ) ) { materials->Bind( pMaterial ); IMesh* pMesh = materials->GetDynamicMesh( true ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( flX, flY, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 0.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( flX+w, flY, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 0.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( flX+w, flY+h, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 1.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( flX, flY+h, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 1.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } } static void OverlayFrameBufferTexture( int nFrameBufferIndex ) { float offsetS = ( 0.5f / 256.0f ); float offsetT = ( 0.5f / 256.0f ); IMaterial *pMaterial; char buf[MAX_PATH]; Q_snprintf( buf, MAX_PATH, "debug/debugfbtexture%d", nFrameBufferIndex ); pMaterial = materials->FindMaterial( buf, TEXTURE_GROUP_OTHER, true ); if( !IsErrorMaterial( pMaterial ) ) { materials->Bind( pMaterial ); IMesh* pMesh = materials->GetDynamicMesh( true ); float w = mat_framebuffercopyoverlaysize.GetFloat(); float h = mat_framebuffercopyoverlaysize.GetFloat(); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( w * nFrameBufferIndex, 0.0f, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 0.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( w * ( nFrameBufferIndex + 1 ), 0.0f, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 0.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( w * ( nFrameBufferIndex + 1 ), h, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 1.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( w * nFrameBufferIndex, h, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 1.0f + offsetT ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } } void UpdateClientRenderableInPVSStatus() { // Vis for this view should already be setup at this point. // For each client-only entity, notify it if it's newly coming into the PVS. CUtlLinkedList &theList = ClientEntityList().GetPVSNotifiers(); FOR_EACH_LL( theList, i ) { CClientEntityList::CPVSNotifyInfo *pInfo = &theList[i]; if ( pInfo->m_InPVSStatus & INPVS_YES ) { // Ok, this entity already thinks it's in the PVS. No need to notify it. // We need to set the INPVS_YES_THISFRAME flag if it's in this frame at all, so we // don't tell the entity it's not in the PVS anymore at the end of the frame. if ( !( pInfo->m_InPVSStatus & INPVS_THISFRAME ) ) { if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) ) { pInfo->m_InPVSStatus |= INPVS_THISFRAME; } } } else { // This entity doesn't think it's in the PVS yet. If it is now in the PVS, let it know. if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) ) { pInfo->m_pNotify->OnPVSStatusChanged( true ); pInfo->m_InPVSStatus |= INPVS_YES; pInfo->m_InPVSStatus |= INPVS_THISFRAME; } } } } //----------------------------------------------------------------------------- // Debugging aid to display a texture //----------------------------------------------------------------------------- #ifdef _XBOX static void OverlayShowTexture( const char* textureName, float scale ) { bool foundVar; IMaterial *pMaterial; IMaterialVar *BaseTextureVar; ITexture *pTex; float x, y, w, h; // screen safe x = 32; y = 32; pMaterial = materials->FindMaterial( "___debug", TEXTURE_GROUP_OTHER, true ); BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); if (!foundVar) return; if ( textureName && textureName[0] ) { pTex = materials->FindTexture( textureName, TEXTURE_GROUP_OTHER, false ); BaseTextureVar->SetTextureValue( pTex ); w = pTex->GetActualWidth() * scale; h = pTex->GetActualHeight() * scale; } else { w = h = 64.0f * scale; } materials->Bind( pMaterial ); IMesh* pMesh = materials->GetDynamicMesh( true ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( x, y, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x+w, y, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, 0.0f ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x+w, y+h, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, 1.0f ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x, x+h, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, 1.0f ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } #endif extern ConVar cl_leveloverview; //----------------------------------------------------------------------------- // Purpose: Builds lists of things to render in the world, called once per view //----------------------------------------------------------------------------- void CViewRender::BuildWorldRenderLists( const CViewSetup *pView, ClientWorldListInfo_t& info, bool bUpdateLightmaps, bool bDrawEntities, int iForceViewLeaf ) { VPROF_BUDGET( "BuildWorldRenderLists", VPROF_BUDGETGROUP_WORLD_RENDERING ); // Server entities already know which ones are in the PVS, but client-only entities don't. // We need to know which client-only entities are in the PVS at this point so the shadow // manager can project/occlude their shadows correctly. UpdateClientRenderableInPVSStatus(); ++m_BuildWorldListsNumber; // Override vis data if specified this render, otherwise use default behavior with NULL VisOverrideData_t* pVisData = (m_bOverrideVisData)?(&m_VisData):(NULL); render->BuildWorldLists_VisOverride( &info, bUpdateLightmaps, iForceViewLeaf, pVisData ); // Return to default area portal behavior by using the CurrentView as the starting area m_bOverrideVisData = false; m_iForceViewLeaf = -1; if ( bDrawEntities ) { // Now that we have the list of all leaves, regenerate shadows cast g_pClientShadowMgr->ComputeShadowTextures( pView, info.m_LeafCount, info.m_pLeafList ); // Compute the prop opacity based on the view position and fov zoom scale float flFactor = 1.0f; C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer(); if ( pLocal ) { flFactor = pLocal->GetFOVDistanceAdjustFactor(); } if ( cl_leveloverview.GetFloat() > 0 ) { // disable prop fading flFactor = -1; } // When zoomed in, tweak the opacity to stay visible from further away staticpropmgr->ComputePropOpacity( CurrentViewOrigin(), flFactor ); // Build a list of detail props to render DetailObjectSystem()->BuildDetailObjectRenderLists(); } } //----------------------------------------------------------------------------- // Purpose: Computes the actual world list info based on the render flags //----------------------------------------------------------------------------- ClientWorldListInfo_t *CViewRender::ComputeActualWorldListInfo( const ClientWorldListInfo_t& info, int nDrawFlags, ClientWorldListInfo_t& tmpInfo ) { // Drawing everything? Just return the world list info as-is int nWaterDrawFlags = nDrawFlags & (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER); if ( nWaterDrawFlags == (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER) ) return const_cast(&info); tmpInfo.m_ViewFogVolume = info.m_ViewFogVolume; tmpInfo.m_LeafCount = 0; // Not drawing anything? Then don't bother with renderable lists if ( nWaterDrawFlags == 0 ) return &tmpInfo; // Create a sub-list based on the actual leaves being rendered bool bRenderingUnderwater = (nWaterDrawFlags & DF_RENDER_UNDERWATER) != 0; for ( int i = 0; i < info.m_LeafCount; ++i ) { bool bLeafIsUnderwater = ( info.m_pLeafFogVolume[i] != -1 ); if ( bRenderingUnderwater == bLeafIsUnderwater ) { tmpInfo.m_pLeafList[ tmpInfo.m_LeafCount ] = info.m_pLeafList[ i ]; tmpInfo.m_pLeafFogVolume[ tmpInfo.m_LeafCount ] = info.m_pLeafFogVolume[ i ]; tmpInfo.m_pActualLeafIndex[ tmpInfo.m_LeafCount ] = i; ++tmpInfo.m_LeafCount; } } return &tmpInfo; } //----------------------------------------------------------------------------- // Purpose: Builds render lists for renderables. Called once for refraction, once for over water //----------------------------------------------------------------------------- void CViewRender::BuildRenderableRenderLists( const CViewSetup *pView, ClientWorldListInfo_t& info, CRenderList &renderList ) { MDLCACHE_CRITICAL_SECTION(); ++m_BuildRenderableListsNumber; // For better sorting, find out the leaf *nearest* to the camera // and render translucent objects as if they are in that leaf. if( ShouldDrawEntities() ) { ClientLeafSystem()->ComputeTranslucentRenderLeaf( info.m_LeafCount, info.m_pLeafList, info.m_pLeafFogVolume, m_BuildRenderableListsNumber ); } SetupRenderList( pView, info, renderList ); } //----------------------------------------------------------------------------- // Computes draw flags for the engine to build its world surface lists //----------------------------------------------------------------------------- static inline unsigned long BuildDrawFlags( bool bDrawSkybox, bool bDrawUnderWater, bool bDrawAboveWater, bool bDrawWaterSurface, bool bClipSkybox ) { unsigned long nEngineFlags = 0; if ( bDrawSkybox ) { nEngineFlags |= DRAWWORLDLISTS_DRAW_SKYBOX; } if ( bDrawAboveWater ) { nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYABOVEWATER; nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER; } if ( bDrawUnderWater ) { nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYUNDERWATER; nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER; } if ( bDrawWaterSurface ) { nEngineFlags |= DRAWWORLDLISTS_DRAW_WATERSURFACE; } if( bClipSkybox ) { nEngineFlags |= DRAWWORLDLISTS_DRAW_CLIPSKYBOX; } return nEngineFlags; } void CViewRender::DrawWorld( ClientWorldListInfo_t& info, CRenderList &renderList, int flags, float waterZAdjust ) { VPROF_INCREMENT_COUNTER( "RenderWorld", 1 ); VPROF_BUDGET( "DrawWorld", VPROF_BUDGETGROUP_WORLD_RENDERING ); if( !r_drawopaqueworld.GetBool() ) { return; } bool drawSkybox = (flags & DF_DRAWSKYBOX) != 0; bool drawUnderWater = (flags & DF_RENDER_UNDERWATER) != 0; bool drawAboveWater = (flags & DF_RENDER_ABOVEWATER) != 0; bool drawWaterSurface = (flags & DF_RENDER_WATER) != 0; bool bClipSkybox = (flags & DF_CLIP_SKYBOX ) != 0; unsigned long engineFlags = BuildDrawFlags( drawSkybox, drawUnderWater, drawAboveWater, drawWaterSurface, bClipSkybox ); if ( flags & DF_MAINTAINWORLDLISTS ) { engineFlags |= DRAWWORLDLISTS_MAINTAIN_RENDER_LISTS; } int oldDrawFlags = m_DrawFlags; m_DrawFlags |= flags; render->DrawWorldLists( engineFlags, waterZAdjust ); m_DrawFlags = oldDrawFlags; } static inline void DrawOpaqueRenderable( IClientRenderable *pEnt, bool twoPass ) { float color[3]; pEnt->GetColorModulation( color ); render->SetColorModulation( color ); int flags = STUDIO_RENDER; if (twoPass) { flags |= STUDIO_TWOPASS; } float *pRenderClipPlane = NULL; if( !materials->UsingFastClipping() && //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though r_entityclips.GetBool() ) //checking here reduces the r_entityclips.GetBool() check from 2 times to 1 pRenderClipPlane = pEnt->GetRenderClipPlane(); if( pRenderClipPlane ) materials->PushCustomClipPlane( pRenderClipPlane ); pEnt->DrawModel( flags ); if( pRenderClipPlane ) materials->PopCustomClipPlane(); } static inline void DrawTranslucentRenderable( IClientRenderable *pEnt, bool twoPass ) { // Determine blending amount and tell engine float blend = (float)( pEnt->GetFxBlend() / 255.0f ); // Totally gone if ( blend <= 0.0f ) return; // Tell engine render->SetBlend( blend ); float color[3]; pEnt->GetColorModulation( color ); render->SetColorModulation( color ); int flags = STUDIO_RENDER | STUDIO_TRANSPARENCY; if (twoPass) flags |= STUDIO_TWOPASS; #if 0 Vector mins, maxs; pEnt->GetRenderBounds( mins, maxs ); debugoverlay->AddBoxOverlay( pEnt->GetRenderOrigin(), mins, maxs, pEnt->GetRenderAngles(), 255, 255, 255, 64, 0.01 ); if ( pEnt->GetModel() ) { const char *pName = modelinfo->GetModelName( pEnt->GetModel() ); if ( Q_stricmp( pName, "models/props_c17/tv_monitor01_screen.mdl" ) ) { debugoverlay->AddTextOverlay( pEnt->GetRenderOrigin(), 0.01, pName ); } } #endif float *pRenderClipPlane = NULL; if( !materials->UsingFastClipping() && //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though r_entityclips.GetBool() ) //checking here reduces the r_entityclips.GetBool() check from 2 times to 1 pRenderClipPlane = pEnt->GetRenderClipPlane(); if( pRenderClipPlane ) materials->PushCustomClipPlane( pRenderClipPlane ); pEnt->DrawModel( flags ); if( pRenderClipPlane ) materials->PopCustomClipPlane(); } //----------------------------------------------------------------------------- // Draws all opaque renderables in leaves that were rendered //----------------------------------------------------------------------------- void CViewRender::DrawOpaqueRenderables( ClientWorldListInfo_t& info, CRenderList &renderList ) { VPROF("CViewRender::DrawOpaqueRenderables"); if( !r_drawopaquerenderables.GetBool() ) return; if( !ShouldDrawEntities() ) return; render->SetBlend( 1 ); // Iterate over all leaves that were visible, and draw opaque things in them. RopeManager()->ResetRenderCache(); // Iterate over all leaves that were visible, and draw opaque things in them. int i; // first do the brush models CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH]; int nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH]; for( i=0; i < nOpaque; ++i ) { Assert(pEntities[i].m_TwoPass==0); DrawOpaqueRenderable( pEntities[i].m_pRenderable, false ); } // now the static props pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_STATIC]; nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_STATIC]; if ( nOpaque ) { nOpaque = min( nOpaque, 512 ); IClientRenderable *pStatics[512]; for( i=0; i < nOpaque; ++i ) { pStatics[i] = pEntities[i].m_pRenderable; } staticpropmgr->DrawStaticProps( pStatics, nOpaque ); } // now the other opaque entities pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_ENTITY]; nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_ENTITY]; for( i=0; i < nOpaque; ++i ) { DrawOpaqueRenderable( pEntities[i].m_pRenderable, (pEntities[i].m_TwoPass != 0) ); } RopeManager()->DrawRenderCache(); } //----------------------------------------------------------------------------- // Renders all translucent world + detail objects in a particular set of leaves //----------------------------------------------------------------------------- void CViewRender::DrawTranslucentWorldInLeaves( int iCurLeafIndex, int iFinalLeafIndex, ClientWorldListInfo_t &info, int nDrawFlags ) { VPROF_BUDGET( "CViewRender::DrawTranslucentWorldInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING ); for( ; iCurLeafIndex >= iFinalLeafIndex; iCurLeafIndex-- ) { int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex; Assert( nActualLeafIndex != INVALID_LEAF_INDEX ); if ( render->LeafContainsTranslucentSurfaces( nActualLeafIndex, nDrawFlags ) ) { // Now draw the surfaces in this leaf render->DrawTranslucentSurfaces( nActualLeafIndex, nDrawFlags ); } } } //----------------------------------------------------------------------------- // Renders all translucent world + detail objects in a particular set of leaves //----------------------------------------------------------------------------- void CViewRender::DrawTranslucentWorldAndDetailPropsInLeaves( int iCurLeafIndex, int iFinalLeafIndex, ClientWorldListInfo_t &info, int nDrawFlags, int &nDetailLeafCount, LeafIndex_t* pDetailLeafList ) { VPROF_BUDGET( "CViewRender::DrawTranslucentWorldAndDetailPropsInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING ); for( ; iCurLeafIndex >= iFinalLeafIndex; iCurLeafIndex-- ) { int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex; Assert( nActualLeafIndex != INVALID_LEAF_INDEX ); if ( render->LeafContainsTranslucentSurfaces( nActualLeafIndex, nDrawFlags ) ) { // First draw any queued-up detail props from previously visited leaves DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList ); nDetailLeafCount = 0; // Now draw the surfaces in this leaf render->DrawTranslucentSurfaces( nActualLeafIndex, nDrawFlags ); } // Queue up detail props that existed in this leaf if ( ClientLeafSystem()->ShouldDrawDetailObjectsInLeaf( info.m_pLeafList[iCurLeafIndex], m_BuildWorldListsNumber ) ) { pDetailLeafList[nDetailLeafCount] = info.m_pLeafList[iCurLeafIndex]; ++nDetailLeafCount; } } } //----------------------------------------------------------------------------- // Renders all translucent entities in the render list //----------------------------------------------------------------------------- void CViewRender::DrawTranslucentRenderablesNoWorld( CRenderList &renderList, bool bInSkybox ) { VPROF( "CViewRender::DrawTranslucentRenderablesNoWorld" ); if ( !ShouldDrawEntities() || !r_drawtranslucentrenderables.GetBool() ) return; // Draw the particle singletons. DrawParticleSingletons( bInSkybox ); CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY]; int iCurTranslucentEntity = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1; while( iCurTranslucentEntity >= 0 ) { IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable; if ( pRenderable->UsesFrameBufferTexture() ) { UpdateRefractTexture(); } DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) ); --iCurTranslucentEntity; } // Reset the blend state. render->SetBlend( 1 ); } //----------------------------------------------------------------------------- // Renders all translucent world, entities, and detail objects in a particular set of leaves //----------------------------------------------------------------------------- void CViewRender::DrawTranslucentRenderables( ClientWorldListInfo_t& info, CRenderList &renderList, int nFlags, bool bInSkybox ) { if ( !r_drawtranslucentworld.GetBool() ) { DrawTranslucentRenderablesNoWorld( renderList, bInSkybox ); return; } VPROF( "CViewRender::DrawTranslucentRenderables" ); int iPrevLeaf = info.m_LeafCount - 1; int nDetailLeafCount = 0; LeafIndex_t *pDetailLeafList = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) ); bool bDrawUnderWater = (nFlags & DF_RENDER_UNDERWATER) != 0; bool bDrawAboveWater = (nFlags & DF_RENDER_ABOVEWATER) != 0; bool bDrawWater = (nFlags & DF_RENDER_WATER) != 0; bool bClipSkybox = (nFlags & DF_CLIP_SKYBOX ) != 0; unsigned long nDrawFlags = BuildDrawFlags( false, bDrawUnderWater, bDrawAboveWater, bDrawWater, bClipSkybox ); DetailObjectSystem()->BeginTranslucentDetailRendering(); if ( ShouldDrawEntities() && r_drawtranslucentrenderables.GetBool() ) { // Draw the particle singletons. DrawParticleSingletons( bInSkybox ); CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY]; int iCurTranslucentEntity = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1; bool bRenderingWaterRenderTargets = nFlags & ( DF_RENDER_REFRACTION | DF_RENDER_REFLECTION ); while( iCurTranslucentEntity >= 0 ) { // Seek the current leaf up to our current translucent-entity leaf. int iThisLeaf = pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf; // First draw the translucent parts of the world up to and including those in this leaf DrawTranslucentWorldAndDetailPropsInLeaves( iPrevLeaf, iThisLeaf, info, nDrawFlags, nDetailLeafCount, pDetailLeafList ); // We're traversing the leaf list backwards to get the appropriate sort ordering (back to front) iPrevLeaf = iThisLeaf - 1; // Draw all the translucent entities with this leaf. int nLeaf = info.m_pLeafList[iThisLeaf]; bool bDrawDetailProps = ClientLeafSystem()->ShouldDrawDetailObjectsInLeaf( nLeaf, m_BuildWorldListsNumber ); if ( bDrawDetailProps ) { // Draw detail props up to but not including this leaf Assert( nDetailLeafCount > 0 ); --nDetailLeafCount; Assert( pDetailLeafList[nDetailLeafCount] == nLeaf ); DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList ); // Draw translucent renderables in the leaf interspersed with detail props for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity ) { MDLCACHE_CRITICAL_SECTION(); IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable; // Draw any detail props in this leaf that's farther than the entity const Vector &vecRenderOrigin = pRenderable->GetRenderOrigin(); DetailObjectSystem()->RenderTranslucentDetailObjectsInLeaf( CurrentViewOrigin(), CurrentViewForward(), nLeaf, &vecRenderOrigin ); if ( pRenderable->UsesFrameBufferTexture() ) { if( bRenderingWaterRenderTargets ) { continue; } UpdateRefractTexture(); } // Then draw the translucent renderable DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) ); } // Draw all remaining props in this leaf DetailObjectSystem()->RenderTranslucentDetailObjectsInLeaf( CurrentViewOrigin(), CurrentViewForward(), nLeaf, NULL ); } else { // Draw queued up detail props (we know that the list of detail leaves won't include this leaf, since ShouldDrawDetailObjectsInLeaf is false) // Therefore no fixup on nDetailLeafCount is required as in the above section DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList ); for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity ) { MDLCACHE_CRITICAL_SECTION(); IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable; if ( pRenderable->UsesFrameBufferTexture() ) { if( bRenderingWaterRenderTargets ) { continue; } UpdateRefractTexture(); } DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) ); } } nDetailLeafCount = 0; } } // Draw the rest of the surfaces in world leaves DrawTranslucentWorldAndDetailPropsInLeaves( iPrevLeaf, 0, info, nDrawFlags, nDetailLeafCount, pDetailLeafList ); // Draw any queued-up detail props from previously visited leaves DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList ); // Reset the blend state. render->SetBlend( 1 ); } //----------------------------------------------------------------------------- // Renders the shadow texture to screen... //----------------------------------------------------------------------------- static void RenderMaterial( const char *pMaterialName ) { // So it's not in the very top left float x = 100.0f, y = 100.0f; // float x = 0.0f, y = 0.0f; IMaterial *pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER, false ); if ( !IsErrorMaterial( pMaterial ) ) { materials->Bind( pMaterial ); IMesh* pMesh = materials->GetDynamicMesh( true ); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( x, y, 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x + pMaterial->GetMappingWidth(), y, 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, 0.0f ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x + pMaterial->GetMappingWidth(), y + pMaterial->GetMappingHeight(), 0.0f ); meshBuilder.TexCoord2f( 0, 1.0f, 1.0f ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( x, y + pMaterial->GetMappingHeight(), 0.0f ); meshBuilder.TexCoord2f( 0, 0.0f, 1.0f ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } } //----------------------------------------------------------------------------- // Draws the screen effect //----------------------------------------------------------------------------- static void DrawScreenEffectMaterial( IMaterial *pMaterial, int x, int y, int w, int h ) { Rect_t actualRect; UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect ); ITexture *pTexture = GetFullFrameFrameBufferTexture( 0 ); materials->DrawScreenSpaceRectangle( pMaterial, x, y, w, h, actualRect.x, actualRect.y, actualRect.x+actualRect.width-1, actualRect.y+actualRect.height-1, pTexture->GetActualWidth(), pTexture->GetActualHeight() ); } //----------------------------------------------------------------------------- // Purpose: Performs screen space effects, if any //----------------------------------------------------------------------------- void CViewRender::PerformScreenSpaceEffects( int x, int y, int w, int h ) { VPROF("CViewRender::PerformScreenSpaceEffects()"); // FIXME: Screen-space effects are busted in the editor if ( engine->IsHammerRunning() ) return; g_pScreenSpaceEffects->RenderEffects( x, y, w, h ); } //----------------------------------------------------------------------------- // Purpose: Sets the screen space effect material (can't be done during rendering) //----------------------------------------------------------------------------- void CViewRender::SetScreenOverlayMaterial( IMaterial *pMaterial ) { m_ScreenOverlayMaterial.Init( pMaterial ); } IMaterial *CViewRender::GetScreenOverlayMaterial( ) { return m_ScreenOverlayMaterial; } //----------------------------------------------------------------------------- // Purpose: Performs screen space effects, if any //----------------------------------------------------------------------------- void CViewRender::PerformScreenOverlay( int x, int y, int w, int h ) { VPROF("CViewRender::PerformScreenOverlay()"); if (m_ScreenOverlayMaterial) { if ( m_ScreenOverlayMaterial->NeedsFullFrameBufferTexture() ) { DrawScreenEffectMaterial( m_ScreenOverlayMaterial, x, y, w, h ); } else if ( m_ScreenOverlayMaterial->NeedsPowerOfTwoFrameBufferTexture() ) { // First copy the FB off to the offscreen texture UpdateRefractTexture( x, y, w, h, true ); // Now draw the entire screen using the material... ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( ); int sw = pTexture->GetActualWidth(); int sh = pTexture->GetActualHeight(); materials->DrawScreenSpaceRectangle( m_ScreenOverlayMaterial, x, y, w, h, 0, 0, sw-1, sh-1, sw, sh ); } else { byte color[4] = { 255, 255, 255, 255 }; render->ViewDrawFade( color, m_ScreenOverlayMaterial ); } } } //----------------------------------------------------------------------------- // Purpose: Renders world and all entities, etc. //----------------------------------------------------------------------------- #include "tier0/memdbgoff.h" void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view, int nClearFlags ) { VPROF("CViewRender::DrawWorldAndEntities"); MDLCACHE_CRITICAL_SECTION(); ClientWorldListInfo_t info; #ifndef _XBOX CRenderList renderList; #else void *ptr = MemAllocScratch(sizeof(CRenderList)); CRenderList *renderList = new (ptr) CRenderList; #endif EnableWorldFog(); int nFlags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER; if (bDrawSkybox) { nFlags |= DF_DRAWSKYBOX; } render->Push3DView( view, nClearFlags, false, NULL, m_Frustum ); BuildWorldRenderLists( &view, info, true, true, m_iForceViewLeaf ); #ifndef _XBOX BuildRenderableRenderLists( &view, info, renderList ); #else BuildRenderableRenderLists( &view, info, *renderList ); #endif // Make sure sound doesn't stutter engine->Sound_ExtraUpdate(); m_DrawFlags = m_BaseDrawFlags; #ifndef _XBOX DrawWorld( info, renderList, nFlags, 0.0f ); DrawOpaqueRenderables( info, renderList ); DrawTranslucentRenderables( info, renderList, nFlags, false ); #else DrawWorld( info, *renderList, nFlags, 0.0f ); DrawOpaqueRenderables( info, *renderList ); DrawTranslucentRenderables( info, *renderList, nFlags, false ); MemFreeScratch(); #endif m_DrawFlags = 0; ParticleMgr()->DrawBeforeViewModelEffects(); render->PopView( m_Frustum ); } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Computes us some geometry to render the frustum planes //----------------------------------------------------------------------------- void CViewRender::ComputeFrustumRenderGeometry( Vector pRenderPoint[8] ) { Vector viewPoint = CurrentViewOrigin(); // Find lines along each of the plane intersections. // We know these lines are perpendicular to both plane normals, // so we can take the cross product to find them. static int edgeIdx[4][2] = { { 0, 2 }, { 0, 3 }, { 1, 3 }, { 1, 2 } }; int i; Vector edges[4]; for ( i = 0; i < 4; ++i) { CrossProduct( GetFrustum()[edgeIdx[i][0]].m_Normal, GetFrustum()[edgeIdx[i][1]].m_Normal, edges[i] ); VectorNormalize( edges[i] ); } // Figure out four near points by intersection lines with the near plane // Figure out four far points by intersection with lines against far plane for (i = 0; i < 4; ++i) { float t = (GetFrustum()[4].m_Dist - DotProduct(GetFrustum()[4].m_Normal, viewPoint)) / DotProduct(GetFrustum()[4].m_Normal, edges[i]); VectorMA( viewPoint, t, edges[i], pRenderPoint[i] ); /* t = (m_FrustumPlanes[5][3] - DotProduct(GetFrustum()[5], viewPoint)) / DotProduct(GetFrustum()[5], edges[i]); VectorMA( viewPoint, t, edges[i], pRenderPoint[i + 4] ); */ if (t < 0) { edges[i] *= -1; } VectorMA( pRenderPoint[i], 200.0, edges[i], pRenderPoint[i + 4] ); } } //----------------------------------------------------------------------------- // renders the frustum //----------------------------------------------------------------------------- void CViewRender::RenderFrustum( ) { static int indices[] = { 0, 1, 1, 2, 2, 3, 3, 0, // near square 4, 5, 5, 6, 6, 7, 7, 4, // far square 0, 4, 1, 5, 2, 6, 3, 7 // connections between them }; int numIndices = sizeof(indices) / sizeof(int); Vector vecFrustumRenderPoint[8]; ComputeFrustumRenderGeometry( vecFrustumRenderPoint ); int i; for ( i = 0; i < numIndices; i += 2 ) { debugoverlay->AddLineOverlay( vecFrustumRenderPoint[indices[i]], vecFrustumRenderPoint[indices[i+1]], 0, 0, 255, 255, 1.0f ); } } //----------------------------------------------------------------------------- // Draws all the debugging info //----------------------------------------------------------------------------- void CViewRender::Draw3DDebuggingInfo( const CViewSetup &view ) { VPROF("CViewRender::Draw3DDebuggingInfo"); // Draw 3d overlays render->Draw3DDebugOverlays(); // Draw the line file used for debugging leaks render->DrawLineFile(); // Draw client side effects // NOTE: These are not sorted against the rest of the frame clienteffects->DrawEffects( gpGlobals->frametime ); // Mark the frame as locked down for client fx additions SetFXCreationAllowed( false ); } //----------------------------------------------------------------------------- // Draws all the debugging info //----------------------------------------------------------------------------- void CViewRender::Draw2DDebuggingInfo( const CViewSetup &view ) { if ( IsXbox() && IsRetail() ) return; // HDRFIXME: Assert NULL rendertarget if ( mat_yuv.GetInt() && (engine->GetDXSupportLevel() >= 80) ) { IMaterial *pMaterial; pMaterial = materials->FindMaterial( "debug/yuv", TEXTURE_GROUP_OTHER, true ); if( !IsErrorMaterial( pMaterial ) ) { DrawScreenEffectMaterial( pMaterial, view.x, view.y, view.width, view.height ); } } #ifndef _XBOX if ( mat_hsv.GetInt() && (engine->GetDXSupportLevel() >= 90) ) { IMaterial *pMaterial; pMaterial = materials->FindMaterial( "debug/hsv", TEXTURE_GROUP_OTHER, true ); if( !IsErrorMaterial( pMaterial ) ) { DrawScreenEffectMaterial( pMaterial, view.x, view.y, view.width, view.height ); } } #endif // Draw debugging lightmaps render->DrawLightmaps(); if ( cl_drawshadowtexture.GetInt() ) { g_pClientShadowMgr->RenderShadowTexture( 256, 256 ); } const char *pDrawMaterial = cl_drawmaterial.GetString(); if ( pDrawMaterial && pDrawMaterial[0] ) { RenderMaterial( pDrawMaterial ); } if ( mat_showwatertextures.GetBool() ) { OverlayWaterTextures(); } if ( mat_showcamerarendertarget.GetBool() ) { float w = mat_wateroverlaysize.GetFloat(); float h = mat_wateroverlaysize.GetFloat(); OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h ); } if ( mat_showframebuffertexture.GetBool() ) { // HDRFIXME: Get rid of these rendertarget sets assuming that the assert at the top of this function is true. materials->PushRenderTargetAndViewport( NULL ); OverlayFrameBufferTexture( 0 ); OverlayFrameBufferTexture( 1 ); materials->PopRenderTargetAndViewport( ); } #ifdef _XBOX if ( mat_viewTextureEnable.GetBool() ) { OverlayShowTexture( mat_viewTextureName.GetString(), mat_viewTextureScale.GetFloat() ); } #endif if( r_flashlightdrawdepth.GetBool() ) { shadowmgr->DrawFlashlightDepthTexture( ); } } //----------------------------------------------------------------------------- // Purpose: Returns the min/max fade distances //----------------------------------------------------------------------------- void CViewRender::GetScreenFadeDistances( float *min, float *max ) { if ( min ) { *min = r_screenfademinsize.GetFloat(); } if ( max ) { *max = r_screenfademaxsize.GetFloat(); } } //----------------------------------------------------------------------------- // Purpose: Renders world and all entities, etc. //----------------------------------------------------------------------------- void CViewRender::ViewDrawScene( bool bDrew3dSkybox, bool bSkyboxVisible, const CViewSetup &view, int nClearFlags, view_id_t viewID, bool bDrawViewModel, int baseDrawFlags ) { VPROF( "CViewRender::ViewDrawScene" ); m_BaseDrawFlags = baseDrawFlags; m_DrawFlags = 0; SetupCurrentView( view.origin, view.angles, viewID ); // Invoke pre-render methods IGameSystem::PreRenderAllSystems(); // Start view unsigned int visFlags; SetupVis( view, visFlags ); if ( !bDrew3dSkybox && !bSkyboxVisible && ( visFlags & IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS ) ) { // This covers the case where we don't see a 3dskybox, yet radial vis is clipping // the far plane. Need to clear to fog color in this case. nClearFlags |= VIEW_CLEAR_COLOR; unsigned char ucFogColor[3]; SetClearColorToFogColor( ucFogColor ); } bool drawSkybox = r_skybox.GetBool(); if ( bDrew3dSkybox || !bSkyboxVisible ) { drawSkybox = false; } ParticleMgr()->IncrementFrameCode(); WaterDrawWorldAndEntities( drawSkybox, view, nClearFlags ); // Disable fog for the rest of the stuff DisableFog(); // UNDONE: Don't do this with masked brush models, they should probably be in a separate list // render->DrawMaskEntities() // Here are the overlays... // This is an overlay that goes over everything else CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState ); // issue the pixel visibility tests PixelVisibility_EndCurrentView(); // Draw rain.. DrawPrecipitation(); // Make sure sound doesn't stutter engine->Sound_ExtraUpdate(); // Debugging info goes over the top Draw3DDebuggingInfo( view ); // Invoke post-render methods IGameSystem::PostRenderAllSystems(); FinishCurrentView(); m_DrawFlags = 0; } void CheckAndTransitionColor( float flPercent, float *pColor, float *pLerpToColor ) { if ( pLerpToColor[0] != pColor[0] || pLerpToColor[1] != pColor[1] || pLerpToColor[2] != pColor[2] ) { float flDestColor[3]; flDestColor[0] = pLerpToColor[0]; flDestColor[1] = pLerpToColor[1]; flDestColor[2] = pLerpToColor[2]; pColor[0] = FLerp( pColor[0], flDestColor[0], flPercent ); pColor[1] = FLerp( pColor[1], flDestColor[1], flPercent ); pColor[2] = FLerp( pColor[2], flDestColor[2], flPercent ); } else { pColor[0] = pLerpToColor[0]; pColor[1] = pLerpToColor[1]; pColor[2] = pLerpToColor[2]; } } static void GetFogColorTransition( CPlayerLocalData *local, float *pColorPrimary, float *pColorSecondary ) { if ( local == NULL ) return; if ( local->m_fog.lerptime >= gpGlobals->curtime ) { float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration ); float flPrimaryColorLerp[3] = { local->m_fog.colorPrimaryLerpTo.GetR(), local->m_fog.colorPrimaryLerpTo.GetG(), local->m_fog.colorPrimaryLerpTo.GetB() }; float flSecondaryColorLerp[3] = { local->m_fog.colorSecondaryLerpTo.GetR(), local->m_fog.colorSecondaryLerpTo.GetG(), local->m_fog.colorSecondaryLerpTo.GetB() }; CheckAndTransitionColor( flPercent, pColorPrimary, flPrimaryColorLerp ); CheckAndTransitionColor( flPercent, pColorSecondary, flSecondaryColorLerp ); } } //----------------------------------------------------------------------------- // Purpose: Returns the fog color to use in rendering the current frame. //----------------------------------------------------------------------------- static void GetFogColor( float *pColor ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return; } CPlayerLocalData *local = &pbp->m_Local; const char *fogColorString = fog_color.GetString(); if( fog_override.GetInt() && fogColorString ) { sscanf( fogColorString, "%f%f%f", pColor, pColor+1, pColor+2 ); } else { float flPrimaryColor[3] = { local->m_fog.colorPrimary.GetR(), local->m_fog.colorPrimary.GetG(), local->m_fog.colorPrimary.GetB() }; float flSecondaryColor[3] = { local->m_fog.colorSecondary.GetR(), local->m_fog.colorSecondary.GetG(), local->m_fog.colorSecondary.GetB() }; GetFogColorTransition( local, flPrimaryColor, flSecondaryColor ); if( local->m_fog.blend ) { // // Blend between two fog colors based on viewing angle. // The secondary fog color is at 180 degrees to the primary fog color. // Vector forward; AngleVectors(pbp->GetAbsAngles(), &forward); Vector vNormalized = local->m_fog.dirPrimary; VectorNormalize( vNormalized ); local->m_fog.dirPrimary = vNormalized; float flBlendFactor = 0.5 * forward.Dot( local->m_fog.dirPrimary ) + 0.5; // FIXME: convert to linear colorspace pColor[0] = flPrimaryColor[0] * flBlendFactor + flSecondaryColor[0] * ( 1 - flBlendFactor ); pColor[1] = flPrimaryColor[1] * flBlendFactor + flSecondaryColor[1] * ( 1 - flBlendFactor ); pColor[2] = flPrimaryColor[2] * flBlendFactor + flSecondaryColor[2] * ( 1 - flBlendFactor ); } else { pColor[0] = flPrimaryColor[0]; pColor[1] = flPrimaryColor[1]; pColor[2] = flPrimaryColor[2]; } } VectorScale( pColor, 1.0f / 255.0f, pColor ); } static float GetFogStart( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return 0.0f; } CPlayerLocalData *local = &pbp->m_Local; if( fog_override.GetInt() ) { if( fog_start.GetFloat() == -1.0f ) { return local->m_fog.start; } else { return fog_start.GetFloat(); } } else { if ( local->m_fog.lerptime > gpGlobals->curtime ) { if ( local->m_fog.start != local->m_fog.startLerpTo ) { if ( local->m_fog.lerptime > gpGlobals->curtime ) { float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration ); return FLerp( local->m_fog.start, local->m_fog.startLerpTo, flPercent ); } else { if ( local->m_fog.start != local->m_fog.startLerpTo ) { local->m_fog.start = local->m_fog.startLerpTo; } } } } return local->m_fog.start; } } static float GetFogEnd( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return 0.0f; } CPlayerLocalData *local = &pbp->m_Local; if( fog_override.GetInt() ) { if( fog_end.GetFloat() == -1.0f ) { return local->m_fog.end; } else { return fog_end.GetFloat(); } } else { if ( local->m_fog.lerptime > gpGlobals->curtime ) { if ( local->m_fog.end != local->m_fog.endLerpTo ) { if ( local->m_fog.lerptime > gpGlobals->curtime ) { float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration ); return FLerp( local->m_fog.end, local->m_fog.endLerpTo, flPercent ); } else { if ( local->m_fog.end != local->m_fog.endLerpTo ) { local->m_fog.end = local->m_fog.endLerpTo; } } } } return local->m_fog.end; } } static bool GetFogEnable( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return false; } CPlayerLocalData *local = &pbp->m_Local; if ( cl_leveloverview.GetFloat() > 0 ) return false; // Ask the clientmode if ( g_pClientMode->ShouldDrawFog() == false ) return false; if( fog_override.GetInt() ) { if( fog_enable.GetInt() ) { return true; } else { return false; } } else { return local->m_fog.enable != false; } } //----------------------------------------------------------------------------- // Purpose: Returns the skybox fog color to use in rendering the current frame. //----------------------------------------------------------------------------- static void GetSkyboxFogColor( float *pColor ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return; } CPlayerLocalData *local = &pbp->m_Local; const char *fogColorString = fog_colorskybox.GetString(); if( fog_override.GetInt() && fogColorString ) { sscanf( fogColorString, "%f%f%f", pColor, pColor+1, pColor+2 ); } else { if( local->m_skybox3d.fog.blend ) { // // Blend between two fog colors based on viewing angle. // The secondary fog color is at 180 degrees to the primary fog color. // Vector forward; AngleVectors( pbp->GetAbsAngles(), &forward ); Vector vNormalized = local->m_fog.dirPrimary; VectorNormalize( vNormalized ); local->m_fog.dirPrimary = vNormalized; float flBlendFactor = 0.5 * forward.Dot( local->m_fog.dirPrimary ) + 0.5; // FIXME: convert to linear colorspace pColor[0] = local->m_skybox3d.fog.colorPrimary.GetR() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetR() * ( 1 - flBlendFactor ); pColor[1] = local->m_skybox3d.fog.colorPrimary.GetG() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetG() * ( 1 - flBlendFactor ); pColor[2] = local->m_skybox3d.fog.colorPrimary.GetB() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetB() * ( 1 - flBlendFactor ); } else { pColor[0] = local->m_skybox3d.fog.colorPrimary.GetR(); pColor[1] = local->m_skybox3d.fog.colorPrimary.GetG(); pColor[2] = local->m_skybox3d.fog.colorPrimary.GetB(); } } VectorScale( pColor, 1.0f / 255.0f, pColor ); } static float GetSkyboxFogStart( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return 0.0f; } CPlayerLocalData *local = &pbp->m_Local; if( fog_override.GetInt() ) { if( fog_startskybox.GetFloat() == -1.0f ) { return local->m_skybox3d.fog.start; } else { return fog_startskybox.GetFloat(); } } else { return local->m_skybox3d.fog.start; } } static float GetSkyboxFogEnd( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return 0.0f; } CPlayerLocalData *local = &pbp->m_Local; if( fog_override.GetInt() ) { if( fog_endskybox.GetFloat() == -1.0f ) { return local->m_skybox3d.fog.end; } else { return fog_endskybox.GetFloat(); } } else { return local->m_skybox3d.fog.end; } } static bool GetSkyboxFogEnable( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return false; } CPlayerLocalData *local = &pbp->m_Local; if( fog_override.GetInt() ) { if( fog_enableskybox.GetInt() ) { return true; } else { return false; } } else { return !!local->m_skybox3d.fog.enable; } } void CViewRender::EnableWorldFog( void ) { VPROF("CViewRender::EnableWorldFog"); if( GetFogEnable() ) { float fogColor[3]; GetFogColor( fogColor ); materials->FogMode( MATERIAL_FOG_LINEAR ); materials->FogColor3fv( fogColor ); materials->FogStart( GetFogStart() ); materials->FogEnd( GetFogEnd() ); } else { materials->FogMode( MATERIAL_FOG_NONE ); } } void CViewRender::Enable3dSkyboxFog( void ) { C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); if( !pbp ) { return; } CPlayerLocalData *local = &pbp->m_Local; if( GetSkyboxFogEnable() ) { float fogColor[3]; GetSkyboxFogColor( fogColor ); float scale = 1.0f; if ( local->m_skybox3d.scale > 0.0f ) { scale = 1.0f / local->m_skybox3d.scale; } materials->FogMode( MATERIAL_FOG_LINEAR ); materials->FogColor3fv( fogColor ); materials->FogStart( GetSkyboxFogStart() * scale ); materials->FogEnd( GetSkyboxFogEnd() * scale ); } else { materials->FogMode( MATERIAL_FOG_NONE ); } } void CViewRender::DisableFog( void ) { VPROF("CViewRander::DisableFog()"); materials->FogMode( MATERIAL_FOG_NONE ); } #include "tier0/memdbgoff.h" void CViewRender::Draw3dSkyboxworld( const CViewSetup &view, int &nClearFlags, bool &bDrew3dSkybox, bool &bSkyboxVisible ) { VPROF_BUDGET( "CViewRender::Draw3dSkyboxworld", "3D Skybox" ); bDrew3dSkybox = false; // The skybox might not be visible from here bSkyboxVisible = engine->IsSkyboxVisibleFromPoint( view.origin ); if ( !bSkyboxVisible && r_3dsky.GetInt() != 2 ) { return; } // render the 3D skybox if ( !r_3dsky.GetInt() ) { return; } C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer(); // No local player object yet... if ( !pbp ) return; CPlayerLocalData* local = &pbp->m_Local; if ( local->m_skybox3d.area == 255 ) return; unsigned char **areabits = render->GetAreaBits(); unsigned char *savebits; unsigned char tmpbits[ 32 ]; savebits = *areabits; memset( tmpbits, 0, sizeof(tmpbits) ); // set the sky area bit tmpbits[local->m_skybox3d.area>>3] |= 1 << (local->m_skybox3d.area&7); *areabits = tmpbits; CViewSetup skyView = view; skyView.zNear = 0.5; skyView.zFar = 18000; // scale origin by sky scale if ( local->m_skybox3d.scale > 0 ) { float scale = 1.0f / local->m_skybox3d.scale; VectorScale( skyView.origin, scale, skyView.origin ); } Enable3dSkyboxFog(); VectorAdd( skyView.origin, local->m_skybox3d.origin, skyView.origin ); skyView.m_vUnreflectedOrigin = skyView.origin; // BUGBUG: Fix this!!! We shouldn't need to call setup vis for the sky if we're connecting // the areas. We'd have to mark all the clusters in the skybox area in the PVS of any // cluster with sky. Then we could just connect the areas to do our vis. //m_bOverrideVisOrigin could hose us here, so call direct render->ViewSetupVis( false, 1, &local->m_skybox3d.origin.Get() ); render->Push3DView( skyView, nClearFlags, false, NULL, m_Frustum ); // At this point, we've cleared everything we need to clear // The next path will need to clear depth, though. nClearFlags &= ~(VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH | VIEW_CLEAR_FULL_TARGET); nClearFlags |= VIEW_CLEAR_DEPTH; // Store off view origin and angles SetupCurrentView( skyView.origin, skyView.angles, VIEW_3DSKY ); // Invoke pre-render methods IGameSystem::PreRenderAllSystems(); ClientWorldListInfo_t info; #ifndef _XBOX CRenderList renderList; CRenderList *pRenderList = &renderList; #else void *ptr = MemAllocScratch(sizeof(CRenderList)); CRenderList *pRenderList = new (ptr) CRenderList; #endif BuildWorldRenderLists( NULL, info, true, true, m_iForceViewLeaf ); BuildRenderableRenderLists( NULL, info, *pRenderList ); int flags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER; if( r_skybox.GetBool() ) { flags |= DF_DRAWSKYBOX; } int oldDrawFlags = m_DrawFlags; m_DrawFlags |= flags; DrawWorld( info, *pRenderList, flags, 0.0f ); // Iterate over all leaves and render objects in those leaves DrawOpaqueRenderables( info, *pRenderList ); // Iterate over all leaves and render objects in those leaves DrawTranslucentRenderables( info, *pRenderList, flags, true ); DisableFog(); CGlowOverlay::UpdateSkyOverlays( skyView.zFar, view.m_bCacheFullSceneState ); PixelVisibility_EndCurrentView(); // restore old area bits *areabits = savebits; // Invoke post-render methods IGameSystem::PostRenderAllSystems(); FinishCurrentView(); m_DrawFlags = oldDrawFlags; #ifdef _XBOX MemFreeScratch(); #endif render->PopView( m_Frustum ); bDrew3dSkybox = true; } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CViewRender::SetupVis( const CViewSetup& view, unsigned int &visFlags ) { VPROF( "CViewRender::SetupVis" ); if ( m_bOverrideVisOrigin ) { // Pass array or vis origins to merge render->ViewSetupVisEx( m_bForceNoVis, m_nNumVisOrigins, m_rgVisOrigins, visFlags ); } else { // Use render origin as vis origin by default render->ViewSetupVisEx( m_bForceNoVis, 1, &view.origin, visFlags ); } } #ifndef _XBOX // hdr parameters ConVar mat_hdr_level( "mat_hdr_level", "2" ); ConVar mat_bloomamount_rate( "mat_bloomamount_rate", "0.05f" ); static ConVar debug_postproc( "mat_debug_postprocessing_effects", "0" ); static ConVar split_postproc( "mat_debug_process_halfscreen", "0" ); static ConVar mat_dynamic_tonemapping( "mat_dynamic_tonemapping", "0" ); static ConVar mat_show_ab_hdr( "mat_show_ab_hdr", "0" ); static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "1" ); ConVar mat_debug_autoexposure("mat_debug_autoexposure","0"); static ConVar mat_autoexposure_max( "mat_autoexposure_max", "2" ); static ConVar mat_autoexposure_min( "mat_autoexposure_min", "0.5" ); static ConVar mat_show_histogram( "mat_show_histogram", "0" ); ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT ); ConVar mat_force_bloom("mat_force_bloom","0"); ConVar mat_disable_bloom("mat_disable_bloom","0"); ConVar mat_debug_bloom("mat_debug_bloom","0"); // fudge factor to make non-hdr bloom more closely match hdr bloom. Because of auto-exposure, high // bloomscales don't blow out as much in hdr. this factor was derived by comparing images in a // reference scene. ConVar mat_non_hdr_bloom_scalefactor("mat_non_hdr_bloom_scalefactor",".3"); enum PostProcessingCondition { PPP_ALWAYS, PPP_IF_COND_VAR, PPP_IF_NOT_COND_VAR }; struct PostProcessingPass { PostProcessingCondition ppp_test; ConVar const *cvar_to_test; char const *material_name; // terminate list with null char const *dest_rendering_target; char const *src_rendering_target; // can be null. needed for source scaling int xdest_scale,ydest_scale; // allows scaling down int xsrc_scale,ysrc_scale; // allows scaling down CMaterialReference m_mat_ref; // so we don't have to keep searching }; #define PPP_PROCESS_PARTIAL_SRC(srcmatname,dest_rt_name,src_tname,scale) \ {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,scale,scale} #define PPP_PROCESS_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,scale) \ {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,scale,scale,1,1} #define PPP_PROCESS_PARTIAL_SRC_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,srcscale,destscale) \ {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,destscale,destscale,srcscale,srcscale} #define PPP_END {PPP_ALWAYS,0,NULL,NULL,0,0,0,0,0} #define PPP_PROCESS(srcmatname,dest_rt_name) {PPP_ALWAYS,0,srcmatname,dest_rt_name,0,1,1,1,1} #define PPP_PROCESS_IF_CVAR(cvarptr,srcmatname,dest_rt_name) \ {PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1} #define PPP_PROCESS_IF_NOT_CVAR(cvarptr,srcmatname,dest_rt_name) \ {PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1} #define PPP_PROCESS_IF_NOT_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_tname,dest_rt_name) \ {PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_tname,1,1,1,1} #define PPP_PROCESS_IF_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_txtrname,dest_rt_name) \ {PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_txtrname,1,1,1,1} #define PPP_PROCESS_SRCTEXTURE(srcmatname,src_tname,dest_rt_name) \ {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,1,1} struct ClipBox { int m_minx, m_miny; int m_maxx, m_maxy; }; static void DrawClippedScreenSpaceRectangle( 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 ClipBox const *clipbox ) { if (clipbox) { if ((destx>clipbox->m_maxx) || (desty>clipbox->m_maxy)) return; if ((destx+width-1m_minx) || (desty+height-1m_miny)) return; // left clip if (destxm_minx) { src_texture_x0=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,clipbox->m_minx); width-=(clipbox->m_minx-destx); destx=clipbox->m_minx; } // top clip if (destym_miny) { src_texture_y0=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,clipbox->m_miny); height-=(clipbox->m_miny-desty); desty=clipbox->m_miny; } // right clip if (destx+width-1>clipbox->m_maxx) { src_texture_x1=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,clipbox->m_maxx); width=clipbox->m_maxx-destx; } // bottom clip if (desty+height-1>clipbox->m_maxy) { src_texture_y1=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,clipbox->m_maxy); height=clipbox->m_maxy-desty; } } materials->DrawScreenSpaceRectangle(pMaterial,destx,desty,width,height,src_texture_x0, src_texture_y0,src_texture_x1,src_texture_y1, src_texture_width,src_texture_height); } void ApplyPostProcessingPasses(PostProcessingPass *pass_list, // table of effects to apply ClipBox const *clipbox=0, // clipping box for these effects ClipBox *dest_coords_out=0) // receives dest coords of last blit { ITexture *pSaveRenderTarget = materials->GetRenderTarget(); int pcount=0; if ( debug_postproc.GetInt() ) { materials->SetRenderTarget(NULL); int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); materials->Viewport( 0, 0, dest_width, dest_height ); materials->ClearColor3ub(255,0,0); // materials->ClearBuffers(true,true); } while(pass_list->material_name) { bool do_it=true; switch(pass_list->ppp_test) { case PPP_IF_COND_VAR: do_it=(pass_list->cvar_to_test)->GetBool(); break; case PPP_IF_NOT_COND_VAR: do_it=! ((pass_list->cvar_to_test)->GetBool()); break; } if ((pass_list->dest_rendering_target==0) && (debug_postproc.GetInt())) do_it=0; if (do_it) { ClipBox const *cb=0; if (pass_list->dest_rendering_target==0) { cb=clipbox; } IMaterial *src_mat=pass_list->m_mat_ref; if (! src_mat) { src_mat=materials->FindMaterial(pass_list->material_name, TEXTURE_GROUP_OTHER,true); if (src_mat) { pass_list->m_mat_ref.Init(src_mat); } } if (pass_list->dest_rendering_target) { ITexture *dest_rt=materials->FindTexture(pass_list->dest_rendering_target, TEXTURE_GROUP_RENDER_TARGET); materials->SetRenderTarget( dest_rt); } else { materials->SetRenderTarget( NULL ); } int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); materials->Viewport( 0, 0, dest_width, dest_height ); dest_width/=pass_list->xdest_scale; dest_height/=pass_list->ydest_scale; if (pass_list->src_rendering_target) { ITexture *src_rt=materials->FindTexture(pass_list->src_rendering_target, TEXTURE_GROUP_RENDER_TARGET); int src_width=src_rt->GetActualWidth(); int src_height=src_rt->GetActualHeight(); int ssrc_width=(src_width-1)/pass_list->xsrc_scale; int ssrc_height=(src_height-1)/pass_list->ysrc_scale; DrawClippedScreenSpaceRectangle( src_mat,0,0,dest_width,dest_height, 0,0,ssrc_width,ssrc_height,src_width,src_height,cb); if ((pass_list->dest_rendering_target) && debug_postproc.GetInt()) { materials->SetRenderTarget(NULL); int row=pcount/2; int col=pcount %2; int vdest_width,vdest_height; materials->GetRenderTargetDimensions( vdest_width, vdest_height ); materials->Viewport( 0, 0, vdest_width, vdest_height ); materials->DrawScreenSpaceRectangle( src_mat,col*400,200+row*300,dest_width,dest_height, 0,0,ssrc_width,ssrc_height,src_width,src_height); } } else { // just draw the whole source if ((pass_list->dest_rendering_target==0) && split_postproc.GetInt()) { DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width/2,dest_height, 0,0,.5,1,1,1,cb); } else { DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width,dest_height, 0,0,1,1,1,1,cb); } if ((pass_list->dest_rendering_target) && debug_postproc.GetInt()) { materials->SetRenderTarget(NULL); int row=pcount/4; int col=pcount %4; int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); materials->Viewport( 0, 0, dest_width, dest_height ); DrawClippedScreenSpaceRectangle(src_mat,10+col*220,10+row*220, 200,200, 0,0,1,1,1,1,cb); } } if (dest_coords_out) { dest_coords_out->m_minx=0; dest_coords_out->m_maxx=dest_width-1; dest_coords_out->m_miny=0; dest_coords_out->m_maxy=dest_height-1; } } pass_list++; pcount++; } materials->SetRenderTarget(pSaveRenderTarget); } PostProcessingPass HDRFinal_Float[] = { PPP_PROCESS_SRCTEXTURE("dev/downsample","_rt_FullFrameFB", "_rt_SmallFB0"), PPP_PROCESS("dev/blurfilterx","_rt_SmallFB1"), PPP_PROCESS("dev/blurfiltery","_rt_SmallFB0"), PPP_PROCESS("dev/floattoscreen_combine",NULL), PPP_END }; PostProcessingPass HDRFinal_Float_NoBloom[] = { PPP_PROCESS_SRCTEXTURE("dev/copyfullframefb", "_rt_FullFrameFB",NULL), PPP_END }; PostProcessingPass HDRSimulate_NonHDR[]={ PPP_PROCESS("dev/copyfullframefb_vanilla",NULL), PPP_END }; static bool IsSplitScreenHDR(void) { return ( mat_show_ab_hdr.GetInt() && (g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE) ); } static void SetRenderTargetAndViewPort(ITexture *rt) { materials->SetRenderTarget(rt); materials->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight()); } #define FILTER_KERNEL_SLOP 20 // Note carefully about the downsampling: the first downsampling samples from the full rendertarget // down to a temp. When doing this sampling, the texture source clamping will take care of the out // of bounds sampling done because of the filter kernels's width. However, on any of the subsequent // sampling operations, we will be sampling from a partially filled render target. So, texture // coordinate clamping cannot help us here. So, we need to always render a few more pixels to the // destination than we actually intend to, so as to replicate the border pixels so that garbage // pixels do not get sucked into the sampling. To deal with this, we always add FILTER_KERNEL_SLOP // to our widths/heights if there is room for them in the destination. static void DrawScreenSpaceRectangleWithSlop( ITexture *dest_rt, 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 ) { // add slop int slopwidth=width+FILTER_KERNEL_SLOP; //min(dest_rt->GetActualWidth()-destx,width+FILTER_KERNEL_SLOP); int slopheight=height+FILTER_KERNEL_SLOP; //min(dest_rt->GetActualHeight()-desty,height+FILTER_KERNEL_SLOP); // adjust coordinates for slop src_texture_x1=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,destx+slopwidth-1); src_texture_y1=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,desty+slopheight-1); width=slopwidth; height=slopheight; materials->DrawScreenSpaceRectangle(pMaterial,destx,desty,width,height, src_texture_x0,src_texture_y0, src_texture_x1,src_texture_y1, src_texture_width,src_texture_height); } enum Histogram_entry_state_t { HESTATE_INITIAL=0, HESTATE_FIRST_QUERY_IN_FLIGHT, HESTATE_QUERY_IN_FLIGHT, HESTATE_QUERY_DONE, }; ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.75", FCVAR_CHEAT ); ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.80", FCVAR_CHEAT ); ConVar mat_exposure_center_region_x_flashlight( "mat_exposure_center_region_x_flashlight","0.33", FCVAR_CHEAT ); ConVar mat_exposure_center_region_y_flashlight( "mat_exposure_center_region_y_flashlight","0.33", FCVAR_CHEAT ); #define N_LUMINANCE_RANGES 31 #define MAX_QUERIES_PER_FRAME 1 class CHistogram_entry_t { public: Histogram_entry_state_t m_state; OcclusionQueryObjectHandle_t m_occ_handle; // the occlusion query handle int m_frame_queued; // when this query was last queued int m_npixels; // # of pixels this histogram represents int m_npixels_in_range; float m_min_lum, m_max_lum; // the luminance range this entry was queried with float m_minx,m_miny,m_maxx,m_maxy; // range is 0..1 in fractions of the screen bool ContainsValidData( void ) { return ( m_state==HESTATE_QUERY_DONE ) || ( m_state==HESTATE_QUERY_IN_FLIGHT ); } void IssueQuery( int frm_num ) { if ( ! m_occ_handle) m_occ_handle=materials->CreateOcclusionQueryObject(); int xl,yl,dest_width,dest_height; materials->GetViewport( xl,yl,dest_width,dest_height); // first, set stencil bits where the colors match IMaterial *test_mat=materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER,true); IMaterialVar *pMinVar = test_mat->FindVar( "$C0_X", NULL ); pMinVar->SetFloatValue( m_min_lum); IMaterialVar *pMaxVar = test_mat->FindVar( "$C0_Y", NULL ); if (m_max_lum==1.0) pMaxVar->SetFloatValue( 10000.0); // count all pixels >1.0 as 1.0 else pMaxVar->SetFloatValue( m_max_lum); int scrx_min=FLerp(xl,xl+dest_width-1,0,1,m_minx); int scrx_max=FLerp(xl,xl+dest_width-1,0,1,m_maxx); int scry_min=FLerp(yl,yl+dest_height-1,0,1,m_miny); int scry_max=FLerp(yl,yl+dest_height-1,0,1,m_maxy); float exposure_width_scale, exposure_height_scale; // now, shrink region of interest if the flashlight is on C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer(); if ( pLocal->IsEffectActive( EF_DIMLIGHT ) ) // check flashlight { exposure_width_scale= (0.5*(1.0-mat_exposure_center_region_x_flashlight.GetFloat())); exposure_height_scale= (0.5*(1.0-mat_exposure_center_region_y_flashlight.GetFloat())); } else { exposure_width_scale= (0.5*(1.0-mat_exposure_center_region_x.GetFloat())); exposure_height_scale= (0.5*(1.0-mat_exposure_center_region_y.GetFloat())); } int skip_edgex=(1+scrx_max-scrx_min)*exposure_width_scale; int skip_edgey=(1+scry_max-scry_min)*exposure_height_scale; // now, do luminance compare float tscale=1.0; if (g_pMaterialSystemHardwareConfig->GetHDRType()==HDR_TYPE_FLOAT) { tscale=materials->GetToneMappingScaleLinear().x; } IMaterialVar *use_t_scale = test_mat->FindVar( "$C0_Z", NULL ); use_t_scale->SetFloatValue( tscale); m_npixels=(1+scrx_max-scrx_min)*(1+scry_max-scry_min); if (mat_tonemapping_occlusion_use_stencil.GetInt()) { materials->SetStencilWriteMask(1); materials->ClearStencilBufferRectangle(scrx_min,scry_min,scrx_max,scry_max,0); materials->SetStencilEnable(true); materials->SetStencilPassOperation(STENCILOPERATION_REPLACE); materials->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_ALWAYS); materials->SetStencilFailOperation(STENCILOPERATION_KEEP); materials->SetStencilZFailOperation(STENCILOPERATION_KEEP); materials->SetStencilReferenceValue(1); } else materials->BeginOcclusionQueryDrawing(m_occ_handle); scrx_min+=skip_edgex; scry_min+=skip_edgey; scrx_max-=skip_edgex; scry_max-=skip_edgey; materials->DrawScreenSpaceRectangle(test_mat, scrx_min,scry_min, 1+scrx_max-scrx_min, 1+scry_max-scry_min, scrx_min,scry_min, scrx_max, scry_max, dest_width,dest_height); if (mat_tonemapping_occlusion_use_stencil.GetInt()) { // now, start counting how many pixels had their stencil bit set via an occlusion query materials->BeginOcclusionQueryDrawing(m_occ_handle); // now, issue an occlusion query using stencil as the mask materials->SetStencilEnable(true); materials->SetStencilTestMask(1); materials->SetStencilPassOperation(STENCILOPERATION_KEEP); materials->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL); materials->SetStencilFailOperation(STENCILOPERATION_KEEP); materials->SetStencilZFailOperation(STENCILOPERATION_KEEP); materials->SetStencilReferenceValue(1); IMaterial *stest_mat=materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER,true); materials->DrawScreenSpaceRectangle(stest_mat, scrx_min,scry_min, 1+scrx_max-scrx_min, 1+scry_max-scry_min, scrx_min,scry_min, scrx_max, scry_max, dest_width,dest_height); materials->SetStencilEnable(false); } materials->EndOcclusionQueryDrawing(m_occ_handle); if (m_state==HESTATE_INITIAL) m_state=HESTATE_FIRST_QUERY_IN_FLIGHT; else m_state=HESTATE_QUERY_IN_FLIGHT; m_frame_queued=frm_num; } }; #define HISTOGRAM_BAR_SIZE 200 class CLuminanceHistogramSystem { CHistogram_entry_t CurHistogram[N_LUMINANCE_RANGES]; int cur_query_frame; public: float GetAverageLuminance( void ) { float total=0; int total_pixels=0; float scale_value=1.0; if (CurHistogram[N_LUMINANCE_RANGES-1].ContainsValidData()) { scale_value=CurHistogram[N_LUMINANCE_RANGES-1].m_npixels* (1.0/CurHistogram[N_LUMINANCE_RANGES-1].m_npixels_in_range); if (mat_debug_autoexposure.GetInt()) Warning("scale value=%f\n",scale_value); } else return 0.5; for(int i=0;i 0) return total*(1.0/total_pixels); else return 0.5; } void Update(void) { // find which histogram entries should have something done this frame int n_queries_issued_this_frame=0; cur_query_frame++; for(int i=0;iCurHistogram[i].m_frame_queued+2 ) { int np= materials->OcclusionQuery_GetNumPixelsRendered( CurHistogram[i].m_occ_handle); if (np!=-1) // -1=query not finished. wait until // next time { CurHistogram[i].m_npixels_in_range=np; // if (mat_debug_autoexposure.GetInt()) // Warning("min=%f max=%f np = %d\n",CurHistogram[i].m_min_lum,CurHistogram[i].m_max_lum,np); CurHistogram[i].m_state=HESTATE_QUERY_DONE; } } break; } } // now, issue queries for the oldest finished queries we have while( n_queries_issued_this_frameViewport(xp,HISTOGRAM_BAR_SIZE-height,width,height); materials->ClearColor3ub(255,0,0); materials->ClearBuffers(true,true); } xp+=width+2; } } CLuminanceHistogramSystem(void) { cur_query_frame=0; for(int bucket=0;bucketResetToneMappingScale(value); } static void SetToneMapScale(float newvalue, float minvalue, float maxvalue) { mat_hdr_tonemapscale.SetValue(newvalue); #if 1 if (s_nInAverage= 1); if ( !engine->MapHasHDRLighting() ) bloom_enabled = false; if ( mat_force_bloom.GetInt() ) bloom_enabled = true; if ( mat_disable_bloom.GetInt() ) bloom_enabled = false; if ( building_cubemaps.GetBool() ) bloom_enabled = false; HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType(); float bloom_coeff=0.0; if (bloom_enabled) { static float currentBloomAmount = 1.0f; float rate = mat_bloomamount_rate.GetFloat(); // Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings. float flCurrentBloomScale = 1.0f; if ( g_bUseCustomBloomScale ) { flCurrentBloomScale = g_flCustomBloomScale; } else { flCurrentBloomScale = mat_bloomscale.GetFloat(); } currentBloomAmount = flCurrentBloomScale * rate + ( 1.0f - rate ) * currentBloomAmount; bloom_coeff=currentBloomAmount; } if ( hdrType == HDR_TYPE_NONE ) bloom_coeff *= mat_non_hdr_bloom_scalefactor.GetFloat(); // Use the appropriate autoexposure min / max settings. // Mapmaker's overrides the convar settings. float flAutoExposureMin; float flAutoExposureMax; if ( g_bUseCustomAutoExposureMin ) { flAutoExposureMin = g_flCustomAutoExposureMin; } else { flAutoExposureMin = mat_autoexposure_min.GetFloat(); } if ( g_bUseCustomAutoExposureMax ) { flAutoExposureMax = g_flCustomAutoExposureMax; } else { flAutoExposureMax = mat_autoexposure_max.GetFloat(); } switch( hdrType ) { case HDR_TYPE_NONE: case HDR_TYPE_INTEGER: { if (mat_debug_bloom.GetInt()==1) { static int wx=0; wx=(wx+1) & 63; materials->SetRenderTarget(NULL); int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); materials->Viewport( 0, 0, dest_width, dest_height ); materials->ClearColor3ub(0,0,0); materials->ClearBuffers(true,true); materials->Viewport( wx+100, 100, 20,20 ); materials->ClearColor3ub(255,255,255); materials->ClearBuffers(true,true); materials->Viewport( 20,20,20,20 ); materials->ClearBuffers(true,true); materials->Viewport( 950,20,20,20 ); materials->ClearBuffers(true,true); materials->Viewport( 950,950,20,20 ); materials->ClearBuffers(true,true); materials->Viewport( 20,950,20,20 ); materials->ClearBuffers(true,true); materials->Viewport( 0, 0, dest_width, dest_height ); } bool did_update=false; if ( ( hdrType != HDR_TYPE_NONE && mat_dynamic_tonemapping.GetInt() ) || mat_show_histogram.GetInt()) { did_update=true; // if (mat_debug_autoexposure.GetInt()) // { // materials->ClearColor3ub(128,0,0); // materials->ClearBuffers(true,true); // } Rect_t actualRect; UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect ); // UpdateScreenEffectTexture( 0, x, y, w, h, true ); int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); g_HDR_HistogramSystem.Update(); float avg_lum=g_HDR_HistogramSystem.GetAverageLuminance(); float last_scale=materials->GetToneMappingScaleLinear().x; float actual_unscaled_luminance=avg_lum*(1.0/last_scale); actual_unscaled_luminance=max(0.000000001,avg_lum); float target_scale=0.005/actual_unscaled_luminance; float scalevalue=max(flAutoExposureMin, min(flAutoExposureMax,target_scale)); if (mat_debug_autoexposure.GetInt()) Warning("avg_lum=%f ra=%f target=%f adj target=%f\n", avg_lum,actual_unscaled_luminance,target_scale,scalevalue); if (mat_dynamic_tonemapping.GetInt()) SetToneMapScale(scalevalue,flAutoExposureMin,flAutoExposureMax); } // bloom if ( bloom_enabled && ( bloom_coeff > 0.0 ) && ( engine->GetDXSupportLevel() >= 80 ) ) { materials->SetRenderTarget(NULL); int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); materials->Viewport( 0, 0, dest_width, dest_height ); if ( !did_update ) { UpdateScreenEffectTexture( 0, x, y, w, h, true ); did_update=true; } IMaterial *downsample_mat = materials->FindMaterial( "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true); IMaterial *xblur_mat = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true ); IMaterial *yblur_mat = materials->FindMaterial( "dev/blurfiltery_and_add_nohdr", TEXTURE_GROUP_OTHER, true ); ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET ); ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); // downsample fb to rt0 SetRenderTargetAndViewPort( dest_rt0 ); // note the -2's below. Thats because we are downsampling on each axis and the shader // accesses pixels on both sides of the source coord materials->DrawScreenSpaceRectangle( downsample_mat, 0, 0, dest_width/4, dest_height/4, 0, 0, dest_width-2, dest_height-2, dest_width, dest_height ); // guassian blur x rt0 to rt1 SetRenderTargetAndViewPort( dest_rt1 ); materials->DrawScreenSpaceRectangle( xblur_mat, 0, 0, dest_width/4, dest_height/4, 0, 0, dest_width/4-1, dest_height/4-1, dest_width/4, dest_height/4 ); // gaussian blur y and add rt1 to frame buffer materials->Viewport( 0, 0, dest_width, dest_height ); materials->SetRenderTarget( NULL ); IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL ); pBloomAmountVar->SetFloatValue( bloom_coeff ); int bloomadd_x=0; int bloomadd_y=0; int bloomadd_width=dest_width; int bloomadd_height=dest_height; float bloomsrc_x=0; float bloomsrc_y=-.5; // compensate for phase shift due to mismatched input/output sizes float bloomsrc_maxx=dest_width/4-1; float bloomsrc_maxy=dest_height/4-1; if ( IsSplitScreenHDR() ) { bloomadd_x+=dest_width/2; bloomadd_width-=dest_width/2; bloomsrc_x+=dest_width/8; } materials->DrawScreenSpaceRectangle(yblur_mat,bloomadd_x,bloomadd_y, bloomadd_width,bloomadd_height, bloomsrc_x,bloomsrc_y, bloomsrc_maxx,bloomsrc_maxy, dest_rt1->GetActualWidth(), dest_rt1->GetActualHeight()); } if ( mat_show_histogram.GetInt() && ( engine->GetDXSupportLevel() >= 90 ) ) { g_HDR_HistogramSystem.DisplayHistogram(); } } break; case HDR_TYPE_FLOAT: { int dest_width,dest_height; materials->GetRenderTargetDimensions( dest_width, dest_height ); if (mat_dynamic_tonemapping.GetInt() || mat_show_histogram.GetInt()) { g_HDR_HistogramSystem.Update(); // Warning("avg_lum=%f\n",g_HDR_HistogramSystem.GetAverageLuminance()); if (mat_dynamic_tonemapping.GetInt()) { float avg_lum=max(0.0001,g_HDR_HistogramSystem.GetAverageLuminance()); float scalevalue=max(flAutoExposureMin, min(flAutoExposureMax,0.18/avg_lum)); mat_hdr_tonemapscale.SetValue(scalevalue); } } IMaterial *pBloomMaterial; pBloomMaterial = materials->FindMaterial( "dev/floattoscreen_combine", "" ); IMaterialVar *pBloomAmountVar = pBloomMaterial->FindVar( "$bloomamount", NULL ); pBloomAmountVar->SetFloatValue( bloom_coeff); if ( IsSplitScreenHDR() ) { // do split screen ClipBox mycb; mycb.m_minx=mycb.m_miny=0; mycb.m_maxx=dest_width/2; mycb.m_maxy=dest_height-1; ApplyPostProcessingPasses(HDRSimulate_NonHDR,&mycb); mycb.m_minx=mycb.m_maxx; mycb.m_maxx=dest_width-1; ApplyPostProcessingPasses(HDRFinal_Float,&mycb); } else { if( bloom_enabled) ApplyPostProcessingPasses(HDRFinal_Float); else ApplyPostProcessingPasses(HDRFinal_Float_NoBloom); } materials->SetRenderTarget(NULL); if ( mat_show_histogram.GetInt() && (engine->GetDXSupportLevel()>=90)) g_HDR_HistogramSystem.DisplayHistogram(); if (mat_dynamic_tonemapping.GetInt()) { float avg_lum=max(0.0001,g_HDR_HistogramSystem.GetAverageLuminance()); float scalevalue=max(flAutoExposureMin, min(flAutoExposureMax,0.023/avg_lum)); SetToneMapScale(scalevalue,flAutoExposureMin,flAutoExposureMax); } materials->SetRenderTarget( NULL ); break; } } } #else void ResetToneMapping(float value) { } #endif // !_XBOX #ifdef _XBOX #define FULLSCREEN_WIDTH 640 #define FULLSCREEN_HEIGHT 480 #define DOWNSAMPLE_WIDTH (FULLSCREEN_WIDTH>>1) #define DOWNSAMPLE_HEIGHT (FULLSCREEN_HEIGHT>>1) void CViewRender::DoScreenSpaceBloom() { int rt_width, rt_height; if ( !mat_bloomscale.GetFloat() || !m_BloomDownsample.IsValid() ) { return; } // rt_waterReflection = DownSample( backBuffer ) ITexture *dest_rt = GetWaterReflectionTexture(); materials->PushRenderTargetAndViewport( dest_rt ); materials->GetRenderTargetDimensions( rt_width, rt_height ); Assert( rt_width >= DOWNSAMPLE_WIDTH && rt_height >= DOWNSAMPLE_HEIGHT ); materials->DrawScreenSpaceRectangle( m_BloomDownsample, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, 0, 0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT, 1, 1 ); // rt_waterRefraction = BlurX( rt_waterReflection ) dest_rt = GetWaterRefractionTexture(); materials->SetRenderTarget( dest_rt ); materials->DrawScreenSpaceRectangle( m_BloomBlurX, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height ); // rt_waterReflection = BlurY( rt_waterRefraction ) dest_rt = GetWaterReflectionTexture(); materials->SetRenderTarget( dest_rt ); materials->DrawScreenSpaceRectangle( m_BloomBlurY, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height ); // restore screen materials->PopRenderTargetAndViewport(); // FrameBuffer += rt_waterReflection materials->DrawScreenSpaceRectangle( m_BloomAdd, 0, 0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height ); } #endif //----------------------------------------------------------------------------- // Purpose: Renders voice feedback and other sprites attached to players // Input : none //----------------------------------------------------------------------------- void CViewRender::RenderPlayerSprites() { #ifndef _XBOX GetClientVoiceMgr()->DrawHeadLabels(); #endif } //----------------------------------------------------------------------------- // Sets up, cleans up the main 3D view //----------------------------------------------------------------------------- void CViewRender::SetupMain3DView( const CViewSetup &view, int &nClearFlags ) { // FIXME: I really want these fields removed from CViewSetup // and passed in as independent flags // Clear the color here if requested. nClearFlags &= ~VIEW_CLEAR_DEPTH; if ( nClearFlags & VIEW_CLEAR_COLOR ) { nClearFlags |= VIEW_CLEAR_DEPTH; } // If we are using HDR, we render to the HDR full frame buffer texture // instead of whatever was previously the render target if( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT ) { render->Push3DView( view, nClearFlags, true, GetFullFrameFrameBufferTexture( 0 ), m_Frustum ); } else { render->Push3DView( view, nClearFlags, false, NULL, m_Frustum ); } // If we didn't clear the depth here, we'll need to clear it later nClearFlags ^= VIEW_CLEAR_DEPTH; if ( nClearFlags & VIEW_CLEAR_COLOR ) { // If we cleared the color here, we don't need to clear it later nClearFlags &= ~( VIEW_CLEAR_COLOR | VIEW_CLEAR_FULL_TARGET ); } } void CViewRender::CleanupMain3DView( const CViewSetup &view ) { render->PopView( m_Frustum ); } //----------------------------------------------------------------------------- // Queues up an overlay rendering //----------------------------------------------------------------------------- void CViewRender::QueueOverlayRenderView( const CViewSetup &view, int nClearFlags, int whatToDraw ) { #ifndef _XBOX // Can't have 2 in a single scene Assert( !m_bDrawOverlay ); m_bDrawOverlay = true; m_OverlayViewSetup = view; m_OverlayClearFlags = nClearFlags; m_OverlayDrawFlags = whatToDraw; #endif } //----------------------------------------------------------------------------- // Purpose: This renders the entire 3D view and the in-game hud/viewmodel // Input : &view - // whatToDraw - //----------------------------------------------------------------------------- // This renders the entire 3D view. void CViewRender::RenderViewEx( const CViewSetup &view, int nClearFlags, int whatToDraw ) { m_CurrentView = view; VPROF( "CViewRender::RenderViewEx" ); ITexture *saveRenderTarget = materials->GetRenderTarget(); g_pClientShadowMgr->AdvanceFrame(); #ifdef USE_MONITORS if ( cl_drawmonitors.GetBool() && g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 ) { DrawMonitors( view ); } #endif g_bRenderingView = true; // Must be first render->SceneBegin(); materials->TurnOnToneMapping(); // clear happens here probably SetupMain3DView( view, nClearFlags ); bool bDrew3dSkybox = false; bool bSkyboxVisible = false; // if the 3d skybox world is drawn, then don't draw the normal skybox Draw3dSkyboxworld( view, nClearFlags, bDrew3dSkybox, bSkyboxVisible ); // Force it to clear the framebuffer if they're in solid space. if ( ( nClearFlags & VIEW_CLEAR_COLOR ) == 0 ) { if ( enginetrace->GetPointContents( view.origin ) == CONTENTS_SOLID ) { nClearFlags |= VIEW_CLEAR_COLOR; } } // Render world and all entities, particles, etc. if( !g_pIntroData ) { ViewDrawScene( bDrew3dSkybox, bSkyboxVisible, view, nClearFlags, VIEW_MAIN, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); } else { ViewDrawScene_Intro( view, nClearFlags, *g_pIntroData ); } // We can still use the 'current view' stuff set up in ViewDrawScene s_bCanAccessCurrentView = true; if ( !IsXbox() ) { engine->DrawPortals(); } DisableFog(); // Finish scene render->SceneEnd(); // Draw lightsources if enabled render->DrawLights(); RenderPlayerSprites(); // Now actually draw the viewmodel DrawViewModels( view, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); PixelVisibility_EndScene(); // Draw fade over entire screen if needed byte color[4]; bool blend; vieweffects->GetFadeParams( view.context, &color[0], &color[1], &color[2], &color[3], &blend ); // Draw an overlay to make it even harder to see inside smoke particle systems. DrawSmokeFogOverlay(); // Overlay screen fade on entire screen IMaterial* pMaterial = blend ? m_ModulateSingleColor : m_TranslucentSingleColor; render->ViewDrawFade( color, pMaterial ); PerformScreenOverlay( view.x, view.y, view.width, view.height ); // Prevent sound stutter if going slow engine->Sound_ExtraUpdate(); #ifndef _XBOX if ( !building_cubemaps.GetBool() && view.m_bDoBloomAndToneMapping ) { DoPostProcessingEffects( view.x, view.y, view.width, view.height ); } #else DoScreenSpaceBloom(); #endif // And here are the screen-space effects // Grab the pre-color corrected frame for editing purposes engine->GrabPreColorCorrectedFrame( view.x, view.y, view.width, view.height ); PerformScreenSpaceEffects( view.x, view.y, view.width, view.height ); if ( !IsXbox() && g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) { materials->SetToneMappingScaleLinear(Vector(1,1,1)); } CleanupMain3DView( view ); materials->SetRenderTarget( saveRenderTarget ); #ifndef _XBOX // Draw the overlay if ( m_bDrawOverlay ) { // This allows us to be ok if there are nested overlay views CViewSetup currentView = m_CurrentView; CViewSetup tempView = m_OverlayViewSetup; tempView.fov = ScaleFOVByWidthRatio( tempView.fov, tempView.m_flAspectRatio / ( 4.0f / 3.0f ) ); tempView.m_bDoBloomAndToneMapping = false; // FIXME: Hack to get Mark up and running m_bDrawOverlay = false; RenderViewEx( tempView, m_OverlayClearFlags, m_OverlayDrawFlags ); m_CurrentView = currentView; } #endif // Draw the 2D graphics render->Push2DView( view, 0, false, NULL, m_Frustum ); Render2DEffectsPreHUD( view ); if ( whatToDraw & RENDERVIEW_DRAWHUD ) { VPROF_BUDGET( "VGui_DrawHud", VPROF_BUDGETGROUP_OTHER_VGUI ); // paint the vgui screen VGui_PreRender(); // Make sure the client .dll root panel is at the proper point before doing the "SolveTraverse" calls vgui::VPANEL root = enginevgui->GetPanel( PANEL_CLIENTDLL ); if ( root != 0 ) { vgui::ipanel()->SetPos( root, view.x, view.y ); vgui::ipanel()->SetSize( root, view.width, view.height ); } // Same for client .dll tools root = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS ); if ( root != 0 ) { vgui::ipanel()->SetPos( root, view.x, view.y ); vgui::ipanel()->SetSize( root, view.width, view.height ); } // The crosshair, etc. needs to get at the current setup stuff AllowCurrentViewAccess( true ); // Draw the in-game stuff based on the actual viewport being used render->VGui_Paint( PAINT_INGAMEPANELS ); AllowCurrentViewAccess( false ); VGui_PostRender(); g_pClientMode->PostRenderVGui(); materials->Flush(); } Draw2DDebuggingInfo( view ); m_AnglesHistory[m_AnglesHistoryCounter] = view.angles; m_AnglesHistoryCounter = (m_AnglesHistoryCounter+1) & ANGLESHISTORY_MASK; // If the angles are moving fast enough, allow LOD transitions. float angleMovementDelta = 0; for(int i=0; i < ANGLESHISTORY_SIZE; i++) { angleMovementDelta += (m_AnglesHistory[(i+1) & ANGLESHISTORY_MASK] - m_AnglesHistory[i]).Length(); } angleMovementDelta /= ANGLESHISTORY_SIZE; if(angleMovementDelta > r_TransitionSensitivity.GetFloat()) { r_DoCovertTransitions.SetValue(1); } else { r_DoCovertTransitions.SetValue(0); } Render2DEffectsPostHUD( view ); g_bRenderingView = false; // We can no longer use the 'current view' stuff set up in ViewDrawScene s_bCanAccessCurrentView = false; // Next frame! ++m_FrameNumber; if ( !IsXbox() ) { GenerateOverdrawForTesting(); } render->PopView( m_Frustum ); } //----------------------------------------------------------------------------- // Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack //----------------------------------------------------------------------------- void CViewRender::Render2DEffectsPreHUD( const CViewSetup &view ) { } //----------------------------------------------------------------------------- // Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack //----------------------------------------------------------------------------- void CViewRender::Render2DEffectsPostHUD( const CViewSetup &view ) { } //----------------------------------------------------------------------------- // Purpose: Renders entire view // Input : &view - // drawViewModel - //----------------------------------------------------------------------------- void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, bool bDrawViewModel ) { int flags = RENDERVIEW_DRAWHUD; if ( bDrawViewModel ) { flags |= RENDERVIEW_DRAWVIEWMODEL; } RenderViewEx( view, nClearFlags, flags ); } int CViewRender::GetDrawFlags() { return m_DrawFlags; } void ViewTransform( const Vector &worldSpace, Vector &viewSpace ) { const VMatrix &viewMatrix = engine->WorldToViewMatrix(); Vector3DMultiplyPosition( viewMatrix, worldSpace, viewSpace ); } //----------------------------------------------------------------------------- // Purpose: UNDONE: Clean this up some, handle off-screen vertices // Input : *point - // *screen - // Output : int //----------------------------------------------------------------------------- int ScreenTransform( const Vector& point, Vector& screen ) { // UNDONE: Clean this up some, handle off-screen vertices float w; const VMatrix &worldToScreen = engine->WorldToScreenMatrix(); screen.x = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3]; screen.y = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3]; // z = worldToScreen[2][0] * point[0] + worldToScreen[2][1] * point[1] + worldToScreen[2][2] * point[2] + worldToScreen[2][3]; w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3]; // Just so we have something valid here screen.z = 0.0f; bool behind; if( w < 0.001f ) { behind = true; screen.x *= 100000; screen.y *= 100000; } else { behind = false; float invw = 1.0f / w; screen.x *= invw; screen.y *= invw; } return behind; } //----------------------------------------------------------------------------- // // NOTE: Below here is all of the stuff that needs to be done for water rendering // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Determines what kind of water we're going to use //----------------------------------------------------------------------------- void CViewRender::DetermineWaterRenderInfo( const VisibleFogVolumeInfo_t &fogVolumeInfo, CViewRender::WaterRenderInfo_t &info ) { // By default, assume cheap water (even if there's no water in the scene!) info.m_bCheapWater = true; info.m_bRefract = false; info.m_bReflect = false; info.m_bReflectEntities = false; info.m_bDrawWaterSurface = false; info.m_bOpaqueWater = true; IMaterial *pWaterMaterial = fogVolumeInfo.m_pFogVolumeMaterial; if (( fogVolumeInfo.m_nVisibleFogVolume == -1 ) || !pWaterMaterial ) return; // Use cheap water if mat_drawwater is set info.m_bDrawWaterSurface = mat_drawwater.GetBool(); if ( !info.m_bDrawWaterSurface ) { info.m_bOpaqueWater = false; return; } // Determine if the water surface is opaque or not info.m_bOpaqueWater = !pWaterMaterial->IsTranslucent(); // DX level 70 can't handle anything but cheap water if (engine->GetDXSupportLevel() < 80) return; bool bForceCheap = false; bool bForceExpensive = r_waterforceexpensive.GetBool(); // The material can override the default settings though IMaterialVar *pForceCheapVar = pWaterMaterial->FindVar( "$forcecheap", NULL, false ); IMaterialVar *pForceExpensiveVar = pWaterMaterial->FindVar( "$forceexpensive", NULL, false ); if ( pForceCheapVar && pForceCheapVar->IsDefined() ) { bForceCheap = ( pForceCheapVar->GetIntValue() != 0 ); if ( bForceCheap ) { bForceExpensive = false; } } if ( !bForceCheap && pForceExpensiveVar && pForceExpensiveVar->IsDefined() ) { bForceExpensive = bForceExpensive || ( pForceExpensiveVar->GetIntValue() != 0 ); } bool bDebugCheapWater = r_debugcheapwater.GetBool(); if( bDebugCheapWater ) { Msg( "Water material: %s dist to water: %f\nforcecheap: %s forceexpensive: %s\n", pWaterMaterial->GetName(), fogVolumeInfo.m_flDistanceToWater, bForceCheap ? "true" : "false", bForceExpensive ? "true" : "false" ); } // Unless expensive water is active, reflections are off. bool bLocalReflection; if( !bForceExpensive || !r_WaterDrawReflection.GetBool() ) { bLocalReflection = false; } else { IMaterialVar *pReflectTextureVar = pWaterMaterial->FindVar( "$reflecttexture", NULL, false ); bLocalReflection = pReflectTextureVar && (pReflectTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE); } // FIXME: I disabled cheap water LOD when local specular is specified. // There are very few places that appear to actually // take advantage of it (places where water is in the PVS, but outside of LOD range). // It was 2 hours before code lock, and I had the choice of either doubling fill-rate everywhere // by making cheap water lod actually work (the water LOD wasn't actually rendering!!!) // or to just always render the reflection + refraction if there's a local specular specified. // Note that water LOD *does* work with refract-only water // Check if the water is out of the cheap water LOD range; if so, use cheap water if ( ( (fogVolumeInfo.m_flDistanceToWater >= m_flCheapWaterEndDistance) && !bLocalReflection ) || bForceCheap ) return; // Get the material that is for the water surface that is visible and check to see // what render targets need to be rendered, if any. if ( !r_WaterDrawRefraction.GetBool() ) { info.m_bRefract = false; } else { IMaterialVar *pRefractTextureVar = pWaterMaterial->FindVar( "$refracttexture", NULL, false ); info.m_bRefract = pRefractTextureVar && (pRefractTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE); // Refractive water can be seen through if ( info.m_bRefract ) { info.m_bOpaqueWater = false; } } #ifndef _XBOX info.m_bReflect = bLocalReflection; if ( info.m_bReflect ) { if( r_waterforcereflectentities.GetBool() ) { info.m_bReflectEntities = true; } else { IMaterialVar *pReflectEntitiesVar = pWaterMaterial->FindVar( "$reflectentities", NULL, false ); info.m_bReflectEntities = pReflectEntitiesVar && (pReflectEntitiesVar->GetIntValue() != 0); } } #endif info.m_bCheapWater = !info.m_bReflect && !info.m_bRefract; if( bDebugCheapWater ) { Warning( "refract: %s reflect: %s\n", info.m_bRefract ? "true" : "false", info.m_bReflect ? "true" : "false" ); } } //----------------------------------------------------------------------------- // Draws the world and all entities //----------------------------------------------------------------------------- void CViewRender::WaterDrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &viewIn, int nClearFlags ) { MDLCACHE_CRITICAL_SECTION(); VisibleFogVolumeInfo_t fogVolumeInfo; render->GetVisibleFogVolume( viewIn.origin, &fogVolumeInfo ); WaterRenderInfo_t info; DetermineWaterRenderInfo( fogVolumeInfo, info ); if ( info.m_bCheapWater ) { ViewDrawScene_NoWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info ); return; } // Blat out the visible fog leaf if we're not going to use it if ( !r_ForceWaterLeaf.GetBool() ) { fogVolumeInfo.m_nVisibleFogVolumeLeaf = -1; } // We can see water of some sort if ( !fogVolumeInfo.m_bEyeInFogVolume ) { ViewDrawScene_EyeAboveWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info ); } else { ViewDrawScene_EyeUnderWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info ); } } //----------------------------------------------------------------------------- // Pushes a water render target //----------------------------------------------------------------------------- static Vector SavedLinearLightMapScale(-1,-1,-1); // x<0 = no saved scale static void SetLightmapScaleForWater(void) { if (g_pMaterialSystemHardwareConfig->GetHDRType()==HDR_TYPE_INTEGER) { SavedLinearLightMapScale=materials->GetToneMappingScaleLinear(); Vector t25=SavedLinearLightMapScale; t25*=0.25; materials->SetToneMappingScaleLinear(t25); } } void CViewRender::PushWaterRenderTarget( CViewSetup &view, int nClearFlags, float waterHeight, int flags ) { float spread = 2.0f; float origWaterHeight = waterHeight; if( flags & DF_FUDGE_UP ) { waterHeight += spread; } else { waterHeight -= spread; } MaterialHeightClipMode_t clipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE; if ( ( flags & DF_CLIP_Z ) && mat_clipz.GetBool() ) { if( flags & DF_CLIP_BELOW ) { clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT; } else { clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT; } } if( flags & DF_RENDER_REFRACTION ) { ITexture *pTexture = GetWaterRefractionTexture(); // Use the aspect ratio of the main view! So, don't recompute it here view.x = view.y = 0; view.width = pTexture->GetActualWidth(); view.height = pTexture->GetActualHeight(); materials->SetFogZ( waterHeight ); materials->SetHeightClipZ( waterHeight ); materials->SetHeightClipMode( clipMode ); // Have to re-set up the view since we reset the size render->Push3DView( view, nClearFlags, true, pTexture, m_Frustum ); return; } if( flags & DF_RENDER_REFLECTION ) { ITexture *pTexture = GetWaterReflectionTexture(); materials->SetFogZ( waterHeight ); // Use the aspect ratio of the main view! So, don't recompute it here view.x = view.y = 0; view.width = pTexture->GetActualWidth(); view.height = pTexture->GetActualHeight(); view.angles[0] = -view.angles[0]; view.angles[2] = -view.angles[2]; view.origin[2] -= 2.0f * ( view.origin[2] - (origWaterHeight)); bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping(); if( bSoftwareUserClipPlane && ( view.origin[2] > waterHeight - r_eyewaterepsilon.GetFloat() ) ) { waterHeight = view.origin[2] + r_eyewaterepsilon.GetFloat(); } materials->SetHeightClipZ( waterHeight ); materials->SetHeightClipMode( clipMode ); render->Push3DView( view, nClearFlags, true, pTexture, m_Frustum ); SetLightmapScaleForWater(); return; } if ( nClearFlags & (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR) ) { materials->ClearBuffers( nClearFlags & VIEW_CLEAR_COLOR, nClearFlags & VIEW_CLEAR_DEPTH ); } materials->SetHeightClipMode( clipMode ); if ( clipMode != MATERIAL_HEIGHTCLIPMODE_DISABLE ) { materials->SetHeightClipZ( waterHeight ); } } //----------------------------------------------------------------------------- // Pops a water render target //----------------------------------------------------------------------------- void CViewRender::PopWaterRenderTarget( int nFlags ) { materials->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); if( nFlags & (DF_RENDER_REFRACTION | DF_RENDER_REFLECTION) ) { render->PopView( m_Frustum ); if (SavedLinearLightMapScale.x>=0) { materials->SetToneMappingScaleLinear(SavedLinearLightMapScale); SavedLinearLightMapScale.x=-1; } } } //----------------------------------------------------------------------------- // Draws the world + entities //----------------------------------------------------------------------------- void CViewRender::WaterDrawHelper( const CViewSetup &view, ClientWorldListInfo_t &info, CRenderList &renderList, float waterHeight, int flags, view_id_t viewID, float waterZAdjust, int iForceViewLeaf ) { int savedViewID = g_CurrentViewID; g_CurrentViewID = viewID; // Need a copy of the view since pushing a water render target // make change the aspect ratio of the projection space transform CViewSetup actualView = view; int nClearFlags = 0; if ( flags & DF_CLEARDEPTH ) { nClearFlags |= VIEW_CLEAR_DEPTH; } if ( flags & DF_CLEARCOLOR ) { nClearFlags |= VIEW_CLEAR_COLOR; } PushWaterRenderTarget( actualView, nClearFlags, waterHeight, flags ); if( flags & DF_BUILDWORLDLISTS ) { bool bDrawEntities = ( flags & DF_DRAW_ENTITITES ) != 0; bool bUpdateLightmaps = ( flags & DF_UPDATELIGHTMAPS ) != 0; BuildWorldRenderLists( &actualView, info, bUpdateLightmaps, bDrawEntities, iForceViewLeaf ); } // Make sure sound doesn't stutter engine->Sound_ExtraUpdate(); // Update our render view flags. m_DrawFlags = flags | m_BaseDrawFlags; ITexture *pSaveFrameBufferCopyTexture = materials->GetFrameBufferCopyTexture( 0 ); if ( engine->GetDXSupportLevel() >= 80 ) { materials->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() ); } DrawWorld( info, renderList, m_DrawFlags, waterZAdjust ); // FIXME: This may have to be static, not sure if we'll get a stack overflow ClientWorldListInfo_t tmpInfo; tmpInfo.m_pLeafList = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) ); tmpInfo.m_pLeafFogVolume = (LeafFogVolume_t*)stackalloc( info.m_LeafCount * sizeof(LeafFogVolume_t) ); tmpInfo.m_pActualLeafIndex = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) ); ClientWorldListInfo_t *pInfo = ComputeActualWorldListInfo( info, m_DrawFlags, tmpInfo ); if ( flags & DF_DRAW_ENTITITES ) { BuildRenderableRenderLists( &actualView, *pInfo, renderList ); DrawOpaqueRenderables( *pInfo, renderList ); DrawTranslucentRenderables( *pInfo, renderList, m_DrawFlags, false ); } else { // Draw translucent world brushes only, no entities DrawTranslucentWorldInLeaves( pInfo->m_LeafCount - 1, 0, *pInfo, m_DrawFlags ); } // issue the pixel visibility tests if ( CurrentViewID() != VIEW_MAIN ) { PixelVisibility_EndCurrentView(); } materials->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture ); PopWaterRenderTarget( m_DrawFlags ); m_DrawFlags = m_BaseDrawFlags; g_CurrentViewID = savedViewID; } //----------------------------------------------------------------------------- // Returns true if the view plane intersects the water //----------------------------------------------------------------------------- bool DoesViewPlaneIntersectWater( float waterZ, int leafWaterDataID ) { if ( leafWaterDataID == -1 ) return false; VMatrix viewMatrix, projectionMatrix, viewProjectionMatrix, inverseViewProjectionMatrix; materials->GetMatrix( MATERIAL_VIEW, &viewMatrix ); materials->GetMatrix( MATERIAL_PROJECTION, &projectionMatrix ); MatrixMultiply( projectionMatrix, viewMatrix, viewProjectionMatrix ); MatrixInverseGeneral( viewProjectionMatrix, inverseViewProjectionMatrix ); Vector mins, maxs; ClearBounds( mins, maxs ); Vector testPoint[4]; testPoint[0].Init( -1.0f, -1.0f, 0.0f ); testPoint[1].Init( -1.0f, 1.0f, 0.0f ); testPoint[2].Init( 1.0f, -1.0f, 0.0f ); testPoint[3].Init( 1.0f, 1.0f, 0.0f ); int i; bool bAbove = false; bool bBelow = false; float fudge = 7.0f; for( i = 0; i < 4; i++ ) { Vector worldPos; Vector3DMultiplyPositionProjective( inverseViewProjectionMatrix, testPoint[i], worldPos ); AddPointToBounds( worldPos, mins, maxs ); // Warning( "viewplanez: %f waterZ: %f\n", worldPos.z, waterZ ); if( worldPos.z + fudge > waterZ ) { bAbove = true; } if( worldPos.z - fudge < waterZ ) { bBelow = true; } } // early out if the near plane doesn't cross the z plane of the water. if( !( bAbove && bBelow ) ) return false; Vector vecFudge( fudge, fudge, fudge ); mins -= vecFudge; maxs += vecFudge; // the near plane does cross the z value for the visible water volume. Call into // the engine to find out if the near plane intersects the water volume. return render->DoesBoxIntersectWaterVolume( mins, maxs, leafWaterDataID ); } static void CalcWaterEyeAdjustments( const CViewSetup &view, const VisibleFogVolumeInfo_t &fogInfo, float &newWaterHeight, float &waterZAdjust, bool bSoftwareUserClipPlane ) { if( !bSoftwareUserClipPlane ) { newWaterHeight = fogInfo.m_flWaterHeight; waterZAdjust = 0.0f; return; } newWaterHeight = fogInfo.m_flWaterHeight; float eyeToWaterZDelta = view.origin[2] - fogInfo.m_flWaterHeight; float epsilon = r_eyewaterepsilon.GetFloat(); waterZAdjust = 0.0f; if( fabs( eyeToWaterZDelta ) < epsilon ) { if( eyeToWaterZDelta > 0 ) { newWaterHeight = view.origin[2] - epsilon; } else { newWaterHeight = view.origin[2] + epsilon; } waterZAdjust = newWaterHeight - fogInfo.m_flWaterHeight; } // Warning( "view.origin[2]: %f newWaterHeight: %f fogInfo.m_flWaterHeight: %f waterZAdjust: %f\n", // ( float )view.origin[2], newWaterHeight, fogInfo.m_flWaterHeight, waterZAdjust ); } #ifdef _XBOX //----------------------------------------------------------------------------- // Draws a perspective-correct dudv map into the reflection texture //----------------------------------------------------------------------------- void CViewRender::DrawScreenSpaceWaterDuDv( const CViewSetup &view, float waterZAdjust ) { ITexture *pTexture = GetWaterReflectionTexture(); // Use the aspect ratio of the main view! So, don't recompute it here CViewSetup actualView = view; actualView.width = pTexture->GetActualWidth(); actualView.height = pTexture->GetActualHeight(); // these DU/DV constants need to be 64, not 128 on xbox because the texture is signed and we only use the positive half of that space materials->ClearColor4ub( 255, 64, 64, 0 ); render->Push3DView( actualView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, true, pTexture, m_Frustum ); materials->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); // Make sure sound doesn't stutter engine->Sound_ExtraUpdate(); render->DrawWaterDuDv( waterZAdjust ); render->PopView( m_Frustum ); } #endif // _XBOX //----------------------------------------------------------------------------- // Draws the scene when the view point is above the level of the water //----------------------------------------------------------------------------- #include "tier0/memdbgoff.h" void CViewRender::ViewDrawScene_EyeAboveWater( bool bDrawSkybox, const CViewSetup &view, int nClearFlags, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo ) { VPROF( "CViewRender::ViewDrawScene_EyeAboveWater" ); bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping(); ClientWorldListInfo_t info; #ifndef _XBOX CRenderList renderList; CRenderList *pRenderList = &renderList; #else void *ptr = MemAllocScratch(sizeof(CRenderList)); CRenderList *pRenderList = new (ptr) CRenderList; #endif float newWaterHeight, waterZAdjust; CalcWaterEyeAdjustments( view, fogInfo, newWaterHeight, waterZAdjust, bSoftwareUserClipPlane ); // eye is outside of water // render the reflection if( waterInfo.m_bReflect ) { // NOTE: Clearing the color is unnecessary since we're drawing the skybox // and dest-alpha is never used in the reflection int nFlags = DF_RENDER_REFLECTION | DF_CLIP_Z | DF_CLIP_BELOW | DF_RENDER_ABOVEWATER | DF_CLEARDEPTH | DF_UPDATELIGHTMAPS | DF_BUILDWORLDLISTS; // NOTE: This will cause us to draw the 2d skybox in the reflection // (which we want to do instead of drawing the 3d skybox) nFlags |= DF_DRAWSKYBOX; if( waterInfo.m_bReflectEntities ) { nFlags |= DF_DRAW_ENTITITES; } // Disable occlusion visualization in reflection bool bVisOcclusion = r_visocclusion.GetInt(); r_visocclusion.SetValue( 0 ); EnableWorldFog(); WaterDrawHelper( view, info, *pRenderList, fogInfo.m_flWaterHeight, nFlags, VIEW_REFLECTION, 0.0f, fogInfo.m_nVisibleFogVolumeLeaf ); r_visocclusion.SetValue( bVisOcclusion ); materials->Flush(); } unsigned char ucFogColor[3]; int nFlags; bool bViewIntersectsWater = false; // render refraction int nFirstPassFlags = DF_BUILDWORLDLISTS | DF_UPDATELIGHTMAPS | DF_CLEARCOLOR; if ( waterInfo.m_bRefract ) { nFlags = nFirstPassFlags | DF_RENDER_REFRACTION | DF_CLIP_Z | DF_RENDER_UNDERWATER | DF_FUDGE_UP | DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS | DF_CLEARDEPTH; nFirstPassFlags = 0; render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true ); SetClearColorToFogColor( ucFogColor ); WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, VIEW_REFRACTION, waterZAdjust, -1 ); if( !bSoftwareUserClipPlane ) { bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume ); } #ifdef _XBOX // This is a workaround for the lack of perspective correction on the XBox for dudv maps EnableWorldFog(); DrawScreenSpaceWaterDuDv( view, waterZAdjust ); #endif materials->ClearColor4ub( 0, 0, 0, 255 ); } materials->Flush(); EnableWorldFog(); // render the world nFlags = nFirstPassFlags | DF_RENDER_ABOVEWATER | DF_CLEARDEPTH | DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS; if( bViewIntersectsWater && !bSoftwareUserClipPlane ) { // This is necessary to keep the non-water fogged world from drawing underwater in // the case where we want to partially see into the water. nFlags |= DF_CLIP_Z | DF_CLIP_BELOW; } if ( bDrawSkybox ) { nFlags |= DF_DRAWSKYBOX; nFlags &= ~DF_CLEARCOLOR; } if ( waterInfo.m_bDrawWaterSurface ) { nFlags |= DF_RENDER_WATER; } if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater ) { nFlags |= DF_RENDER_UNDERWATER; } WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 ); if( waterZAdjust != 0.0f && bSoftwareUserClipPlane && waterInfo.m_bRefract ) { nFlags = DF_RENDER_UNDERWATER; WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust,-1 ); } else if( bViewIntersectsWater ) { materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true ); nFlags = DF_RENDER_UNDERWATER | DF_CLIP_Z | DF_DRAW_ENTITITES; WaterDrawHelper( view, info, *pRenderList, fogInfo.m_flWaterHeight, nFlags, VIEW_NONE, 0.0f, -1 ); } materials->ClearColor4ub( 0, 0, 0, 255 ); #ifdef _XBOX MemFreeScratch(); #endif } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Draws the scene when the view point is under the level of the water //----------------------------------------------------------------------------- #include "tier0/memdbgoff.h" void CViewRender::ViewDrawScene_EyeUnderWater( bool bDrawSkybox, const CViewSetup &view, int nClearFlags, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo ) { // FIXME: The 3d skybox shouldn't be drawn when the eye is under water VPROF( "CViewRender::ViewDrawScene_EyeUnderWater" ); bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping(); float newWaterHeight, waterZAdjust; CalcWaterEyeAdjustments( view, fogInfo, newWaterHeight, waterZAdjust, bSoftwareUserClipPlane ); ClientWorldListInfo_t info; #ifndef _XBOX CRenderList renderList; CRenderList *pRenderList = &renderList; #else void *ptr = MemAllocScratch(sizeof(CRenderList)); CRenderList *pRenderList = new (ptr) CRenderList; #endif int nFirstPassFlags = DF_BUILDWORLDLISTS | DF_UPDATELIGHTMAPS; render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true ); unsigned char ucFogColor[3]; materials->GetFogColor( ucFogColor ); materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); // render refraction (out of water) if ( waterInfo.m_bRefract ) { // NOTE: Refraction renders into the back buffer, over the top of the 3D skybox // It is then blitted out into the refraction target. This is so that // we only have to set up 3d sky vis once, and only render it once also! int nFlags = nFirstPassFlags | DF_CLIP_Z | DF_CLIP_BELOW | DF_RENDER_ABOVEWATER | DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS | DF_CLEARDEPTH; if ( bDrawSkybox ) { nFlags |= DF_DRAWSKYBOX | DF_CLIP_SKYBOX | DF_CLEARCOLOR; } nFirstPassFlags = 0; EnableWorldFog(); WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, VIEW_REFRACTION, waterZAdjust, -1 ); Rect_t srcRect; srcRect.x = view.x; srcRect.y = view.y; srcRect.width = view.width; srcRect.height = view.height; ITexture *pTexture = GetWaterRefractionTexture(); materials->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL ); #ifdef _XBOX // This is a workaround for the lack of perspective correction on the XBox for dudv maps render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false ); DrawScreenSpaceWaterDuDv( view, waterZAdjust ); materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); #endif } // NOTE: We're not drawing the 2d skybox under water since it's assumed to not be visible. // render the world underwater // Clear the color to get the appropriate underwater fog color int nFlags = nFirstPassFlags | DF_MAINTAINWORLDLISTS | DF_FUDGE_UP | DF_RENDER_UNDERWATER | DF_CLEARDEPTH | DF_DRAW_ENTITITES; if( !bSoftwareUserClipPlane ) { nFlags |= DF_CLIP_Z; } if ( waterInfo.m_bDrawWaterSurface ) { nFlags |= DF_RENDER_WATER; } if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater ) { nFlags |= DF_RENDER_ABOVEWATER; } render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false ); WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 ); if( waterZAdjust != 0.0f && bSoftwareUserClipPlane && waterInfo.m_bRefract ) { nFlags = DF_RENDER_ABOVEWATER; WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 ); } materials->ClearColor4ub( 0, 0, 0, 255 ); #ifdef _XBOX MemFreeScratch(); #endif } //----------------------------------------------------------------------------- // Draws the scene when there's no water or cheap water //----------------------------------------------------------------------------- void CViewRender::ViewDrawScene_NoWater( bool bDrawSkybox, const CViewSetup &view, int nClearFlags, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo ) { VPROF( "CViewRender::ViewDrawScene_NoWater" ); ClientWorldListInfo_t info; #ifndef _XBOX CRenderList renderList; CRenderList *pRenderList = &renderList; #else void *ptr = MemAllocScratch(sizeof(CRenderList)); CRenderList *pRenderList = new (ptr) CRenderList; #endif int nFlags = DF_BUILDWORLDLISTS | DF_DRAW_ENTITITES | DF_UPDATELIGHTMAPS; if ( nClearFlags & VIEW_CLEAR_COLOR ) { nFlags |= DF_CLEARCOLOR; } if ( nClearFlags & VIEW_CLEAR_DEPTH ) { nFlags |= DF_CLEARDEPTH; } if ( !waterInfo.m_bOpaqueWater ) { nFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; } else { bool bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume ); if( bViewIntersectsWater ) { // have to draw both sides if we can see both. nFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; } else if ( fogInfo.m_bEyeInFogVolume ) { nFlags |= DF_RENDER_UNDERWATER; } else { nFlags |= DF_RENDER_ABOVEWATER; } } if ( waterInfo.m_bDrawWaterSurface ) { nFlags |= DF_RENDER_WATER; } if ( !fogInfo.m_bEyeInFogVolume ) { if ( bDrawSkybox ) { nFlags |= DF_DRAWSKYBOX; } EnableWorldFog(); } else { nFlags |= DF_CLEARCOLOR; render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false ); unsigned char ucFogColor[3]; materials->GetFogColor( ucFogColor ); materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); } WaterDrawHelper( view, info, *pRenderList, 0.0f, nFlags, CurrentViewID(), 0.0f, m_iForceViewLeaf ); materials->ClearColor4ub( 0, 0, 0, 255 ); #ifdef _XBOX MemFreeScratch(); #endif } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Methods related to controlling the cheap water distance //----------------------------------------------------------------------------- void CViewRender::SetCheapWaterStartDistance( float flCheapWaterStartDistance ) { m_flCheapWaterStartDistance = flCheapWaterStartDistance; } void CViewRender::SetCheapWaterEndDistance( float flCheapWaterEndDistance ) { m_flCheapWaterEndDistance = flCheapWaterEndDistance; } void CViewRender::GetWaterLODParams( float &flCheapWaterStartDistance, float &flCheapWaterEndDistance ) { flCheapWaterStartDistance = m_flCheapWaterStartDistance; flCheapWaterEndDistance = m_flCheapWaterEndDistance; } static void CheapWaterStart_f( void ) { if( engine->Cmd_Argc() == 2 ) { float dist = atof( engine->Cmd_Argv( 1 ) ); view->SetCheapWaterStartDistance( dist ); } else { float start, end; view->GetWaterLODParams( start, end ); Warning( "r_cheapwaterstart: %f\n", start ); } } static void CheapWaterEnd_f( void ) { if( engine->Cmd_Argc() == 2 ) { float dist = atof( engine->Cmd_Argv( 1 ) ); view->SetCheapWaterEndDistance( dist ); } else { float start, end; view->GetWaterLODParams( start, end ); Warning( "r_cheapwaterend: %f\n", end ); } } //----------------------------------------------------------------------------- // A console command allowing you to draw a material as an overlay //----------------------------------------------------------------------------- static void ScreenOverlay_f( void ) { if( engine->Cmd_Argc() == 2 ) { if ( !Q_stricmp( "off", engine->Cmd_Argv(1) ) ) { view->SetScreenOverlayMaterial( NULL ); } else { IMaterial *pMaterial = materials->FindMaterial( engine->Cmd_Argv(1), TEXTURE_GROUP_OTHER, false ); if ( !IsErrorMaterial( pMaterial ) ) { view->SetScreenOverlayMaterial( pMaterial ); } else { view->SetScreenOverlayMaterial( NULL ); } } } else { IMaterial *pMaterial = view->GetScreenOverlayMaterial(); Warning( "r_screenoverlay: %s\n", pMaterial ? pMaterial->GetName() : "off" ); } } //----------------------------------------------------------------------------- // Purpose: // Input : &view - // &introData - //----------------------------------------------------------------------------- void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData ) { VPROF( "CViewRender::ViewDrawScene" ); // ----------------------------------------------------------------------- // Set the clear color to black since we are going to be adding up things // in the frame buffer. // ----------------------------------------------------------------------- // Clear alpha to 255 so that masking with the vortigaunts (0) works properly. materials->ClearColor4ub( 0, 0, 0, 255 ); // ----------------------------------------------------------------------- // Draw the primary scene and copy it to the first framebuffer texture // ----------------------------------------------------------------------- CViewSetup playerView = view; playerView.origin = introData.m_vecCameraView; playerView.m_vUnreflectedOrigin = introData.m_vecCameraView; playerView.angles = introData.m_vecCameraViewAngles; if ( introData.m_playerViewFOV ) { playerView.fov = ScaleFOVByWidthRatio( introData.m_playerViewFOV, engine->GetScreenAspectRatio() / ( 4.0f / 3.0f ) ); } SetupCurrentView( playerView.origin, playerView.angles, VIEW_INTRO_PLAYER ); // Invoke pre-render methods IGameSystem::PreRenderAllSystems(); // Start view, clear frame/z buffer if necessary unsigned int visFlags; SetupVis( playerView, visFlags ); if( introData.m_bDrawSecondary ) { // NOTE: We only increment this once since time doesn't move forward. ParticleMgr()->IncrementFrameCode(); } if( introData.m_bDrawPrimary ) { DrawWorldAndEntities( true /* drawSkybox */, playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH ); } else { materials->ClearBuffers( true, true ); } Rect_t actualRect; UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false, &actualRect ); // ----------------------------------------------------------------------- // Draw the secondary scene and copy it to the second framebuffer texture // ----------------------------------------------------------------------- CViewSetup cameraView = view; SetupCurrentView( cameraView.origin, cameraView.angles, VIEW_INTRO_CAMERA ); // Invoke pre-render methods IGameSystem::PreRenderAllSystems(); // Start view, clear frame/z buffer if necessary SetupVis( cameraView, visFlags ); // Clear alpha to 255 so that masking with the vortigaunts (0) works properly. materials->ClearColor4ub( 0, 0, 0, 255 ); if( introData.m_bDrawSecondary ) { DrawWorldAndEntities( true /* drawSkybox */, cameraView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH ); } else { materials->ClearBuffers( true, true ); } UpdateScreenEffectTexture( 1, view.x, view.y, view.width, view.height ); // ----------------------------------------------------------------------- // Draw quads on the screen for each screenspace pass. // ----------------------------------------------------------------------- // Find the material that we use to render the overlays IMaterial *pOverlayMaterial = materials->FindMaterial( "scripted/intro_screenspaceeffect", TEXTURE_GROUP_OTHER ); IMaterialVar *pModeVar = pOverlayMaterial->FindVar( "$mode", NULL ); IMaterialVar *pAlphaVar = pOverlayMaterial->FindVar( "$alpha", NULL ); materials->ClearBuffers( true, true ); materials->MatrixMode( MATERIAL_VIEW ); materials->PushMatrix(); materials->LoadIdentity(); materials->MatrixMode( MATERIAL_PROJECTION ); materials->PushMatrix(); materials->LoadIdentity(); int passID; for( passID = 0; passID < introData.m_Passes.Count(); passID++ ) { const IntroDataBlendPass_t& pass = introData.m_Passes[passID]; if ( pass.m_Alpha == 0 ) continue; // Pick one of the blend modes for the material. if( pass.m_BlendMode >= 0 && pass.m_BlendMode < 9 ) { pModeVar->SetIntValue( pass.m_BlendMode ); } else { Assert(0); } // Set the alpha value for the material. pAlphaVar->SetFloatValue( pass.m_Alpha ); // Draw a quad for this pass. ITexture *pTexture = GetFullFrameFrameBufferTexture( 0 ); materials->DrawScreenSpaceRectangle( pOverlayMaterial, view.x, view.y, view.width, view.height, actualRect.x, actualRect.y, actualRect.x+actualRect.width-1, actualRect.y+actualRect.height-1, pTexture->GetActualWidth(), pTexture->GetActualHeight() ); } materials->MatrixMode( MATERIAL_VIEW ); materials->PopMatrix(); materials->MatrixMode( MATERIAL_PROJECTION ); materials->PopMatrix(); // Draw the starfield // FIXME // blur? // Disable fog for the rest of the stuff DisableFog(); // Here are the overlays... CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState ); // issue the pixel visibility tests PixelVisibility_EndCurrentView(); // And here are the screen-space effects PerformScreenSpaceEffects( view.x, view.y, view.width, view.height ); // Make sure sound doesn't stutter engine->Sound_ExtraUpdate(); // Debugging info goes over the top Draw3DDebuggingInfo( view ); // Let the particle manager simulate things that haven't been simulated. ParticleMgr()->PostRender(); FinishCurrentView(); } #ifdef _XBOX void ViewTexture_f() { int argc; char* enable; argc = engine->Cmd_Argc(); if (argc == 1) { // toggle enable = mat_viewTextureEnable.GetInt() ? "0" : "1"; } else { // force on enable = "1"; } mat_viewTextureEnable.SetValue(enable); if (argc >= 2) mat_viewTextureName.SetValue(engine->Cmd_Argv(1)); if (argc >= 3) mat_viewTextureScale.SetValue(engine->Cmd_Argv(2)); } static ConCommand mat_viewTexture("mat_viewTexture", ViewTexture_f, "Show/Hide texture [name] [scale]"); #endif static ConCommand r_cheapwaterstart( "r_cheapwaterstart", CheapWaterStart_f ); static ConCommand r_cheapwaterend( "r_cheapwaterend", CheapWaterEnd_f ); static ConCommand r_screenspacematerial( "r_screenoverlay", ScreenOverlay_f );