Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1747 lines
71 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "portalrenderable_flatbasic.h"
  9. #include "precache_register.h"
  10. #include "Portal_DynamicMeshRenderingUtils.h"
  11. #include "portal_shareddefs.h"
  12. #include "view.h"
  13. #include "c_pixel_visibility.h"
  14. #include "glow_overlay.h"
  15. #include "portal_render_targets.h"
  16. #include "materialsystem/ITexture.h"
  17. #include "toolframework/itoolframework.h"
  18. #include "toolframework_client.h"
  19. #include "tier1/keyvalues.h"
  20. #include "view_scene.h"
  21. #include "tier0/vprof.h"
  22. #include "materialsystem/imaterialvar.h"
  23. extern ConVar r_portal_fastpath;
  24. #define PORTALRENDERABLE_FLATBASIC_MINPIXELVIS 0.0f
  25. CUtlStack<Vector4D> CPortalRenderable_FlatBasic::ms_clipPlaneStack;
  26. CPortalRenderable_FlatBasic::CPortalRenderable_FlatBasic( void )
  27. : m_pLinkedPortal( NULL ),
  28. m_ptOrigin( 0.0f, 0.0f, 0.0f ),
  29. m_vForward( 1.0f, 0.0f, 0.0f ),
  30. m_vUp( 0.0f, 0.0f, 1.0f ),
  31. m_vRight( 0.0f, 1.0f, 0.0f ),
  32. m_bIsPortal2( false )
  33. {
  34. m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
  35. m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = Vector(0,0,0);
  36. m_InternallyMaintainedData.m_VisData.m_bTrimFrustumToPortalCorners = false;
  37. m_InternallyMaintainedData.m_VisData.m_vPortalOrigin = m_InternallyMaintainedData.m_VisData.m_vPortalForward = vec3_origin;
  38. m_InternallyMaintainedData.m_VisData.m_flPortalRadius = 0.0f;
  39. m_InternallyMaintainedData.m_iViewLeaf = -1;
  40. for( int i = 0; i != ARRAYSIZE( m_InternallyMaintainedData.m_DepthDoublerTextureView ); ++i )
  41. {
  42. m_InternallyMaintainedData.m_DepthDoublerTextureView[i].Identity();
  43. }
  44. m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = false;
  45. m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = SKYBOX_NOT_VISIBLE;
  46. m_InternallyMaintainedData.m_ptForwardOrigin.Init( 1.0f, 0.0f, 0.0f );
  47. m_InternallyMaintainedData.m_ptCorners[0] =
  48. m_InternallyMaintainedData.m_ptCorners[1] =
  49. m_InternallyMaintainedData.m_ptCorners[2] =
  50. m_InternallyMaintainedData.m_ptCorners[3] =
  51. Vector( 0.0f, 0.0f, 0.0f );
  52. }
  53. void CPortalRenderable_FlatBasic::PortalMoved( void )
  54. {
  55. m_InternallyMaintainedData.m_ptForwardOrigin = m_ptOrigin + m_vForward;
  56. m_InternallyMaintainedData.m_fPlaneDist = m_vForward.Dot( m_ptOrigin );
  57. // Update the points on the portal which we add to PVS
  58. {
  59. Vector vScaledRight = m_vRight * m_fHalfWidth;
  60. Vector vScaledUp = m_vUp * m_fHalfHeight;
  61. m_InternallyMaintainedData.m_ptCorners[0] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) + vScaledUp;
  62. m_InternallyMaintainedData.m_ptCorners[1] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) + vScaledUp;
  63. m_InternallyMaintainedData.m_ptCorners[2] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) - vScaledUp;
  64. m_InternallyMaintainedData.m_ptCorners[3] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) - vScaledUp;
  65. m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = m_InternallyMaintainedData.m_ptForwardOrigin;
  66. m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
  67. m_InternallyMaintainedData.m_VisData.m_bTrimFrustumToPortalCorners = true;
  68. memcpy( m_InternallyMaintainedData.m_VisData.m_vPortalCorners, m_InternallyMaintainedData.m_ptCorners, sizeof( m_InternallyMaintainedData.m_VisData.m_vPortalCorners ) );
  69. m_InternallyMaintainedData.m_VisData.m_vPortalOrigin = m_ptOrigin;
  70. m_InternallyMaintainedData.m_VisData.m_vPortalForward = m_vForward;
  71. m_InternallyMaintainedData.m_VisData.m_flPortalRadius = sqrtf( m_fHalfWidth * m_fHalfWidth + m_fHalfHeight * m_fHalfHeight );
  72. m_InternallyMaintainedData.m_iViewLeaf = enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
  73. }
  74. m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
  75. for( int i = 0; i < 4 && ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners != SKYBOX_3DSKYBOX_VISIBLE ); ++i )
  76. {
  77. SkyboxVisibility_t nCornerVis = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptCorners[i] );
  78. if ( ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners == SKYBOX_NOT_VISIBLE ) || ( nCornerVis != SKYBOX_NOT_VISIBLE ) )
  79. {
  80. m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = nCornerVis;
  81. }
  82. }
  83. //render fix bounding planes
  84. {
  85. for( int i = 0; i != PORTALRENDERFIXMESH_OUTERBOUNDPLANES; ++i )
  86. {
  87. float fCirclePos = ((float)(i)) * ((M_PI * 2.0f) / (float)PORTALRENDERFIXMESH_OUTERBOUNDPLANES);
  88. float fUpBlend = cosf( fCirclePos );
  89. float fRightBlend = sinf( fCirclePos );
  90. Vector vNormal = -fUpBlend * m_vUp - fRightBlend * m_vRight;
  91. Vector ptOnPlane = m_ptOrigin + (m_vUp * (fUpBlend * m_fHalfHeight * 1.1f)) + (m_vRight * (fRightBlend * m_fHalfWidth * 1.1f));
  92. m_InternallyMaintainedData.m_BoundingPlanes[i].Init( vNormal, vNormal.Dot( ptOnPlane ) );
  93. }
  94. m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].Init( -m_vForward, (-m_vForward).Dot( m_ptOrigin ) );
  95. m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1].Init( m_vForward, m_vForward.Dot( m_ptOrigin - (m_vForward * 5.0f) ) );
  96. }
  97. //update depth doubler usability flag
  98. m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration =
  99. ( m_pLinkedPortal && //linked to another portal
  100. ( m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - m_ptOrigin ) > 0.0f ) && //this portal looking in the general direction of the other portal
  101. ( m_vForward.Dot( m_pLinkedPortal->m_vForward ) < -0.7071f ) ); //within 45 degrees of facing directly at each other
  102. if( m_pLinkedPortal )
  103. m_pLinkedPortal->m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration;
  104. #if 0
  105. if ( IsToolRecording() && ((m_ptOrigin != m_ptLastRecordedOrigin) || (m_qAbsAngle != m_qLastRecordedAngle)) )
  106. {
  107. static EntityTeleportedRecordingState_t state;
  108. KeyValues *msg = new KeyValues( "entity_teleported" );
  109. msg->SetPtr( "state", &state );
  110. state.m_bTeleported = true;
  111. state.m_bViewOverride = false;
  112. state.m_vecTo = m_ptOrigin;
  113. state.m_qaTo = m_qAbsAngle;
  114. VMatrix mat_OldPosition, mat_InvOldPosition, mat_CurPosition;
  115. AngleMatrix( m_qLastRecordedAngle, m_ptLastRecordedOrigin, mat_OldPosition.As3x4() );
  116. MatrixInverseTR( mat_OldPosition, mat_InvOldPosition );
  117. AngleMatrix( m_qAbsAngle, m_ptOrigin, mat_CurPosition.As3x4() );
  118. ConcatTransforms( mat_InvOldPosition.As3x4(), mat_CurPosition.As3x4(), state.m_teleportMatrix );
  119. //state.m_teleportMatrix.Init( Vector( 1.0f, 0.0f, 0.0f ), Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), vec3_origin );
  120. m_ptLastRecordedOrigin = m_ptOrigin;
  121. m_qLastRecordedAngle = m_qAbsAngle;
  122. // Post a message back to all IToolSystems
  123. Assert( (int)GetToolHandle() != 0 );
  124. ToolFramework_PostToolMessage( GetToolHandle(), msg );
  125. msg->deleteThis();
  126. }
  127. #endif
  128. }
  129. bool CPortalRenderable_FlatBasic::WillUseDepthDoublerThisDraw( void ) const
  130. {
  131. return m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration &&
  132. (g_pPortalRender->GetRemainingPortalViewDepth() == 0) &&
  133. (g_pPortalRender->GetViewRecursionLevel() > 1) &&
  134. (g_pPortalRender->GetCurrentViewEntryPortal() == this);
  135. }
  136. ConVar r_portal_use_complex_frustums( "r_portal_use_complex_frustums", "1", FCVAR_CLIENTDLL, "View optimization, turn this off if you get odd visual bugs." );
  137. bool CPortalRenderable_FlatBasic::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
  138. {
  139. if( r_portal_use_complex_frustums.GetBool() == false )
  140. return false;
  141. if( (g_pPortalRender->GetViewRecursionLevel() == 0) &&
  142. ( (ptCurrentViewOrigin - m_ptOrigin).LengthSqr() < (m_fHalfHeight * m_fHalfHeight) ) )//FIXME: Player closeness check might need reimplementation
  143. {
  144. //calculations are most likely going to be completely useless, return nothing
  145. return false;
  146. }
  147. if( m_pLinkedPortal == NULL )
  148. return false;
  149. if( m_vForward.Dot( ptCurrentViewOrigin ) <= m_InternallyMaintainedData.m_fPlaneDist )
  150. return false; //looking at portal backface
  151. return CalcFrustumThroughPolygon( m_InternallyMaintainedData.m_ptCorners, 4, ptCurrentViewOrigin, OutputFrustum );
  152. }
  153. ConVar cl_showcomplexfrustum( "cl_showcomplexfrustum", "0" );
  154. bool CPortalRenderable_FlatBasic::CalcFrustumThroughPolygon( const Vector *pPolyVertices, int iPolyVertCount, const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
  155. {
  156. int iViewRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
  157. int iNextViewRecursionLevel = iViewRecursionLevel + 1;
  158. Vector vTransformedViewOrigin = m_matrixThisToLinked * ptCurrentViewOrigin;
  159. VPlane *pInputFrustumPlanes = g_pPortalRender->GetRecursiveViewComplexFrustums( iViewRecursionLevel ).Base();
  160. int iInputFrustumPlanes = g_pPortalRender->GetRecursiveViewComplexFrustums( iViewRecursionLevel ).Count();
  161. Assert( iInputFrustumPlanes > 0 );
  162. Vector *pClippedVerts;
  163. int iClippedVertCount;
  164. {
  165. //clip the polygon by the input frustum
  166. int iAllocSize = iPolyVertCount + iInputFrustumPlanes;
  167. Vector *pWorkVerts[2];
  168. pWorkVerts[0] = (Vector *)stackalloc( sizeof( Vector ) * iAllocSize * 2 ); //possible to add 1 point per cut, iPolyVertCount starting points, iInputFrustumPlaneCount cuts
  169. pWorkVerts[1] = pWorkVerts[0] + iAllocSize;
  170. //clip by first plane and put output into pInVerts
  171. iClippedVertCount = ClipPolyToPlane( (Vector *)pPolyVertices, iPolyVertCount, pWorkVerts[0], pInputFrustumPlanes[0].m_Normal, pInputFrustumPlanes[0].m_Dist, 0.01f );
  172. //clip by other planes and flipflop in and out pointers
  173. for( int i = 1; i != iInputFrustumPlanes; ++i )
  174. {
  175. if( iClippedVertCount < 3 )
  176. return false; //nothing left in the frustum
  177. iClippedVertCount = ClipPolyToPlane( pWorkVerts[(i & 1) ^ 1], iClippedVertCount, pWorkVerts[i & 1], pInputFrustumPlanes[i].m_Normal, pInputFrustumPlanes[i].m_Dist, 0.01f );
  178. }
  179. if( iClippedVertCount < 3 )
  180. return false; //nothing left in the frustum
  181. pClippedVerts = pWorkVerts[iInputFrustumPlanes & 1];
  182. }
  183. for( int i = 0; i != iClippedVertCount; ++i )
  184. {
  185. pClippedVerts[i] = m_matrixThisToLinked * pClippedVerts[i];
  186. }
  187. VPlane plane_NearZ, plane_FarZ;
  188. //Near Z
  189. plane_NearZ.Init( m_pLinkedPortal->m_vForward, m_pLinkedPortal->m_InternallyMaintainedData.m_fPlaneDist );
  190. //Far Z
  191. {
  192. Vector vNormal = m_matrixThisToLinked.ApplyRotation( pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Normal );
  193. Vector ptOnPlane = pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Dist * pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Normal;
  194. plane_FarZ.Init( vNormal, vNormal.Dot( m_matrixThisToLinked * ptOnPlane ) );
  195. }
  196. VPlane *pOutputFrustumPlanes = (VPlane *)stackalloc( sizeof( VPlane ) * MAX( iClippedVertCount, 4 ) );
  197. //calculate and store the complex frustum with an unbounded number of planes
  198. {
  199. int iComplexCount = UTIL_CalcFrustumThroughConvexPolygon( pClippedVerts, iClippedVertCount, vTransformedViewOrigin, NULL, 0, pOutputFrustumPlanes, iClippedVertCount, 0 );
  200. if( iComplexCount == 0 )
  201. return false;
  202. g_pPortalRender->GetRecursiveViewComplexFrustums( iNextViewRecursionLevel ).SetCount( iComplexCount + 2 ); //+2 for near and far z planes
  203. VPlane *pWritePlane = g_pPortalRender->GetRecursiveViewComplexFrustums( iNextViewRecursionLevel ).Base();
  204. /*for( int i = 0; i != iComplexCount; ++i )
  205. {
  206. Vector vPoint = m_matrixThisToLinked * (pOutputFrustumPlanes[i].m_Normal * pOutputFrustumPlanes[i].m_Dist);
  207. Vector vNormal = m_matrixThisToLinked.ApplyRotation( pOutputFrustumPlanes[i].m_Normal );
  208. pWritePlane->Init( vNormal, vNormal.Dot( vPoint ) );
  209. ++pWritePlane;
  210. }*/
  211. memcpy( pWritePlane, pOutputFrustumPlanes, sizeof( VPlane ) * iComplexCount );
  212. pWritePlane += iComplexCount;
  213. *pWritePlane = plane_NearZ;
  214. ++pWritePlane;
  215. *pWritePlane = plane_FarZ;
  216. }
  217. //calculate and store the simple frustum, limited to exactly 6 planes
  218. {
  219. int iSimpleCount = UTIL_CalcFrustumThroughConvexPolygon( pClippedVerts, iClippedVertCount, vTransformedViewOrigin, NULL, 0, pOutputFrustumPlanes, 4, 0 );
  220. VPlane *pWritePlane = &OutputFrustum[0];
  221. /*for( int i = 0; i != iSimpleCount; ++i )
  222. {
  223. Vector vPoint = m_matrixThisToLinked * (pOutputFrustumPlanes[i].m_Normal * pOutputFrustumPlanes[i].m_Dist);
  224. Vector vNormal = m_matrixThisToLinked.ApplyRotation( pOutputFrustumPlanes[i].m_Normal );
  225. pWritePlane->Init( vNormal, vNormal.Dot( vPoint ) );
  226. ++pWritePlane;
  227. }*/
  228. memcpy( pWritePlane, pOutputFrustumPlanes, sizeof( VPlane ) * iSimpleCount );
  229. pWritePlane += iSimpleCount;
  230. while( iSimpleCount < 4 )
  231. {
  232. //crap, just copy the near plane into would-be empty slots
  233. *pWritePlane = plane_NearZ;
  234. ++pWritePlane;
  235. ++iSimpleCount;
  236. }
  237. *pWritePlane = plane_NearZ;
  238. ++pWritePlane;
  239. *pWritePlane = plane_FarZ;
  240. }
  241. return true;
  242. }
  243. static void DrawFrustum( Frustum_t &frustum )
  244. {
  245. const int maxPoints = 8;
  246. int i;
  247. for( i = 0; i < FRUSTUM_NUMPLANES; i++ )
  248. {
  249. Vector points[maxPoints];
  250. Vector points2[maxPoints];
  251. Vector normal;
  252. float dist;
  253. frustum.GetPlane( i, &normal, &dist );
  254. int numPoints = PolyFromPlane( points, normal, dist );
  255. Assert( numPoints <= maxPoints );
  256. Vector *in, *out;
  257. in = points;
  258. out = points2;
  259. int j;
  260. for( j = 0; j < FRUSTUM_NUMPLANES; j++ )
  261. {
  262. if( i == j )
  263. {
  264. continue;
  265. }
  266. frustum.GetPlane( j, &normal, &dist );
  267. numPoints = ClipPolyToPlane( in, numPoints, out, normal, dist );
  268. Assert( numPoints <= maxPoints );
  269. V_swap( in, out );
  270. }
  271. int c;
  272. for( c = 0; c < numPoints; c++ )
  273. {
  274. debugoverlay->AddLineOverlay( in[c], in[(c+1)%numPoints], 0, 255, 0, false, -1 );
  275. }
  276. }
  277. }
  278. ConVar r_drawportalfrustum( "r_drawportalfrustum", "0" );
  279. ConVar r_lockportalfrustum( "r_lockportalfrustum", "0" );
  280. void CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView )
  281. {
  282. VPROF( "CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer" );
  283. if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
  284. return;
  285. VPROF_INCREMENT_COUNTER( "PortalRenders", 1 );
  286. Frustum FrustumBackup;
  287. memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
  288. Frustum seeThroughFrustum;
  289. bool bUseSeeThroughFrustum;
  290. bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
  291. if ( r_drawportalfrustum.GetBool() )
  292. {
  293. static Frustum_t tmpFrustum;
  294. if ( !r_lockportalfrustum.GetBool() )
  295. {
  296. tmpFrustum.SetPlanes( seeThroughFrustum );
  297. }
  298. if ( bUseSeeThroughFrustum )
  299. {
  300. DrawFrustum( tmpFrustum );
  301. }
  302. }
  303. Vector vCameraForward;
  304. AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
  305. // Setup fog state for the camera.
  306. Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
  307. Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
  308. Vector ptRemotePortalPosition = m_pLinkedPortal->m_ptOrigin;
  309. Vector vRemotePortalForward = m_pLinkedPortal->m_vForward;
  310. CViewSetup portalView = cameraView;
  311. if( portalView.zNear < 1.0f )
  312. portalView.zNear = 1.0f;
  313. QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
  314. portalView.origin = ptPOVOrigin;
  315. portalView.angles = qPOVAngles;
  316. VMatrix matCurrentView;
  317. if( cameraView.m_bCustomViewMatrix )
  318. {
  319. matCurrentView.CopyFrom3x4( cameraView.m_matCustomViewMatrix );
  320. }
  321. else
  322. {
  323. matCurrentView.Identity();
  324. //generate the view matrix for the existing position and angle, then wedge our portal matrix onto it as a world transformation that prepends the view
  325. MatrixRotate( matCurrentView, Vector( 1, 0, 0 ), -cameraView.angles[2] );
  326. MatrixRotate( matCurrentView, Vector( 0, 1, 0 ), -cameraView.angles[0] );
  327. MatrixRotate( matCurrentView, Vector( 0, 0, 1 ), -cameraView.angles[1] );
  328. MatrixTranslate( matCurrentView, -cameraView.origin );
  329. }
  330. VMatrix matTemp = matCurrentView * m_pLinkedPortal->m_matrixThisToLinked;
  331. portalView.m_matCustomViewMatrix = matTemp.As3x4();
  332. portalView.m_bCustomViewMatrix = true;
  333. CopyToCurrentView( pViewRender, portalView );
  334. CMatRenderContextPtr pRenderContext( materials );
  335. //if we look through multiple unique pairs of portals, we have to take care not to clip too much
  336. bool bReplaceOldPortalClipPlane = (g_pPortalRender->GetViewRecursionLevel() != 0) && (dynamic_cast<CPortalRenderable_FlatBasic *>(g_pPortalRender->GetCurrentViewExitPortal()) != NULL);
  337. Vector4D vClipPlaneToRestore( 0.0f, 0.0f, 0.0f, 0.0f );
  338. Assert( ( g_pPortalRender->GetViewRecursionLevel() == 0 ) ? ( ms_clipPlaneStack.Count() == 0 ) : true );
  339. {
  340. Vector4D vCustomClipPlane;
  341. vCustomClipPlane.x = vRemotePortalForward.x;
  342. vCustomClipPlane.y = vRemotePortalForward.y;
  343. vCustomClipPlane.z = vRemotePortalForward.z;
  344. vCustomClipPlane.w = vRemotePortalForward.Dot( ptRemotePortalPosition ) - 2.0f; //moving it back a smidge to eliminate visual artifacts for half-in objects
  345. if ( g_pMaterialSystemHardwareConfig->UseFastClipping() )
  346. {
  347. // We need to make sure the clip plane never goes behind the eye position, as that would result in a degenerate projection matrix.
  348. // Note: This clip plane is for the distant side of the portal, so the camera is supposed to stay behind this plane (flCamDist should be negative).
  349. float flCamDist = DotProduct( cameraView.origin, vRemotePortalForward ) - vCustomClipPlane.w;
  350. static const float CAMERA_DIST_EPSILON = 1.0f;
  351. if ( flCamDist > -CAMERA_DIST_EPSILON )
  352. {
  353. float flClipPlaneCorrectionOffset = flCamDist + CAMERA_DIST_EPSILON;
  354. vCustomClipPlane.w += flClipPlaneCorrectionOffset;
  355. }
  356. }
  357. if( bReplaceOldPortalClipPlane )
  358. {
  359. pRenderContext->PopCustomClipPlane(); //HACKHACK: We really only want to remove the clip plane of the current portal view. This assumes we're the only ones leaving clip planes on the stack
  360. ms_clipPlaneStack.Pop( vClipPlaneToRestore );
  361. }
  362. pRenderContext->PushCustomClipPlane( vCustomClipPlane.Base() );
  363. ms_clipPlaneStack.Push( Vector4D( vCustomClipPlane ) );
  364. }
  365. shadowmgr->PushFlashlightScissorBounds();
  366. {
  367. ViewCustomVisibility_t customVisibility;
  368. m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
  369. CMatRenderContextPtr pRenderContext( materials );
  370. render->Push3DView( pRenderContext, portalView, 0, NULL, pViewRender->GetFrustum() );
  371. {
  372. if( bUseSeeThroughFrustum)
  373. memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
  374. render->OverrideViewFrustum( pViewRender->GetFrustum() );
  375. SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() + 1 );
  376. CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
  377. CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
  378. SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
  379. //DRAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  380. {
  381. PIXEVENT( pRenderContext, "PortalRender" );
  382. ViewDrawScene_PortalStencil( pViewRender, portalView, &customVisibility );
  383. }
  384. SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
  385. if( m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration && (g_pPortalRender->GetRemainingPortalViewDepth() == 1) )
  386. {
  387. //save the view matrix for usage with the depth doubler.
  388. //It's important that we do this AFTER using the depth doubler this frame to compensate for the fact that the front buffer is 1 frame behind the current view matrix
  389. //otherwise we get a lag effect when the player changes their viewing angles
  390. pRenderContext->GetMatrix( MATERIAL_VIEW, &m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
  391. }
  392. SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() - 1 );
  393. }
  394. render->PopView( pRenderContext, pViewRender->GetFrustum() );
  395. //restore old frustum
  396. memcpy( pViewRender->GetFrustum(), FrustumBackup, sizeof( Frustum ) );
  397. render->OverrideViewFrustum( FrustumBackup );
  398. }
  399. shadowmgr->PopFlashlightScissorBounds();
  400. pRenderContext->PopCustomClipPlane();
  401. ms_clipPlaneStack.Pop(); // This pops my own clip plane
  402. if( bReplaceOldPortalClipPlane )
  403. {
  404. pRenderContext->PushCustomClipPlane( vClipPlaneToRestore.Base() );
  405. ms_clipPlaneStack.Push( vClipPlaneToRestore );
  406. }
  407. //restore old vis data
  408. CopyToCurrentView( pViewRender, cameraView );
  409. }
  410. void CPortalRenderable_FlatBasic::RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView )
  411. {
  412. if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
  413. return;
  414. float fPixelVisibilty = g_pPortalRender->GetPixelVisilityForPortalSurface( this );
  415. if( (fPixelVisibilty >= 0.0f) && (fPixelVisibilty <= PORTALRENDERABLE_FLATBASIC_MINPIXELVIS) )
  416. return;
  417. ITexture *pRenderTarget;
  418. if( m_bIsPortal2 )
  419. pRenderTarget = portalrendertargets->GetPortal2Texture();
  420. else
  421. pRenderTarget = portalrendertargets->GetPortal1Texture();
  422. // Require that we have render textures for drawing
  423. AssertMsg( pRenderTarget, "Portal render targets not initialized properly" );
  424. // We're about to dereference this, so just bail if we can't
  425. if ( !pRenderTarget )
  426. return;
  427. CMatRenderContextPtr pRenderContext( materials );
  428. Vector vCameraForward;
  429. AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
  430. Frustum seeThroughFrustum;
  431. bool bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
  432. // Setup fog state for the camera.
  433. Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
  434. Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
  435. Vector vCameraToPortal = m_ptOrigin - cameraView.origin;
  436. CViewSetup portalView = cameraView;
  437. Frustum frustumBackup;
  438. memcpy( frustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
  439. QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
  440. portalView.width = pRenderTarget->GetActualWidth();
  441. portalView.height = pRenderTarget->GetActualHeight();
  442. portalView.x = 0;
  443. portalView.y = 0;
  444. portalView.origin = ptPOVOrigin;
  445. portalView.angles = qPOVAngles;
  446. portalView.fov = cameraView.fov;
  447. portalView.m_bOrtho = false;
  448. portalView.m_flAspectRatio = (float)cameraView.width / (float)cameraView.height; //use the screen aspect ratio, 0.0f doesn't work as advertised
  449. //pRenderContext->Flush( false );
  450. float fCustomClipPlane[4];
  451. fCustomClipPlane[0] = m_pLinkedPortal->m_vForward.x;
  452. fCustomClipPlane[1] = m_pLinkedPortal->m_vForward.y;
  453. fCustomClipPlane[2] = m_pLinkedPortal->m_vForward.z;
  454. fCustomClipPlane[3] = m_pLinkedPortal->m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - (m_pLinkedPortal->m_vForward * 2.0f) ); //moving it back a smidge to eliminate visual artifacts for half-in objects
  455. pRenderContext->PushCustomClipPlane( fCustomClipPlane );
  456. {
  457. render->Push3DView( pRenderContext, portalView, VIEW_CLEAR_DEPTH, pRenderTarget, pViewRender->GetFrustum() );
  458. {
  459. ViewCustomVisibility_t customVisibility;
  460. m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
  461. SetRemainingViewDepth( 0 );
  462. SetViewRecursionLevel( 1 );
  463. CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
  464. CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
  465. SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
  466. bool bDrew3dSkybox = false;
  467. SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE;
  468. // if the 3d skybox world is drawn, then don't draw the normal skybox
  469. int nClearFlags = 0;
  470. Draw3dSkyboxworld_Portal( pViewRender, portalView, nClearFlags, bDrew3dSkybox, nSkyboxVisible, pRenderTarget );
  471. if( bUseSeeThroughFrustum )
  472. {
  473. memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
  474. }
  475. render->OverrideViewFrustum( pViewRender->GetFrustum() );
  476. pRenderContext->EnableUserClipTransformOverride( false );
  477. ViewDrawScene( pViewRender, bDrew3dSkybox, nSkyboxVisible, portalView, nClearFlags, (view_id_t)g_pPortalRender->GetCurrentViewId(), false, 0, &customVisibility );
  478. SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
  479. SetRemainingViewDepth( 1 );
  480. SetViewRecursionLevel( 0 );
  481. memcpy( pViewRender->GetFrustum(), frustumBackup, sizeof( Frustum ) );
  482. render->OverrideViewFrustum( pViewRender->GetFrustum() );
  483. }
  484. render->PopView( pRenderContext, pViewRender->GetFrustum() );
  485. }
  486. pRenderContext->PopCustomClipPlane();
  487. //pRenderContext->Flush( false );
  488. CopyToCurrentView( pViewRender, cameraView );
  489. }
  490. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  491. // AddToVisAsExitPortal
  492. // input - pViewRender: pointer to the CViewRender class used to render this scene.
  493. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  494. void CPortalRenderable_FlatBasic::AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility )
  495. {
  496. if ( !pCustomVisibility )
  497. return;
  498. // Add four corners of the portal to the renderer as visibility origins
  499. for ( int i = 0; i < 4; ++i )
  500. {
  501. if( enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptCorners[i] ) != -1 )
  502. pCustomVisibility->AddVisOrigin( m_InternallyMaintainedData.m_ptCorners[i] );
  503. }
  504. // Add a point forward from the center of the portal in case the corners are outside of the world
  505. if( enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptForwardOrigin ) != -1 )
  506. pCustomVisibility->AddVisOrigin( m_InternallyMaintainedData.m_ptForwardOrigin );
  507. // Specify which leaf to use for area portal culling
  508. pCustomVisibility->ForceVisOverride( m_InternallyMaintainedData.m_VisData );
  509. pCustomVisibility->ForceViewLeaf( m_InternallyMaintainedData.m_iViewLeaf );
  510. }
  511. void CPortalRenderable_FlatBasic::DrawStencilMask( IMatRenderContext *pRenderContext )
  512. {
  513. DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
  514. DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
  515. }
  516. void CPortalRenderable_FlatBasic::DrawPostStencilFixes( IMatRenderContext *pRenderContext )
  517. {
  518. //fast clipping may have hosed depth, reset it
  519. if ( !r_portal_fastpath.GetBool() )
  520. {
  521. // TODO: Figure out if I actually need to do this
  522. pRenderContext->ClearBuffersObeyStencil( false, true );
  523. }
  524. //replace the fog we overwrote
  525. //RenderFogQuad(); //technically the fog quad is incorrect for all types of fog. But offers no usefulness when the local fog is <= the remote fog
  526. //replace depth
  527. DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
  528. DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
  529. }
  530. void CPortalRenderable_FlatBasic::DrawPortal( IMatRenderContext *pRenderContext )
  531. {
  532. //stencil-based rendering
  533. if( g_pPortalRender->GetCurrentViewExitPortal() != this )
  534. {
  535. if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
  536. && (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
  537. && (g_pPortalRender->GetViewRecursionLevel() > 1) )
  538. {
  539. DrawDepthDoublerMesh( pRenderContext );
  540. }
  541. }
  542. }
  543. int CPortalRenderable_FlatBasic::BindPortalMaterial( IMatRenderContext *pRenderContext, int nPassIndex, bool *pAllowRingMeshOptimizationOut )
  544. {
  545. VPROF_BUDGET( __FUNCTION__, "FlatBasic::BindPortalMaterial" );
  546. *pAllowRingMeshOptimizationOut = true;
  547. //stencil-based rendering
  548. if( g_pPortalRender->GetCurrentViewExitPortal() != this )
  549. {
  550. if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
  551. && (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
  552. && (g_pPortalRender->GetViewRecursionLevel() > 1)
  553. && (g_pPortalRender->GetCurrentViewEntryPortal() == this) )
  554. {
  555. if( CPortalRender::DepthDoublerPIPDisableCheck() )
  556. return 0;
  557. if ( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler.IsValid() )
  558. {
  559. IMaterialVar *pVar = g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &g_pPortalRender->m_MaterialsAccess.m_nDepthDoubleViewMatrixVarCache );
  560. if ( pVar != NULL )
  561. {
  562. pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
  563. }
  564. }
  565. pRenderContext->Bind( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler, GetClientRenderable() );
  566. *pAllowRingMeshOptimizationOut = false;
  567. return 1;
  568. }
  569. }
  570. return 0;
  571. }
  572. void CPortalRenderable_FlatBasic::DrawDepthDoublerMesh( IMatRenderContext *pRenderContext, float fForwardOffsetModifier )
  573. {
  574. if( CPortalRender::DepthDoublerPIPDisableCheck() )
  575. return;
  576. if ( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler.IsValid() )
  577. {
  578. IMaterialVar *pVar = g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &g_pPortalRender->m_MaterialsAccess.m_nDepthDoubleViewMatrixVarCache );
  579. if ( pVar != NULL )
  580. {
  581. pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
  582. }
  583. }
  584. DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler, fForwardOffsetModifier );
  585. }
  586. bool CPortalRenderable_FlatBasic::ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup )
  587. {
  588. return ( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration) && (m_pLinkedPortal != NULL) );
  589. }
  590. void CPortalRenderable_FlatBasic::GetToolRecordingState( KeyValues *msg )
  591. {
  592. if ( !ToolsEnabled() )
  593. return;
  594. VPROF_BUDGET( "CPortalRenderable_FlatBasic::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
  595. BaseClass::GetToolRecordingState( msg );
  596. C_Prop_Portal *pLinkedPortal = static_cast<C_Prop_Portal*>( m_pLinkedPortal );
  597. static PortalRecordingState_t state;
  598. state.m_nPortalId = static_cast<C_Prop_Portal*>( this )->index;
  599. state.m_nLinkedPortalId = pLinkedPortal ? pLinkedPortal->index : -1;
  600. state.m_fHalfWidth = m_fHalfWidth;
  601. state.m_fHalfHeight = m_fHalfHeight;
  602. state.m_bIsPortal2 = m_bIsPortal2;
  603. state.m_portalType = "Flat Basic";
  604. msg->SetPtr( "portal", &state );
  605. RemoveEffects( EF_NOINTERP );
  606. }
  607. void CPortalRenderable_FlatBasic::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
  608. {
  609. int nLinkedPortalId = pKeyValues->GetInt( "linkedPortalId" );
  610. m_pLinkedPortal = nLinkedPortalId >= 0 ? (CPortalRenderable_FlatBasic *)FindRecordedPortal( nLinkedPortalId ) : NULL;
  611. if( m_pLinkedPortal )
  612. {
  613. m_pLinkedPortal->m_pLinkedPortal = this;
  614. }
  615. m_bIsPortal2 = pKeyValues->GetInt( "isPortal2" ) != 0;
  616. matrix3x4_t *pMat = (matrix3x4_t*)pKeyValues->GetPtr( "portalToWorld" );
  617. MatrixGetColumn( *pMat, 3, m_ptOrigin );
  618. MatrixGetColumn( *pMat, 0, m_vForward );
  619. MatrixGetColumn( *pMat, 1, m_vRight );
  620. MatrixGetColumn( *pMat, 2, m_vUp );
  621. m_vRight *= -1.0f;
  622. SetHalfSizes( pKeyValues->GetFloat( "halfWidth", 0.0f ), pKeyValues->GetFloat( "halfHeight", 0.0f ) );
  623. PortalMoved();
  624. UTIL_Portal_ComputeMatrix( this, m_pLinkedPortal );
  625. }
  626. extern ConVar mat_wireframe;
  627. static void DrawComplexPortalMesh_SubQuad( Vector &ptBottomLeft, Vector &vUp, Vector &vRight, float *fSubQuadRect, void *pBindEnt, const IMaterial *pMaterial, const VMatrix *pReplacementViewMatrixForTexCoords = NULL )
  628. {
  629. PortalMeshPoint_t Vertices[4];
  630. Vertices[0].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[3]);
  631. Vertices[0].texCoord.x = fSubQuadRect[2];
  632. Vertices[0].texCoord.y = fSubQuadRect[3];
  633. Vertices[1].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[1]);
  634. Vertices[1].texCoord.x = fSubQuadRect[2];
  635. Vertices[1].texCoord.y = fSubQuadRect[1];
  636. Vertices[2].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[1]);
  637. Vertices[2].texCoord.x = fSubQuadRect[0];
  638. Vertices[2].texCoord.y = fSubQuadRect[1];
  639. Vertices[3].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[3]);
  640. Vertices[3].texCoord.x = fSubQuadRect[0];
  641. Vertices[3].texCoord.y = fSubQuadRect[3];
  642. Clip_And_Render_Convex_Polygon( Vertices, 4, pMaterial, pBindEnt );
  643. }
  644. #define PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS 8
  645. #define PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS 6
  646. void CPortalRenderable_FlatBasic::DrawComplexPortalMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fForwardOffsetModifier ) //generates and draws the portal mesh (Needed for compatibility with fixed function rendering)
  647. {
  648. PortalMeshPoint_t BaseVertices[4];
  649. Vector ptBottomLeft = m_ptOrigin + (m_vForward * (fForwardOffsetModifier)) - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  650. Vector vScaledUp = m_vUp * (2.0f * m_fHalfHeight);
  651. Vector vScaledRight = m_vRight * (2.0f * m_fHalfWidth);
  652. VMatrix matView;
  653. pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
  654. float fSubQuadRect[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
  655. float fHeightBegin = 0.0f;
  656. for( int i = 0; i != PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS; ++i )
  657. {
  658. float fHeightEnd = fHeightBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS));
  659. fSubQuadRect[1] = fHeightBegin;
  660. fSubQuadRect[3] = fHeightEnd;
  661. float fWidthBegin = 0.0f;
  662. for( int j = 0; j != PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS; ++j )
  663. {
  664. float fWidthEnd = fWidthBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS));
  665. fSubQuadRect[0] = fWidthBegin;
  666. fSubQuadRect[2] = fWidthEnd;
  667. DrawComplexPortalMesh_SubQuad( ptBottomLeft, vScaledUp, vScaledRight, fSubQuadRect, GetClientRenderable(), pMaterial );
  668. fWidthBegin = fWidthEnd;
  669. }
  670. fHeightBegin = fHeightEnd;
  671. }
  672. //pRenderContext->Flush( false );
  673. }
  674. static void CreateRingMesh( CMeshBuilder &meshBuilder, int nSegs, float flHalfWidth, float flHalfHeight, float flInnerScale, int *pStartVertex, int *pIndexCountOut )
  675. {
  676. float vTangent[4] = { -1.0f, 0.0f, 0.0f, 1.0f };
  677. for ( int i = 0; i < nSegs; i++ )
  678. {
  679. float flRadians = 2.0f * M_PI * float( i ) / float( nSegs );
  680. Vector2D vUvOuter( sinf( flRadians), cosf( flRadians ) );
  681. Vector vPosOuter( -vUvOuter.x * flHalfWidth, -vUvOuter.y * flHalfHeight, 0.0f );
  682. Vector vPosInner( flInnerScale * vPosOuter );
  683. Vector2D vUvInner( flInnerScale * vUvOuter );
  684. vPosOuter *= 1.05f; // Scale factor that hides the poly edge on the portal ring effect
  685. vUvOuter *= 1.05f;
  686. vUvOuter = 0.5f * vUvOuter;
  687. vUvOuter.x += 0.5f;
  688. vUvOuter.y += 0.5f;
  689. vUvInner = 0.5f * vUvInner;
  690. vUvInner.x += 0.5f;
  691. vUvInner.y += 0.5f;
  692. meshBuilder.Position3fv( &vPosOuter.x );
  693. meshBuilder.TexCoord2f( 0, vUvOuter.x, vUvOuter.y );
  694. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  695. meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
  696. meshBuilder.Color4f( 1.0f, 1.0f, 1.0f, 1.0f );
  697. meshBuilder.UserData( vTangent );
  698. meshBuilder.AdvanceVertex();
  699. meshBuilder.Position3fv( &vPosInner.x );
  700. meshBuilder.TexCoord2f( 0, vUvInner.x, vUvInner.y );
  701. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  702. meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
  703. meshBuilder.Color4f( 1.0f, 1.0f, 1.0f, 1.0f );
  704. meshBuilder.UserData( vTangent );
  705. meshBuilder.AdvanceVertex();
  706. int nSegmentIndex = *pStartVertex + 2 * i;
  707. int nNextSegmentIndex = *pStartVertex + 2 * ( ( i + 1 ) % nSegs );
  708. meshBuilder.FastIndex( nSegmentIndex );
  709. meshBuilder.FastIndex( nSegmentIndex + 1 );
  710. meshBuilder.FastIndex( nNextSegmentIndex + 1);
  711. meshBuilder.FastIndex( nSegmentIndex );
  712. meshBuilder.FastIndex( nNextSegmentIndex + 1 );
  713. meshBuilder.FastIndex( nNextSegmentIndex );
  714. }
  715. *pStartVertex += nSegs * 2;
  716. *pIndexCountOut = nSegs * 6;
  717. }
  718. inline float ComputePointToPortalDistance( const Vector &vPt, const CPortalRenderable_FlatBasic *pPortal, const Vector &vAdjustedOrigin )
  719. {
  720. // Transform point into portal space
  721. matrix3x4_t matPortalToWorld( pPortal->m_vRight, pPortal->m_vUp, pPortal->m_vForward, vAdjustedOrigin );
  722. Vector vPtPortalSpace;
  723. VectorITransform( vPt, matPortalToWorld, vPtPortalSpace );
  724. // Compute distance from point to portal bounding rectangle
  725. Vector vClamped;
  726. vClamped.x = clamp( vPtPortalSpace.x, -pPortal->GetHalfWidth(), pPortal->GetHalfWidth() );
  727. vClamped.y = clamp( vPtPortalSpace.y, -pPortal->GetHalfHeight(), pPortal->GetHalfHeight() );
  728. vClamped.z = 0.0f;
  729. return ( vPtPortalSpace - vClamped ).Length();
  730. }
  731. IMesh *CPortalRenderable_FlatBasic::CreateMeshForPortals( IMatRenderContext *pRenderContext, int nPortalCount, CPortalRenderable **ppPortals,
  732. CUtlVector< ClampedPortalMeshRenderInfo_t > &clampedPortalMeshRenderInfos )
  733. {
  734. VPROF_BUDGET( "CreateMeshForPortals", "CreateMeshForPortals" );
  735. VertexFormat_t unionVertexFmt = (VertexFormat_t)VERTEX_POSITION | VERTEX_COLOR | VERTEX_NORMAL | VERTEX_TEXCOORD0_2D | VERTEX_USERDATA_SIZE(4) |
  736. VERTEX_TEXCOORD_SIZE( 0, 2 ) | VERTEX_TEXCOORD_SIZE( 1, 2 );
  737. CMeshBuilder meshBuilder;
  738. IMesh* pMesh = pRenderContext->GetDynamicMeshEx( unionVertexFmt );
  739. const int nRingMeshSegmentCount = 12;
  740. int nMaxTriangles = ( nPortalCount * 2 ) + ( nPortalCount * 10 ) + ( nRingMeshSegmentCount * 2 ); // One quad per portal, plus a near cap poly per portal worst case
  741. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 3 * nMaxTriangles, 3 * nMaxTriangles );
  742. Vector *pTmpVerts = (Vector*)stackalloc( nPortalCount * 4 * sizeof( Vector ) );
  743. Vector4D *pGhostColors = (Vector4D*)stackalloc( nPortalCount * sizeof( Vector4D ) );
  744. float *pMaxDecalOffsets = (float*)stackalloc( nPortalCount * sizeof( float ) );
  745. Vector vecDeltaRight, vecDeltaUp;
  746. float vTangent[4];
  747. const Vector vCameraPos( view->GetViewSetup()->origin );
  748. int nStartVertex = 0;
  749. // Create basic quad meshes
  750. for ( int i = 0; i < nPortalCount; i++ )
  751. {
  752. const CPortalRenderable_FlatBasic *pPortal = assert_cast< const CPortalRenderable_FlatBasic* >( ppPortals[i] );
  753. Vector ptCenter = pPortal->m_ptOrigin;
  754. // Math below implements this
  755. // verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  756. // verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  757. // verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  758. // verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  759. Vector *pVerts = pTmpVerts + ( 4 * i );
  760. VectorMultiply( pPortal->m_vRight, pPortal->m_fHalfWidth, vecDeltaRight );
  761. VectorMultiply( pPortal->m_vUp, pPortal->m_fHalfHeight, vecDeltaUp );
  762. VectorSubtract( ptCenter, vecDeltaUp, pVerts[0] );
  763. vecDeltaUp *= 2.0f;
  764. pVerts[0] += vecDeltaRight;
  765. vecDeltaRight *= 2.0f;
  766. VectorAdd( pVerts[0], vecDeltaUp, pVerts[1] );
  767. VectorSubtract( pVerts[0], vecDeltaRight, pVerts[2] );
  768. VectorSubtract( pVerts[1], vecDeltaRight, pVerts[3] );
  769. // Compute max decal offset. We don't want the decal to be offset past the camera position
  770. Vector vDir( vCameraPos - pPortal->m_ptOrigin );
  771. float flCameraDistToPortalPlane = DotProduct( vDir, pPortal->m_vForward );
  772. pMaxDecalOffsets[i] = clamp( flCameraDistToPortalPlane, 0.02f, 0.251f ) - 0.01f;
  773. Color clrPortal = UTIL_Portal_Color( pPortal->m_bIsPortal2 ? 2 : 1, pPortal->GetTeamNumber() );
  774. pGhostColors[i].x = float( clrPortal.r() ) / 255.0f;
  775. pGhostColors[i].y = float( clrPortal.g() ) / 255.0f;
  776. pGhostColors[i].z = float( clrPortal.b() ) / 255.0f;
  777. pGhostColors[i].w = pPortal->GetPortalGhostAlpha();
  778. vTangent[0] = -pPortal->m_vRight.x;
  779. vTangent[1] = -pPortal->m_vRight.y;
  780. vTangent[2] = -pPortal->m_vRight.z;
  781. vTangent[3] = 1.0f;
  782. meshBuilder.Position3fv( &pVerts[0].x );
  783. meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
  784. meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
  785. meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
  786. meshBuilder.Color4fv( &pGhostColors[i].x );
  787. meshBuilder.UserData( vTangent );
  788. meshBuilder.AdvanceVertex();
  789. meshBuilder.Position3fv( &pVerts[1].x );
  790. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  791. meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
  792. meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
  793. meshBuilder.Color4fv( &pGhostColors[i].x );
  794. meshBuilder.UserData( vTangent );
  795. meshBuilder.AdvanceVertex();
  796. meshBuilder.Position3fv( &pVerts[3].x );
  797. meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
  798. meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
  799. meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
  800. meshBuilder.Color4fv( &pGhostColors[i].x );
  801. meshBuilder.UserData( vTangent );
  802. meshBuilder.AdvanceVertex();
  803. meshBuilder.Position3fv( &pVerts[2].x );
  804. meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
  805. meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
  806. meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
  807. meshBuilder.Color4fv( &pGhostColors[i].x );
  808. meshBuilder.UserData( vTangent );
  809. meshBuilder.AdvanceVertex();
  810. meshBuilder.FastQuad( nStartVertex );
  811. nStartVertex += 4;
  812. }
  813. PortalMeshPoint_t portalQuad[16];
  814. int nInsideVertCount = 0;
  815. PortalMeshPoint_t insidePoly[16];
  816. // Make a frustum enclosing the pyramid between the camera position and the near plane.
  817. VPlane nearPlaneFrustum[5];
  818. V_memcpy( nearPlaneFrustum, view->GetFrustum(), 5 * sizeof( VPlane ) );
  819. // flip near plane
  820. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal;
  821. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist;
  822. clampedPortalMeshRenderInfos.SetCount( nPortalCount );
  823. int nStartIndex = 6 * nPortalCount;
  824. // Compute inverse view-projectio matrix
  825. VMatrix matView;
  826. VMatrix matProj;
  827. pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
  828. pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
  829. VMatrix matViewProj;
  830. MatrixMultiply( matProj, matView, matViewProj );
  831. VMatrix matInvViewProj;
  832. MatrixInverseGeneral( matViewProj, matInvViewProj );
  833. // Create meshes with a z-near-cap for portals that intersect the near plane
  834. for ( int i = 0; i < nPortalCount; i++ )
  835. {
  836. const CPortalRenderable_FlatBasic *pPortal = assert_cast< const CPortalRenderable_FlatBasic* >( ppPortals[i] );
  837. clampedPortalMeshRenderInfos[i].nStartIndex = -1;
  838. clampedPortalMeshRenderInfos[i].nIndexCount = -1;
  839. if ( R_CullSphere( nearPlaneFrustum, 5, &pPortal->m_ptOrigin, MAX( pPortal->m_fHalfWidth, pPortal->m_fHalfHeight ) ) )
  840. {
  841. // Portal definitely doesn't need a near plane cap
  842. continue;
  843. }
  844. static const float CLIP_NEARPLANE_OFFSET = 0.3f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
  845. static const float PROJECT_NEARPLANE_OFFSET = 0.01f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
  846. static const float PORTAL_DISTANCE_EPSILON = 0.4f;
  847. float flTweakedDecalOffset = pMaxDecalOffsets[i] + 0.05f;
  848. float flDistToPortal = ComputePointToPortalDistance( vCameraPos, pPortal, pPortal->m_ptOrigin + flTweakedDecalOffset * pPortal->m_vForward );
  849. if ( flDistToPortal < PORTAL_DISTANCE_EPSILON )
  850. {
  851. // We're too close to the portal to perform reliable clipping and reprojection to the near plane (the portal plane basically goes through the eye position.
  852. // Instead, we clip the camera near plane by the portal plane and use the result as our near cap poly.
  853. // Init portal quad with near plane. Offset a tiny bit from 1.0 to avoid precision errors that can leave a 1-pixel border around the screen uncovered by the quad.
  854. matInvViewProj.V3Mul( Vector( -1.01f, -1.01f, 0.0f ), portalQuad[0].vWorldSpacePosition );
  855. matInvViewProj.V3Mul( Vector( -1.01f, 1.01f, 0.0f ), portalQuad[1].vWorldSpacePosition );
  856. matInvViewProj.V3Mul( Vector( 1.01f, 1.01f, 0.0f ), portalQuad[2].vWorldSpacePosition );
  857. matInvViewProj.V3Mul( Vector( 1.01f, -1.01f, 0.0f ), portalQuad[3].vWorldSpacePosition );
  858. portalQuad[0].texCoord.Init( 0.5f, 0.5f );
  859. portalQuad[1].texCoord.Init( 0.5f, 0.5f );
  860. portalQuad[2].texCoord.Init( 0.5f, 0.5f );
  861. portalQuad[3].texCoord.Init( 0.5f, 0.5f );
  862. // Clip against portal plane
  863. float flPortalPlaneDist = DotProduct( pPortal->m_vForward, pPortal->m_ptOrigin + flTweakedDecalOffset * pPortal->m_vForward );
  864. ClipPortalPolyToPlane( portalQuad, 4, insidePoly, &nInsideVertCount, -pPortal->m_vForward, -flPortalPlaneDist );
  865. }
  866. else
  867. {
  868. // Clip portal quad against view frustum.
  869. portalQuad[0].vWorldSpacePosition = pTmpVerts[ 4 * i ] + flTweakedDecalOffset * pPortal->m_vForward;
  870. portalQuad[1].vWorldSpacePosition = pTmpVerts[ 4 * i + 1 ] + flTweakedDecalOffset * pPortal->m_vForward;
  871. portalQuad[2].vWorldSpacePosition = pTmpVerts[ 4 * i + 3 ] + flTweakedDecalOffset * pPortal->m_vForward;
  872. portalQuad[3].vWorldSpacePosition = pTmpVerts[ 4 * i + 2 ] + flTweakedDecalOffset * pPortal->m_vForward;
  873. portalQuad[0].texCoord.Init( 0.5f, 0.5f );
  874. portalQuad[1].texCoord.Init( 0.5f, 0.5f );
  875. portalQuad[2].texCoord.Init( 0.5f, 0.5f );
  876. portalQuad[3].texCoord.Init( 0.5f, 0.5f );
  877. /*
  878. portalQuad[0].texCoord.Init( 0.0f, 1.0f );
  879. portalQuad[1].texCoord.Init( 0.0f, 0.0f );
  880. portalQuad[2].texCoord.Init( 1.0f, 0.0f );
  881. portalQuad[3].texCoord.Init( 1.0f, 1.0f );
  882. */
  883. // Clip against flipped near plane so that we get the part of the portal quad that would be clipped by the near plane. Offset the near plane so that our cap poly will cover up
  884. // the clip intersection line.
  885. bool bClippedSomething = ClipPortalPolyToPlane( portalQuad, 4, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal,
  886. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - CLIP_NEARPLANE_OFFSET );
  887. if ( !bClippedSomething )
  888. {
  889. continue;
  890. }
  891. // Clip against the four remaining frustum planes. Offset the planes a tiny bit so that we have some slack when reprojecting the clipped verts back onto the near plane.
  892. // This hack is only acceptable because we're not passing the proper texture coordinates for the quad anyways.
  893. ClipPortalPolyToPlane( insidePoly, nInsideVertCount, portalQuad, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Normal, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Dist - 0.01f );
  894. ClipPortalPolyToPlane( portalQuad, nInsideVertCount, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Normal, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Dist - 0.01f );
  895. ClipPortalPolyToPlane( insidePoly, nInsideVertCount, portalQuad, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_TOP ].m_Normal, nearPlaneFrustum[ FRUSTUM_TOP ].m_Dist - 0.01f );
  896. ClipPortalPolyToPlane( portalQuad, nInsideVertCount, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Normal, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Dist - 0.01f );
  897. }
  898. if ( nInsideVertCount < 3 )
  899. {
  900. continue;
  901. }
  902. ProjectPortalPolyToPlane( insidePoly, nInsideVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - PROJECT_NEARPLANE_OFFSET, vCameraPos );
  903. vTangent[0] = -pPortal->m_vRight.x;
  904. vTangent[1] = -pPortal->m_vRight.y;
  905. vTangent[2] = -pPortal->m_vRight.z;
  906. vTangent[3] = 1.0f;
  907. nInsideVertCount = ( nInsideVertCount < 3 ) ? 0 : nInsideVertCount;
  908. int nInsideTriCount = MAX( nInsideVertCount - 2, 0 );
  909. clampedPortalMeshRenderInfos[i].nStartIndex = nStartIndex;
  910. clampedPortalMeshRenderInfos[i].nIndexCount = nInsideTriCount * 3;
  911. for ( int j = 0; j < nInsideVertCount; j++ )
  912. {
  913. meshBuilder.Position3fv( &( insidePoly[j].vWorldSpacePosition.x ) );
  914. meshBuilder.TexCoord2fv( 0, &( insidePoly[j].texCoord.x ) );
  915. meshBuilder.TexCoord2f( 1, 0.0f, 0.0f ); // No decal offset for near plane cap poly
  916. meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
  917. meshBuilder.Color4fv( &pGhostColors[i].x );
  918. meshBuilder.UserData( vTangent );
  919. meshBuilder.AdvanceVertex();
  920. }
  921. for ( int j = 0; j < nInsideTriCount; j++ )
  922. {
  923. meshBuilder.FastIndex( nStartVertex );
  924. meshBuilder.FastIndex( nStartVertex + j + 1 );
  925. meshBuilder.FastIndex( nStartVertex + j + 2 );
  926. }
  927. nStartVertex += nInsideVertCount;
  928. nStartIndex += clampedPortalMeshRenderInfos[i].nIndexCount;
  929. }
  930. int nRingMeshIndexCount = 0;
  931. CreateRingMesh( meshBuilder, nRingMeshSegmentCount, 32.0f, 56.0f, 0.65f, &nStartVertex, &nRingMeshIndexCount );
  932. clampedPortalMeshRenderInfos.AddToTail();
  933. clampedPortalMeshRenderInfos.Tail().nStartIndex = nStartIndex;
  934. clampedPortalMeshRenderInfos.Tail().nIndexCount = nRingMeshIndexCount;
  935. meshBuilder.End();
  936. return pMesh;
  937. }
  938. bool CPortalRenderable_FlatBasic::ComputeClipSpacePortalCorners( Vector4D *pClipSpacePortalCornersOut, const VMatrix &matViewProj ) const
  939. {
  940. // Math below implements this
  941. // verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  942. // verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  943. // verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  944. // verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  945. Vector4D verts[4];
  946. Vector vecDeltaRight, vecDeltaUp;
  947. VectorMultiply( m_vRight, m_fHalfWidth, vecDeltaRight );
  948. VectorMultiply( m_vUp, m_fHalfHeight, vecDeltaUp );
  949. VectorSubtract( m_ptOrigin.Base(), vecDeltaUp.Base(), verts[0].Base() );
  950. vecDeltaUp *= 2.0f;
  951. VectorAdd( verts[0].Base(), vecDeltaRight.Base(), verts[0].Base() );
  952. vecDeltaRight *= 2.0f;
  953. VectorAdd( verts[0].Base(), vecDeltaUp.Base(), verts[1].Base() );
  954. VectorSubtract( verts[0].Base(), vecDeltaRight.Base(), verts[2].Base() );
  955. VectorSubtract( verts[1].Base(), vecDeltaRight.Base(), verts[3].Base() );
  956. bool bClipsNearPlane = false;
  957. for ( int i = 0; i < 4; i++ )
  958. {
  959. verts[i].w = 1.0f;
  960. matViewProj.V4Mul( verts[i], pClipSpacePortalCornersOut[i] );
  961. bClipsNearPlane |= ( pClipSpacePortalCornersOut[i].z <= 0.0f );
  962. }
  963. return !bClipsNearPlane;
  964. }
  965. void CPortalRenderable_FlatBasic::DrawSimplePortalMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fForwardOffsetModifier, float flAlpha, const Vector *pVertexColor )
  966. {
  967. VPROF_BUDGET( "DrawSimplePortalMesh", "DrawSimplePortalMesh" );
  968. pRenderContext->Bind( (IMaterial *)pMaterial, GetClientRenderable() );
  969. // This can depend on the Bind command above, so keep this after!
  970. UpdateFrontBufferTexturesForMaterial( (IMaterial *)pMaterial );
  971. pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
  972. pRenderContext->PushMatrix();
  973. pRenderContext->LoadIdentity();
  974. // Disable offset modifier, put it in shader code in portal and portal_refract shaders instead
  975. // FIXME: Remove this from the code
  976. fForwardOffsetModifier = 0;
  977. Vector ptCenter = m_ptOrigin + (m_vForward * fForwardOffsetModifier);
  978. // Math below implements this
  979. // verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  980. // verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  981. // verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  982. // verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  983. Vector verts[4];
  984. Vector vecDeltaRight, vecDeltaUp;
  985. VectorMultiply( m_vRight, m_fHalfWidth, vecDeltaRight );
  986. VectorMultiply( m_vUp, m_fHalfHeight, vecDeltaUp );
  987. VectorSubtract( ptCenter, vecDeltaUp, verts[0] );
  988. vecDeltaUp *= 2.0f;
  989. verts[0] += vecDeltaRight;
  990. vecDeltaRight *= 2.0f;
  991. VectorAdd( verts[0], vecDeltaUp, verts[1] );
  992. VectorSubtract( verts[0], vecDeltaRight, verts[2] );
  993. VectorSubtract( verts[1], vecDeltaRight, verts[3] );
  994. float pColor[4] = { 1.0f, 1.0f, 1.0f, flAlpha };
  995. if( pVertexColor )
  996. {
  997. memcpy( pColor, pVertexColor, sizeof( float ) * 3 );
  998. }
  999. float vTangent[4] = { -m_vRight.x, -m_vRight.y, -m_vRight.z, 1.0f };
  1000. CMeshBuilder meshBuilder;
  1001. IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
  1002. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
  1003. meshBuilder.Position3fv( &verts[0].x );
  1004. meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
  1005. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  1006. meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
  1007. meshBuilder.Color4fv( pColor );
  1008. meshBuilder.UserData( vTangent );
  1009. meshBuilder.AdvanceVertex();
  1010. meshBuilder.Position3fv( &verts[1].x );
  1011. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  1012. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  1013. meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
  1014. meshBuilder.Color4fv( pColor );
  1015. meshBuilder.UserData( vTangent );
  1016. meshBuilder.AdvanceVertex();
  1017. meshBuilder.Position3fv( &verts[2].x );
  1018. meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
  1019. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  1020. meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
  1021. meshBuilder.Color4fv( pColor );
  1022. meshBuilder.UserData( vTangent );
  1023. meshBuilder.AdvanceVertex();
  1024. meshBuilder.Position3fv( &verts[3].x );
  1025. meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
  1026. meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
  1027. meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
  1028. meshBuilder.Color4fv( pColor );
  1029. meshBuilder.UserData( vTangent );
  1030. meshBuilder.AdvanceVertex();
  1031. meshBuilder.End();
  1032. pMesh->Draw();
  1033. if( mat_wireframe.GetBool() )
  1034. {
  1035. pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_Wireframe, (CPortalRenderable*)this );
  1036. IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
  1037. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
  1038. meshBuilder.Position3fv( &verts[0].x );
  1039. meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
  1040. meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
  1041. meshBuilder.AdvanceVertex();
  1042. meshBuilder.Position3fv( &verts[1].x );
  1043. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  1044. meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
  1045. meshBuilder.AdvanceVertex();
  1046. meshBuilder.Position3fv( &verts[2].x );
  1047. meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
  1048. meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
  1049. meshBuilder.AdvanceVertex();
  1050. meshBuilder.Position3fv( &verts[3].x );
  1051. meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
  1052. meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
  1053. meshBuilder.AdvanceVertex();
  1054. meshBuilder.End();
  1055. pMesh->Draw();
  1056. }
  1057. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1058. pRenderContext->PopMatrix();
  1059. }
  1060. void CPortalRenderable_FlatBasic::DrawRenderFixMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fFrontClipDistance )
  1061. {
  1062. if( g_pPortalRender->GetViewRecursionLevel() != 0 )
  1063. return; //a render fix should only ever be necessary in the primary view
  1064. Vector ptCameraOrigin = CurrentViewOrigin();
  1065. Vector vPortalCenterToCamera = ptCameraOrigin - m_ptOrigin;
  1066. if( (vPortalCenterToCamera.Dot( m_vForward ) < -1.0f) ) //camera coplanar (to 1.0 units) or in front of portal plane
  1067. return;
  1068. if( vPortalCenterToCamera.LengthSqr() < (m_fHalfHeight * m_fHalfHeight) ) //FIXME: Player closeness check might need reimplementation
  1069. {
  1070. float fOldDist = m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist;
  1071. float fCameraDist = m_vForward.Dot(ptCameraOrigin - m_ptOrigin);
  1072. if( fFrontClipDistance > fCameraDist ) //never clip further out than the camera, we can see into the garbage space of the portal view's texture
  1073. fFrontClipDistance = fCameraDist;
  1074. m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist -= fFrontClipDistance;
  1075. Internal_DrawRenderFixMesh( pRenderContext, pMaterial );
  1076. m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist = fOldDist;
  1077. }
  1078. }
  1079. void CPortalRenderable_FlatBasic::Internal_DrawRenderFixMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial )
  1080. {
  1081. PortalMeshPoint_t WorkVertices[12];
  1082. PortalMeshPoint_t clippedVerts[12];
  1083. int nClippedVertCount = 0;
  1084. pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
  1085. pRenderContext->PushMatrix();
  1086. pRenderContext->LoadIdentity();
  1087. // Compute inverse view-projection matrix
  1088. VMatrix matView;
  1089. VMatrix matProj;
  1090. pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
  1091. pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
  1092. VMatrix matViewProj;
  1093. MatrixMultiply( matProj, matView, matViewProj );
  1094. VMatrix matInvViewProj;
  1095. MatrixInverseGeneral( matViewProj, matInvViewProj );
  1096. VPlane nearPlaneFrustum[5];
  1097. V_memcpy( nearPlaneFrustum, view->GetFrustum(), 5 * sizeof( VPlane ) );
  1098. // flip near plane
  1099. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal;
  1100. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist;
  1101. static const float PORTAL_OFFSET = 0.275f;
  1102. static const float CLIP_NEARPLANE_OFFSET = 0.3f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
  1103. static const float PROJECT_NEARPLANE_OFFSET = 0.01f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
  1104. static const float PORTAL_DISTANCE_EPSILON = 0.4f;
  1105. const Vector vCameraPos( view->GetViewSetup()->origin );
  1106. Vector vOffsetOrigin( m_ptOrigin + PORTAL_OFFSET * m_vForward );
  1107. float flDistToPortal = ComputePointToPortalDistance( vCameraPos, this, vOffsetOrigin );
  1108. if ( flDistToPortal < PORTAL_DISTANCE_EPSILON )
  1109. {
  1110. // We're too close to the portal to perform reliable clipping and reprojection to the near plane (the portal plane basically goes through the eye position.
  1111. // Instead, we clip the camera near plane by the portal plane and use the result as our near cap poly.
  1112. // Init portal quad with near plane. Offset a tiny bit from 1.0 to avoid precision errors that can leave a 1-pixel border around the screen uncovered by the quad.
  1113. matInvViewProj.V3Mul( Vector( -1.01f, -1.01f, 0.0f ), WorkVertices[0].vWorldSpacePosition );
  1114. matInvViewProj.V3Mul( Vector( 1.01f, -1.01f, 0.0f ), WorkVertices[1].vWorldSpacePosition );
  1115. matInvViewProj.V3Mul( Vector( 1.01f, 1.01f, 0.0f ), WorkVertices[2].vWorldSpacePosition );
  1116. matInvViewProj.V3Mul( Vector( -1.01f, 1.01f, 0.0f ), WorkVertices[3].vWorldSpacePosition );
  1117. WorkVertices[0].texCoord.Init( 0.5f, 0.5f );
  1118. WorkVertices[1].texCoord.Init( 0.5f, 0.5f );
  1119. WorkVertices[2].texCoord.Init( 0.5f, 0.5f );
  1120. WorkVertices[3].texCoord.Init( 0.5f, 0.5f );
  1121. // Clip against portal plane
  1122. float flPortalPlaneDist = DotProduct( m_vForward, vOffsetOrigin );
  1123. ClipPortalPolyToPlane( WorkVertices, 4, clippedVerts, &nClippedVertCount, -m_vForward, -flPortalPlaneDist );
  1124. }
  1125. else
  1126. {
  1127. // Clip portal quad against view frustum.
  1128. WorkVertices[0].vWorldSpacePosition = vOffsetOrigin - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  1129. WorkVertices[1].vWorldSpacePosition = vOffsetOrigin + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
  1130. WorkVertices[2].vWorldSpacePosition = vOffsetOrigin + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  1131. WorkVertices[3].vWorldSpacePosition = vOffsetOrigin - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
  1132. WorkVertices[0].texCoord.Init( 0.5f, 0.5f );
  1133. WorkVertices[1].texCoord.Init( 0.5f, 0.5f );
  1134. WorkVertices[2].texCoord.Init( 0.5f, 0.5f );
  1135. WorkVertices[3].texCoord.Init( 0.5f, 0.5f );
  1136. // Clip against flipped near plane so that we get the part of the portal quad that would be clipped by the near plane. Offset the near plane so that our cap poly will cover up
  1137. // the clip intersection line.
  1138. bool bClippedSomething = ClipPortalPolyToPlane( WorkVertices, 4, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal,
  1139. nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - CLIP_NEARPLANE_OFFSET );
  1140. if ( !bClippedSomething )
  1141. {
  1142. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1143. pRenderContext->PopMatrix();
  1144. return;
  1145. }
  1146. // Clip against the four remaining frustum planes. Offset the planes a tiny bit so that we have some slack when reprojecting the clipped verts back onto the near plane.
  1147. // This hack is only acceptable because we're not passing the proper texture coordinates for the quad anyways.
  1148. ClipPortalPolyToPlane( clippedVerts, nClippedVertCount, WorkVertices, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Normal, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Dist - 0.01f );
  1149. ClipPortalPolyToPlane( WorkVertices, nClippedVertCount, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Normal, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Dist - 0.01f );
  1150. ClipPortalPolyToPlane( clippedVerts, nClippedVertCount, WorkVertices, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_TOP ].m_Normal, nearPlaneFrustum[ FRUSTUM_TOP ].m_Dist - 0.01f );
  1151. ClipPortalPolyToPlane( WorkVertices, nClippedVertCount, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Normal, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Dist - 0.01f );
  1152. }
  1153. if ( nClippedVertCount < 3 )
  1154. {
  1155. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1156. pRenderContext->PopMatrix();
  1157. return;
  1158. }
  1159. ProjectPortalPolyToPlane( clippedVerts, nClippedVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - PROJECT_NEARPLANE_OFFSET, vCameraPos );
  1160. pRenderContext->OverrideDepthEnable( true, true, false );
  1161. RenderPortalMeshConvexPolygon( clippedVerts, nClippedVertCount, pMaterial, this );
  1162. pRenderContext->OverrideDepthEnable( false, true, true );
  1163. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1164. pRenderContext->PopMatrix();
  1165. }
  1166. void CPortalRenderable_FlatBasic::ClipFixToBoundingAreaAndDraw( PortalMeshPoint_t *pVerts, const IMaterial *pMaterial )
  1167. {
  1168. PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) ); //really only should need 4x points, but I'm paranoid
  1169. PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) );
  1170. PortalMeshPoint_t *pTempVerts;
  1171. memcpy( pInVerts, pVerts, sizeof( PortalMeshPoint_t ) * 4 );
  1172. int iVertCount = 4;
  1173. //clip by bounding area planes
  1174. {
  1175. for( int i = 0; i != (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1); ++i )
  1176. {
  1177. if( iVertCount < 3 )
  1178. return; //nothing to draw
  1179. iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Normal, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Dist, 0.01f );
  1180. pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
  1181. }
  1182. if( iVertCount < 3 )
  1183. return; //nothing to draw
  1184. }
  1185. //clip by the viewing frustum
  1186. {
  1187. VPlane *pFrustum = view->GetFrustum();
  1188. for( int i = 0; i != FRUSTUM_NUMPLANES; ++i )
  1189. {
  1190. if( iVertCount < 3 )
  1191. return; //nothing to draw
  1192. iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, pFrustum[i].m_Normal, pFrustum[i].m_Dist, 0.01f );
  1193. pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
  1194. }
  1195. if( iVertCount < 3 )
  1196. return; //nothing to draw
  1197. }
  1198. CMatRenderContextPtr pRenderContext( materials );
  1199. //project the points so we can fudge the numbers a bit and move them to exactly 0.0f depth
  1200. {
  1201. VMatrix matProj, matView, matViewProj;
  1202. pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
  1203. pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
  1204. MatrixMultiply( matProj, matView, matViewProj );
  1205. for( int i = 0; i != iVertCount; ++i )
  1206. {
  1207. float W, inverseW;
  1208. W = matViewProj.m[3][0] * pInVerts[i].vWorldSpacePosition.x;
  1209. W += matViewProj.m[3][1] * pInVerts[i].vWorldSpacePosition.y;
  1210. W += matViewProj.m[3][2] * pInVerts[i].vWorldSpacePosition.z;
  1211. W += matViewProj.m[3][3];
  1212. inverseW = 1.0f / W;
  1213. pInVerts[i].vWorldSpacePosition = matViewProj * pInVerts[i].vWorldSpacePosition;
  1214. pInVerts[i].vWorldSpacePosition *= inverseW;
  1215. pInVerts[i].vWorldSpacePosition.z = 0.00001f; //the primary reason we're projecting on the CPU to begin with
  1216. }
  1217. }
  1218. //render with identity transforms and clipping disabled
  1219. {
  1220. bool bClippingEnabled = pRenderContext->EnableClipping( false );
  1221. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1222. pRenderContext->PushMatrix();
  1223. pRenderContext->LoadIdentity();
  1224. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1225. pRenderContext->PushMatrix();
  1226. pRenderContext->LoadIdentity();
  1227. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1228. pRenderContext->PushMatrix();
  1229. pRenderContext->LoadIdentity();
  1230. RenderPortalMeshConvexPolygon( pInVerts, iVertCount, pMaterial, this );
  1231. if( mat_wireframe.GetBool() )
  1232. RenderPortalMeshConvexPolygon( pInVerts, iVertCount, materials->FindMaterial( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS, false ), this );
  1233. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1234. pRenderContext->PopMatrix();
  1235. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1236. pRenderContext->PopMatrix();
  1237. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1238. pRenderContext->PopMatrix();
  1239. pRenderContext->EnableClipping( bClippingEnabled );
  1240. }
  1241. }
  1242. //-----------------------------------------------------------------------------
  1243. // renders a quad that simulates fog as an overlay for something else (most notably the hole we create for stencil mode portals)
  1244. //-----------------------------------------------------------------------------
  1245. void CPortalRenderable_FlatBasic::RenderFogQuad( void )
  1246. {
  1247. CMatRenderContextPtr pRenderContext( materials );
  1248. if( pRenderContext->GetFogMode() == MATERIAL_FOG_NONE )
  1249. return;
  1250. float fFogStart, fFogEnd;
  1251. pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, NULL );
  1252. float vertexColor[4];
  1253. unsigned char fogColor[3];
  1254. pRenderContext->GetFogColor( fogColor );
  1255. float fFogMaxDensity = 1.0f; //pRenderContext->GetFogMaxDensity(); //function only exists in a pickled changelist for now
  1256. /*float fColorScale = LinearToGammaFullRange( pRenderContext->GetToneMappingScaleLinear().x );
  1257. fogColor[0] *= fColorScale;
  1258. fogColor[1] *= fColorScale;
  1259. fogColor[2] *= fColorScale;*/
  1260. vertexColor[0] = fogColor[0] * (1.0f / 255.0f);
  1261. vertexColor[1] = fogColor[1] * (1.0f / 255.0f);
  1262. vertexColor[2] = fogColor[2] * (1.0f / 255.0f);
  1263. float ooFogRange = 1.0f;
  1264. if ( fFogEnd != fFogStart )
  1265. {
  1266. ooFogRange = 1.0f / (fFogEnd - fFogStart);
  1267. }
  1268. float FogEndOverFogRange = ooFogRange * fFogEnd;
  1269. VMatrix matView, matViewProj, matProj;
  1270. pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
  1271. pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
  1272. MatrixMultiply( matProj, matView, matViewProj );
  1273. Vector vUp = m_vUp * (m_fHalfHeight * 2.0f);
  1274. Vector vRight = m_vRight * (m_fHalfWidth * 2.0f);
  1275. Vector ptCorners[4];
  1276. ptCorners[0] = (m_ptOrigin + vUp) + vRight;
  1277. ptCorners[1] = (m_ptOrigin + vUp) - vRight;
  1278. ptCorners[2] = (m_ptOrigin - vUp) + vRight;
  1279. ptCorners[3] = (m_ptOrigin - vUp) - vRight;
  1280. pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_TranslucentVertexColor );
  1281. IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
  1282. CMeshBuilder meshBuilder;
  1283. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
  1284. for( int i = 0; i != 4; ++i )
  1285. {
  1286. float projZ;
  1287. projZ = matViewProj.m[2][0] * ptCorners[i].x;
  1288. projZ += matViewProj.m[2][1] * ptCorners[i].y;
  1289. projZ += matViewProj.m[2][2] * ptCorners[i].z;
  1290. projZ += matViewProj.m[2][3];
  1291. float fFogAmount = ((-projZ * ooFogRange) + FogEndOverFogRange); //projZ should be negative
  1292. //range fix
  1293. if( fFogAmount >= fFogMaxDensity )
  1294. fFogAmount = fFogMaxDensity;
  1295. if( fFogAmount <= 0.0f )
  1296. fFogAmount = 0.0f;
  1297. vertexColor[3] = fFogMaxDensity - fFogAmount; //alpha is inverse fog
  1298. meshBuilder.Position3fv( &ptCorners[i].x );
  1299. meshBuilder.Color4fv( vertexColor );
  1300. meshBuilder.AdvanceVertex();
  1301. }
  1302. meshBuilder.End();
  1303. pMesh->Draw();
  1304. }
  1305. bool CPortalRenderable_FlatBasic::DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
  1306. {
  1307. bool bAboveWater = false, bBelowWater = false;
  1308. for( int i = 0; i != 4; ++i )
  1309. {
  1310. bAboveWater |= (m_InternallyMaintainedData.m_ptCorners[i].z >= waterZ);
  1311. bBelowWater |= (m_InternallyMaintainedData.m_ptCorners[i].z <= waterZ);
  1312. }
  1313. if( !(bAboveWater && bBelowWater) )
  1314. {
  1315. RANDOM_CEG_TEST_SECRET();
  1316. return false;
  1317. }
  1318. Vector vMins, vMaxs;
  1319. vMins = vMaxs = m_InternallyMaintainedData.m_ptCorners[0];
  1320. for( int i = 1; i != 4; ++i )
  1321. {
  1322. vMins = VectorMin( vMins, m_InternallyMaintainedData.m_ptCorners[i] );
  1323. vMaxs = VectorMax( vMaxs, m_InternallyMaintainedData.m_ptCorners[i] );
  1324. }
  1325. return render->DoesBoxIntersectWaterVolume( vMins, vMaxs, leafWaterDataID );
  1326. }
  1327. float CPortalRenderable_FlatBasic::GetPortalDistanceBias() const
  1328. {
  1329. Vector vCameraToExitPortal = m_ptOrigin - CurrentViewOrigin();
  1330. float flDistModifier = vCameraToExitPortal.Length();
  1331. flDistModifier = MAX( flDistModifier, 0.01f );
  1332. // Increase the distance factor based on angle (so we drop detail more aggressively when viewed at an angle)
  1333. // Vector vNormalizedCameraToExitPortal = vCameraToExitPortal * ( 1.0f / flDistModifier );
  1334. // float flAngleModifier = vNormalizedCameraToExitPortal.Dot( m_vForward );
  1335. // flAngleModifier = clamp( flAngleModifier, 0.01f, 1.0f );
  1336. // flAngleModifier = sqrtf( flAngleModifier ); // Use a square root to reduce the multiplier effect
  1337. // flDistModifier /= flAngleModifier;
  1338. return flDistModifier;
  1339. }
  1340. bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, const CUtlVector<VPlane> &currentComplexFrustum )
  1341. {
  1342. if( m_pLinkedPortal == NULL )
  1343. return false;
  1344. Vector vCameraPos = currentView.origin;
  1345. if( (g_pPortalRender->GetViewRecursionLevel() == 0) &&
  1346. ((m_ptOrigin - vCameraPos).LengthSqr() < (m_fHalfHeight * m_fHalfHeight)) ) //FIXME: Player closeness check might need reimplementation
  1347. {
  1348. return true; //fudgery time. The player might not be able to see the surface, but they can probably see the render fix
  1349. }
  1350. if( m_vForward.Dot( vCameraPos ) <= m_InternallyMaintainedData.m_fPlaneDist )
  1351. return false; //looking at portal backface
  1352. int iCurrentFrustmPlanes = currentComplexFrustum.Count();
  1353. //now slice up the portal quad and see if any is visible within the frustum
  1354. int allocSize = (6 + currentComplexFrustum.Count()); //possible to add 1 point per cut, 4 starting points, N plane cuts, 2 extra because I'm paranoid
  1355. Vector *pInVerts = (Vector *)stackalloc( sizeof( Vector ) * allocSize * 2 );
  1356. Vector *pOutVerts = pInVerts + allocSize;
  1357. Vector *pTempVerts;
  1358. //clip by first plane and put output into pInVerts
  1359. int iVertCount = ClipPolyToPlane( m_InternallyMaintainedData.m_ptCorners, 4, pInVerts, currentComplexFrustum[0].m_Normal, currentComplexFrustum[0].m_Dist, 0.01f );
  1360. //clip by other planes and flipflop in and out pointers
  1361. for( int i = 1; i != iCurrentFrustmPlanes; ++i )
  1362. {
  1363. if( iVertCount < 3 )
  1364. return false; //nothing left in the frustum
  1365. iVertCount = ClipPolyToPlane( pInVerts, iVertCount, pOutVerts, currentComplexFrustum[i].m_Normal, currentComplexFrustum[i].m_Dist, 0.01f );
  1366. pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
  1367. }
  1368. if( iVertCount < 3 )
  1369. return false; //nothing left in the frustum
  1370. return true;
  1371. }
  1372. bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized )
  1373. {
  1374. return (fScreenFilledByStencilMaskLastFrame_Normalized < 0.0f) || // < 0 is an error value
  1375. (fScreenFilledByStencilMaskLastFrame_Normalized > PORTALRENDERABLE_FLATBASIC_MINPIXELVIS );
  1376. }
  1377. CPortalRenderable *CreatePortal_FlatBasic_Fn( void )
  1378. {
  1379. return new CPortalRenderable_FlatBasic;
  1380. }
  1381. static CPortalRenderableCreator_AutoRegister CreatePortal_FlatBasic( "Flat Basic", CreatePortal_FlatBasic_Fn );