//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Rising liquid that acts as a one-way portal // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "c_func_liquidportal.h" #include "debugoverlay_shared.h" #include "view_scene.h" #include "view.h" #include "ScreenSpaceEffects.h" #include "materialsystem/imaterialvar.h" LINK_ENTITY_TO_CLASS( func_liquidportal, C_Func_LiquidPortal ); IMPLEMENT_CLIENTCLASS_DT( C_Func_LiquidPortal, DT_Func_LiquidPortal, CFunc_LiquidPortal ) RecvPropEHandle( RECVINFO(m_hLinkedPortal) ), RecvPropFloat( RECVINFO(m_fFillStartTime) ), RecvPropFloat( RECVINFO(m_fFillEndTime) ), END_RECV_TABLE() #define LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( vertnum, xtexbase, xtexscale, ytexbase, ytexscale )\ meshBuilder.Position3fv( &vVertices[vertnum].x );\ meshBuilder.TexCoord2f( 0, vVertices[vertnum].xtexbase * xtexscale, vVertices[vertnum].ytexbase * ytexscale );\ meshBuilder.AdvanceVertex(); C_Func_LiquidPortal::C_Func_LiquidPortal( void ) { g_pPortalRender->AddPortal( this ); } C_Func_LiquidPortal::~C_Func_LiquidPortal( void ) { g_pPortalRender->RemovePortal( this ); } int C_Func_LiquidPortal::DrawModel( int flags ) { if( IsFillingNow() ) { DrawPortal(); return 1; } return 0; } void C_Func_LiquidPortal::OnDataChanged( DataUpdateType_t updateType ) { GetRenderBoundsWorldspace( m_vAABBMins, m_vAABBMaxs ); m_pLinkedPortal = m_hLinkedPortal.Get(); ComputeLinkMatrix(); UpdateBoundingPlanes(); } void C_Func_LiquidPortal::ComputeLinkMatrix( void ) { C_Func_LiquidPortal *pLinkedPortal = m_hLinkedPortal.Get(); if( pLinkedPortal ) { VMatrix matLocalToWorld, matLocalToWorldInv, matRemoteToWorld; //matLocalToWorld.Identity(); //matLocalToWorld.SetTranslation( CollisionProp()->WorldSpaceCenter() ); matLocalToWorld = EntityToWorldTransform(); //matRemoteToWorld.Identity(); //matRemoteToWorld.SetTranslation( pLinkedPortal->CollisionProp()->WorldSpaceCenter() ); matRemoteToWorld = pLinkedPortal->EntityToWorldTransform(); MatrixInverseTR( matLocalToWorld, matLocalToWorldInv ); m_matrixThisToLinked = matRemoteToWorld * matLocalToWorldInv; MatrixInverseTR( m_matrixThisToLinked, pLinkedPortal->m_matrixThisToLinked ); } else { m_matrixThisToLinked.Identity(); } } void CPortalRenderable_Func_LiquidPortal::UpdateBoundingPlanes( void ) { //x min m_fBoundingPlanes[0][0] = 1.0f; m_fBoundingPlanes[0][1] = 0.0f; m_fBoundingPlanes[0][2] = 0.0f; m_fBoundingPlanes[0][3] = m_vAABBMins.x; //x max m_fBoundingPlanes[1][0] = -1.0f; m_fBoundingPlanes[1][1] = 0.0f; m_fBoundingPlanes[1][2] = 0.0f; m_fBoundingPlanes[1][3] = -m_vAABBMaxs.x; //y min m_fBoundingPlanes[2][0] = 0.0f; m_fBoundingPlanes[2][1] = 1.0f; m_fBoundingPlanes[2][2] = 0.0f; m_fBoundingPlanes[2][3] = m_vAABBMins.y; //y max m_fBoundingPlanes[3][0] = 0.0f; m_fBoundingPlanes[3][1] = -1.0f; m_fBoundingPlanes[3][2] = 0.0f; m_fBoundingPlanes[3][3] = -m_vAABBMaxs.y; //z min m_fBoundingPlanes[4][0] = 0.0f; m_fBoundingPlanes[4][1] = 0.0f; m_fBoundingPlanes[4][2] = 1.0f; m_fBoundingPlanes[4][3] = m_vAABBMins.z; //z max is too variable to store } void CPortalRenderable_Func_LiquidPortal::DrawPreStencilMask( void ) { // Should we do something here like flatbasic? } void CPortalRenderable_Func_LiquidPortal::DrawStencilMask( void ) { DrawOutwardBox( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model ); DrawInnerLiquid( true, 1.0f, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model ); } void CPortalRenderable_Func_LiquidPortal::DrawPostStencilFixes( void ) { DrawOutwardBox( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model ); DrawInnerLiquid( true, 1.0f, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model ); } void CPortalRenderable_Func_LiquidPortal::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView ) { if( m_pLinkedPortal == NULL ) //not linked to any portal return; Frustum FrustumBackup; memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) ); Frustum seeThroughFrustum; bool bUseSeeThroughFrustum; if ( g_pPortalRender->GetViewRecursionLevel() == 0 ) { bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum, pViewRender->GetFrustum(), FRUSTUM_NUMPLANES ); } else { bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum ); } Vector vCameraForward; AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL ); // Setup fog state for the camera. Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin; Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward ); CViewSetup portalView = cameraView; QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() ); portalView.width = cameraView.width; portalView.height = cameraView.height; portalView.x = 0; portalView.y = 0; portalView.origin = ptPOVOrigin; portalView.angles = qPOVAngles; portalView.fov = cameraView.fov; portalView.m_bOrtho = false; portalView.m_flAspectRatio = cameraView.m_flAspectRatio; //use the screen aspect ratio, 0.0f doesn't work as advertised CopyToCurrentView( pViewRender, portalView ); CMatRenderContextPtr pRenderContext( materials ); { ViewCustomVisibility_t customVisibility; m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility ); render->Push3DView( portalView, 0, NULL, pViewRender->GetFrustum() ); { if( bUseSeeThroughFrustum) memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) ); render->OverrideViewFrustum( pViewRender->GetFrustum() ); SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() + 1 ); CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal(); CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal(); SetViewEntranceAndExitPortals( this, m_pLinkedPortal ); //DRAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ViewDrawScene_PortalStencil( pViewRender, portalView, &customVisibility ); SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup ); SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() - 1 ); } render->PopView( pViewRender->GetFrustum() ); //restore old frustum memcpy( pViewRender->GetFrustum(), FrustumBackup, sizeof( Frustum ) ); render->OverrideViewFrustum( FrustumBackup ); } //restore old vis data CopyToCurrentView( pViewRender, cameraView ); } void CPortalRenderable_Func_LiquidPortal::RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView ) { } void CPortalRenderable_Func_LiquidPortal::AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility ) { if ( !pCustomVisibility ) return; VisOverrideData_t visOverride; Vector vOrigin = (m_vAABBMins + m_vAABBMaxs) * 0.5f; visOverride.m_vecVisOrigin = vOrigin; visOverride.m_fDistToAreaPortalTolerance = 64.0f; // Specify which leaf to use for area portal culling pCustomVisibility->ForceVisOverride( visOverride ); pCustomVisibility->ForceViewLeaf( enginetrace->GetLeafContainingPoint( vOrigin ) ); pCustomVisibility->AddVisOrigin( vOrigin ); } bool CPortalRenderable_Func_LiquidPortal::DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const { return ((m_vAABBMins.z < waterZ) && (m_vAABBMaxs.z > waterZ)); } SkyboxVisibility_t CPortalRenderable_Func_LiquidPortal::SkyBoxVisibleFromPortal( void ) { return SKYBOX_NOT_VISIBLE; } bool CPortalRenderable_Func_LiquidPortal::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum, const VPlane *pInputFrustum, int iInputFrustumPlaneCount ) { return false; } const Vector& CPortalRenderable_Func_LiquidPortal::GetFogOrigin( void ) const { return vec3_origin; } void CPortalRenderable_Func_LiquidPortal::ShiftFogForExitPortalView() const { } bool CPortalRenderable_Func_LiquidPortal::ShouldUpdatePortalView_BasedOnView( const CViewSetup ¤tView, Frustum currentFrustum ) { //return false; return IsFillingNow(); } CPortalRenderable* CPortalRenderable_Func_LiquidPortal::GetLinkedPortal() const { return m_pLinkedPortal; } bool CPortalRenderable_Func_LiquidPortal::ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup ) { return false; } void CPortalRenderable_Func_LiquidPortal::DrawPortal( void ) { if( IsFillingNow() ) { //"shadertest/gooinglass" //"glass/glasswindow_refract01" //IMaterial *pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER ); //UpdateFrontBufferTexturesForMaterial( (IMaterial *)pMaterial ); DrawOutwardBox(); //DrawInnerLiquid( pMaterial ); //DrawInwardBox( pMaterial ); } } void CPortalRenderable_Func_LiquidPortal::GetToolRecordingState( bool bActive, KeyValues *msg ) { } void CPortalRenderable_Func_LiquidPortal::HandlePortalPlaybackMessage( KeyValues *pKeyValues ) { } void CPortalRenderable_Func_LiquidPortal::DrawOutwardBox( const IMaterial *pMaterial ) { if( pMaterial == NULL ) pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER ); const float fVerticalTextureScale = 1.0f / 100.0f; const float fHorizontalTextureScale = 1.0f / 100.0f; float fMaxZ = m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount()); Vector vVertices[8]; for( int i = 0; i != 8; ++i ) { vVertices[i].x = (i&(1<<0)) ? m_vAABBMaxs.x : m_vAABBMins.x; vVertices[i].y = (i&(1<<1)) ? m_vAABBMaxs.y : m_vAABBMins.y; vVertices[i].z = (i&(1<<2)) ? fMaxZ : m_vAABBMins.z; } CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( (IMaterial *)pMaterial, (CPortalRenderable_Func_LiquidPortal*)this ); CMeshBuilder meshBuilder; IMesh* pMesh = pRenderContext->GetDynamicMesh( false ); meshBuilder.Begin( pMesh, MATERIAL_QUADS, 6 ); //x min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); //x max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); //y min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); //y max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); //z min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, y, fVerticalTextureScale ); //z max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, y, fVerticalTextureScale ); meshBuilder.End(); pMesh->Draw(); pRenderContext->Flush( false ); } void CPortalRenderable_Func_LiquidPortal::DrawInwardBox( const IMaterial *pMaterial ) { if( pMaterial == NULL ) pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER ); const float fVerticalTextureScale = 1.0f / 100.0f; const float fHorizontalTextureScale = 1.0f / 100.0f; float fMaxZ = m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount()); Vector vVertices[8]; for( int i = 0; i != 8; ++i ) { vVertices[i].x = (i&(1<<0)) ? m_vAABBMaxs.x : m_vAABBMins.x; vVertices[i].y = (i&(1<<1)) ? m_vAABBMaxs.y : m_vAABBMins.y; vVertices[i].z = (i&(1<<2)) ? fMaxZ : m_vAABBMins.z; } CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( (IMaterial *)pMaterial, (CPortalRenderable_Func_LiquidPortal*)this ); CMeshBuilder meshBuilder; IMesh* pMesh = pRenderContext->GetDynamicMesh( false ); meshBuilder.Begin( pMesh, MATERIAL_QUADS, 6 ); //x min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); //x max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, y, fHorizontalTextureScale, z, -fVerticalTextureScale ); //y min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); //y max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, z, -fVerticalTextureScale ); //z min LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, y, fVerticalTextureScale ); //z max LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, y, fVerticalTextureScale ); LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, y, fVerticalTextureScale ); meshBuilder.End(); pMesh->Draw(); pRenderContext->Flush( false ); } void CPortalRenderable_Func_LiquidPortal::DrawInnerLiquid( bool bClipToBounds, float fOpacity, const IMaterial *pMaterial ) //quads in front of camera clipped to box dimensions { if( !IsFillingNow() && bClipToBounds ) return; PortalMeshPoint_t WorkVertices[4]; //view->GetViewSetup()->zNear; Vector vForward, vUp, vRight, vOrigin; vForward = CurrentViewForward(); vUp = CurrentViewUp(); vRight = CurrentViewRight(); //vOrigin = CurrentViewOrigin() + vForward * (view->GetViewSetup()->zNear + 0.011f); //experimentation has shown this to be the optimal distance on the Nvidia 6800 cards we develop on vOrigin = CurrentViewOrigin() + vForward * (view->GetViewSetup()->zNear + 1.0f ); const float fScalingAmount = 5.0f; WorkVertices[0].texCoord.x = fScalingAmount; WorkVertices[0].texCoord.y = fScalingAmount; WorkVertices[1].texCoord.x = fScalingAmount; WorkVertices[1].texCoord.y = 0.0f; WorkVertices[2].texCoord.x = 0.0f; WorkVertices[2].texCoord.y = 0.0f; WorkVertices[3].texCoord.x = 0.0f; WorkVertices[3].texCoord.y = fScalingAmount; WorkVertices[0].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * -40.0f); WorkVertices[1].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * 40.0f); WorkVertices[2].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * 40.0f); WorkVertices[3].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * -40.0f); PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( 4 * (6) * 2 * sizeof( PortalMeshPoint_t ) ); //really only should need 2x points, but I'm paranoid PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( 4 * (6) * 2 * sizeof( PortalMeshPoint_t ) ); PortalMeshPoint_t *pFinalVerts; int iVertCount; if( bClipToBounds ) { PortalMeshPoint_t *pTempVerts; //clip by first plane and put output into pInVerts iVertCount = ClipPolyToPlane_LerpTexCoords( WorkVertices, 4, pInVerts, Vector( 0.0f, 0.0f, -1.0f ), -(m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount())), 0.01f ); //clip by other planes and flipflop in and out pointers for( int i = 0; i != 5; ++i ) { if( iVertCount < 3 ) return; //nothing to draw iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, *(Vector *)m_fBoundingPlanes[i], m_fBoundingPlanes[i][3], 0.01f ); pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers } if( iVertCount < 3 ) return; //nothing to draw pFinalVerts = pInVerts; } else { pFinalVerts = WorkVertices; iVertCount = 4; } bool bInterpOpacity = false; if( pMaterial == NULL ) { pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER ); bInterpOpacity = ( fOpacity != 1.0f ); } if( bInterpOpacity ) { IMaterial *pEditMaterial = (IMaterial *)pMaterial; //we'll be making changes, then changing it back IMaterialVar *pRefractAmount = pEditMaterial->FindVar( "$refractamount", NULL ); IMaterialVar *pBlurAmount = pEditMaterial->FindVar( "$bluramount", NULL ); IMaterialVar *pTint = pEditMaterial->FindVar( "$refracttint", NULL ); float fOriginalRefractAmount = pRefractAmount->GetFloatValue(); float fOriginalBlurAmount = pBlurAmount->GetFloatValue(); Vector4D vOriginalTint; pTint->GetVecValue( &vOriginalTint.x, 4 ); Vector4D vModdedTint = vOriginalTint; pRefractAmount->SetFloatValue( fOriginalRefractAmount * fOpacity ); pBlurAmount->SetFloatValue( fOriginalBlurAmount * fOpacity ); vModdedTint.x = 1.0f - ((1.0f - vOriginalTint.x) * fOpacity); vModdedTint.y = 1.0f - ((1.0f - vOriginalTint.y) * fOpacity); vModdedTint.z = 1.0f - ((1.0f - vOriginalTint.z) * fOpacity); pTint->SetVecValue( &vModdedTint.x, 4 ); Clip_And_Render_Convex_Polygon( pFinalVerts, iVertCount, pEditMaterial, this ); materials->Flush(); pRefractAmount->SetFloatValue( fOriginalRefractAmount ); pBlurAmount->SetFloatValue( fOriginalBlurAmount ); pTint->SetVecValue( &vOriginalTint.x, 4 ); } else { Clip_And_Render_Convex_Polygon( pFinalVerts, iVertCount, pMaterial, this ); } } ADD_SCREENSPACE_EFFECT( CLiquidPortal_InnerLiquidEffect, LiquidPortal_InnerLiquid ); const float CLiquidPortal_InnerLiquidEffect::s_fFadeBackEffectTime = 5.0f; CLiquidPortal_InnerLiquidEffect::CLiquidPortal_InnerLiquidEffect( void ) : m_bEnable(true), m_pImmersionPortal(NULL), m_bFadeBackToReality(false), m_fFadeBackTimeLeft(0.0f) { } void CLiquidPortal_InnerLiquidEffect::SetParameters( KeyValues *params ) { /*KeyValues *pImmersionPortal = params->FindKey( "immersion_portal" ); if( pImmersionPortal ) m_pImmersionPortal = (C_Func_LiquidPortal *)pImmersionPortal->GetPtr();*/ } void CLiquidPortal_InnerLiquidEffect::Render( int x, int y, int w, int h ) { if( !m_pImmersionPortal || !m_bEnable ) return; if( m_bFadeBackToReality ) { //effect should cover whole screen and have a alpha-like fade back to normal view m_fFadeBackTimeLeft -= gpGlobals->absoluteframetime; if( m_fFadeBackTimeLeft > 0.0f ) { float fInterp = m_fFadeBackTimeLeft/s_fFadeBackEffectTime; //clear depth buffer so we can be all warpy on the view model too CMatRenderContextPtr pRenderContext( materials ); pRenderContext->ClearBuffers( false, true, false ); pRenderContext->OverrideDepthEnable( true, false ); m_pImmersionPortal->DrawInnerLiquid( false, fInterp ); pRenderContext->OverrideDepthEnable( false, true ); } else { m_bFadeBackToReality = false; m_pImmersionPortal = NULL; } } else { //effect should only cover a portion of the screen and be in full warpiness //clear depth buffer so we can be all warpy on the view model too CMatRenderContextPtr pRenderContext( materials ); pRenderContext->ClearBuffers( false, true, false ); pRenderContext->OverrideDepthEnable( true, false ); m_pImmersionPortal->DrawInnerLiquid(); pRenderContext->OverrideDepthEnable( false, true ); } }