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.

495 lines
15 KiB

  1. //====== Copyright � 1996-2003, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "c_env_projectedtexture.h"
  8. #include "shareddefs.h"
  9. #include "materialsystem/imesh.h"
  10. #include "materialsystem/imaterial.h"
  11. #include "view.h"
  12. #include "iviewrender.h"
  13. #include "view_shared.h"
  14. #include "texture_group_names.h"
  15. #include "tier0/icommandline.h"
  16. #include "tier0/platform.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. float C_EnvProjectedTexture::m_flVisibleBBoxMinHeight = -FLT_MAX;
  20. IMPLEMENT_CLIENTCLASS_DT( C_EnvProjectedTexture, DT_EnvProjectedTexture, CEnvProjectedTexture )
  21. RecvPropEHandle( RECVINFO( m_hTargetEntity ) ),
  22. RecvPropBool( RECVINFO( m_bState ) ),
  23. RecvPropBool( RECVINFO( m_bAlwaysUpdate ) ),
  24. RecvPropFloat( RECVINFO( m_flLightFOV ) ),
  25. RecvPropBool( RECVINFO( m_bEnableShadows ) ),
  26. RecvPropBool( RECVINFO( m_bSimpleProjection ) ),
  27. RecvPropBool( RECVINFO( m_bLightOnlyTarget ) ),
  28. RecvPropBool( RECVINFO( m_bLightWorld ) ),
  29. RecvPropBool( RECVINFO( m_bCameraSpace ) ),
  30. RecvPropFloat( RECVINFO( m_flBrightnessScale ) ),
  31. RecvPropInt( RECVINFO( m_LightColor ), 0, RecvProxy_Int32ToColor32 ),
  32. RecvPropFloat( RECVINFO( m_flColorTransitionTime ) ),
  33. RecvPropFloat( RECVINFO( m_flAmbient ) ),
  34. RecvPropString( RECVINFO( m_SpotlightTextureName ) ),
  35. RecvPropInt( RECVINFO( m_nSpotlightTextureFrame ) ),
  36. RecvPropFloat( RECVINFO( m_flNearZ ) ),
  37. RecvPropFloat( RECVINFO( m_flFarZ ) ),
  38. RecvPropInt( RECVINFO( m_nShadowQuality ) ),
  39. RecvPropFloat( RECVINFO( m_flProjectionSize ) ),
  40. RecvPropFloat( RECVINFO( m_flRotation ) ),
  41. RecvPropInt( RECVINFO( m_iStyle ) ),
  42. END_RECV_TABLE()
  43. C_EnvProjectedTexture *C_EnvProjectedTexture::Create( )
  44. {
  45. C_EnvProjectedTexture *pEnt = new C_EnvProjectedTexture();
  46. pEnt->m_flNearZ = 4.0f;
  47. pEnt->m_flFarZ = 2000.0f;
  48. // strcpy( pEnt->m_SpotlightTextureName, "particle/rj" );
  49. pEnt->m_bLightWorld = true;
  50. pEnt->m_bLightOnlyTarget = false;
  51. pEnt->m_bSimpleProjection = true;
  52. pEnt->m_nShadowQuality = 1;
  53. pEnt->m_flLightFOV = 10.0f;
  54. pEnt->m_LightColor.r = 255;
  55. pEnt->m_LightColor.g = 255;
  56. pEnt->m_LightColor.b = 255;
  57. pEnt->m_LightColor.a = 255;
  58. pEnt->m_bEnableShadows = false;
  59. pEnt->m_flColorTransitionTime = 1.0f;
  60. pEnt->m_bCameraSpace = false;
  61. pEnt->SetAbsAngles( QAngle( 90, 0, 0 ) );
  62. pEnt->m_bAlwaysUpdate = true;
  63. pEnt->m_bState = true;
  64. pEnt->m_flProjectionSize = 500.0f;
  65. pEnt->m_flRotation = 0.0f;
  66. return pEnt;
  67. }
  68. C_EnvProjectedTexture::C_EnvProjectedTexture( void )
  69. {
  70. m_LightHandle = CLIENTSHADOW_INVALID_HANDLE;
  71. m_bForceUpdate = true;
  72. m_pMaterial = NULL;
  73. m_bIsCurrentlyProjected = false;
  74. AddToEntityList( ENTITY_LIST_SIMULATE );
  75. }
  76. C_EnvProjectedTexture::~C_EnvProjectedTexture( void )
  77. {
  78. ShutDownLightHandle();
  79. }
  80. void C_EnvProjectedTexture::ShutDownLightHandle( void )
  81. {
  82. // Clear out the light
  83. if( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE )
  84. {
  85. if ( m_bSimpleProjection == true )
  86. {
  87. g_pClientShadowMgr->DestroyProjection( m_LightHandle );
  88. }
  89. else
  90. {
  91. g_pClientShadowMgr->DestroyFlashlight( m_LightHandle );
  92. }
  93. m_LightHandle = CLIENTSHADOW_INVALID_HANDLE;
  94. }
  95. m_bIsCurrentlyProjected = false;
  96. }
  97. void C_EnvProjectedTexture::SetMaterial( IMaterial *pMaterial )
  98. {
  99. if ( pMaterial != m_ProjectedMaterial )
  100. {
  101. m_ProjectedMaterial.Init( pMaterial );
  102. pMaterial->AddRef();
  103. }
  104. }
  105. void C_EnvProjectedTexture::SetLightColor( byte r, byte g, byte b, byte a )
  106. {
  107. m_LightColor.r = r;
  108. m_LightColor.g = g;
  109. m_LightColor.b = b;
  110. m_LightColor.a = a;
  111. }
  112. void C_EnvProjectedTexture::SetSize( float flSize )
  113. {
  114. m_flProjectionSize = flSize;
  115. }
  116. void C_EnvProjectedTexture::SetRotation( float flRotation )
  117. {
  118. m_flRotation = flRotation;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. // Input : updateType -
  123. //-----------------------------------------------------------------------------
  124. void C_EnvProjectedTexture::OnDataChanged( DataUpdateType_t updateType )
  125. {
  126. if ( updateType == DATA_UPDATE_CREATED )
  127. {
  128. Assert( m_SpotlightTexture.IsValid() == false );
  129. m_SpotlightTexture.Init( m_SpotlightTextureName, TEXTURE_GROUP_OTHER, true );
  130. m_ProjectedMaterial.Init( m_SpotlightTextureName, TEXTURE_GROUP_OTHER );
  131. }
  132. m_bForceUpdate = true;
  133. UpdateLight();
  134. BaseClass::OnDataChanged( updateType );
  135. }
  136. static ConVar asw_perf_wtf("asw_perf_wtf", "0", FCVAR_DEVELOPMENTONLY, "Disable updating of projected shadow textures from UpdateLight" );
  137. extern ConVar r_flashlightenableculling;
  138. bool C_EnvProjectedTexture::ShouldUpdate( void )
  139. {
  140. if ( !IsGameConsole() )
  141. {
  142. CPULevel_t nCPULevel = GetCPULevel();
  143. bool bNoDraw = ( GetMinCPULevel() && GetMinCPULevel()-1 > nCPULevel );
  144. bNoDraw = bNoDraw || ( GetMaxCPULevel() && GetMaxCPULevel()-1 < nCPULevel );
  145. if ( bNoDraw )
  146. return false;
  147. GPULevel_t nGPULevel = GetGPULevel();
  148. bNoDraw = ( GetMinGPULevel() && GetMinGPULevel()-1 > nGPULevel );
  149. bNoDraw = bNoDraw || ( GetMaxGPULevel() && GetMaxGPULevel()-1 < nGPULevel );
  150. if ( bNoDraw )
  151. return false;
  152. }
  153. return true;
  154. }
  155. void C_EnvProjectedTexture::UpdateLight( void )
  156. {
  157. if ( !ShouldUpdate() )
  158. {
  159. if ( m_bIsCurrentlyProjected )
  160. {
  161. ShutDownLightHandle();
  162. }
  163. return;
  164. }
  165. VPROF("C_EnvProjectedTexture::UpdateLight");
  166. bool bVisible = true;
  167. Vector vLinearFloatLightColor( m_LightColor.r, m_LightColor.g, m_LightColor.b );
  168. float flLinearFloatLightAlpha = m_LightColor.a;
  169. if ( m_bAlwaysUpdate )
  170. {
  171. m_bForceUpdate = true;
  172. }
  173. if ( m_CurrentLinearFloatLightColor != vLinearFloatLightColor || m_flCurrentLinearFloatLightAlpha != flLinearFloatLightAlpha )
  174. {
  175. float flColorTransitionSpeed = gpGlobals->frametime * m_flColorTransitionTime * 255.0f;
  176. m_CurrentLinearFloatLightColor.x = Approach( vLinearFloatLightColor.x, m_CurrentLinearFloatLightColor.x, flColorTransitionSpeed );
  177. m_CurrentLinearFloatLightColor.y = Approach( vLinearFloatLightColor.y, m_CurrentLinearFloatLightColor.y, flColorTransitionSpeed );
  178. m_CurrentLinearFloatLightColor.z = Approach( vLinearFloatLightColor.z, m_CurrentLinearFloatLightColor.z, flColorTransitionSpeed );
  179. m_flCurrentLinearFloatLightAlpha = Approach( flLinearFloatLightAlpha, m_flCurrentLinearFloatLightAlpha, flColorTransitionSpeed );
  180. m_bForceUpdate = true;
  181. }
  182. if ( !m_bForceUpdate && r_flashlightenableculling.GetBool() )
  183. {
  184. bVisible = IsBBoxVisible();
  185. }
  186. if ( m_bState == false || !bVisible )
  187. {
  188. // Spotlight's extents aren't in view
  189. ShutDownLightHandle();
  190. return;
  191. }
  192. if ( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE || m_hTargetEntity != NULL || m_bForceUpdate )
  193. {
  194. Vector vForward, vRight, vUp, vPos = GetAbsOrigin();
  195. FlashlightState_t state;
  196. if ( m_hTargetEntity != NULL )
  197. {
  198. if ( m_bCameraSpace )
  199. {
  200. const QAngle &angles = GetLocalAngles();
  201. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  202. if( pPlayer )
  203. {
  204. const QAngle playerAngles = pPlayer->GetAbsAngles();
  205. Vector vPlayerForward, vPlayerRight, vPlayerUp;
  206. AngleVectors( playerAngles, &vPlayerForward, &vPlayerRight, &vPlayerUp );
  207. matrix3x4_t mRotMatrix;
  208. AngleMatrix( angles, mRotMatrix );
  209. VectorITransform( vPlayerForward, mRotMatrix, vForward );
  210. VectorITransform( vPlayerRight, mRotMatrix, vRight );
  211. VectorITransform( vPlayerUp, mRotMatrix, vUp );
  212. float dist = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()).Length();
  213. vPos = m_hTargetEntity->GetAbsOrigin() - vForward*dist;
  214. VectorNormalize( vForward );
  215. VectorNormalize( vRight );
  216. VectorNormalize( vUp );
  217. }
  218. }
  219. else
  220. {
  221. vForward = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin();
  222. VectorNormalize( vForward );
  223. // JasonM - unimplemented
  224. Assert (0);
  225. //Quaternion q = DirectionToOrientation( dir );
  226. //
  227. // JasonM - set up vRight, vUp
  228. //
  229. // VectorNormalize( vRight );
  230. // VectorNormalize( vUp );
  231. }
  232. }
  233. else
  234. {
  235. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  236. }
  237. state.m_fHorizontalFOVDegrees = m_flLightFOV;
  238. state.m_fVerticalFOVDegrees = m_flLightFOV;
  239. state.m_vecLightOrigin = vPos;
  240. BasisToQuaternion( vForward, vRight, vUp, state.m_quatOrientation );
  241. state.m_NearZ = m_flNearZ;
  242. state.m_FarZ = m_flFarZ;
  243. if ( r_flashlightenableculling.GetBool() )
  244. {
  245. // quickly check the proposed light's bbox against the view frustum to determine whether we
  246. // should bother to create it, if it doesn't exist, or cull it, if it does.
  247. #ifndef LINUX
  248. #pragma message("OPTIMIZATION: this should be made SIMD")
  249. #endif
  250. // get the half-widths of the near and far planes,
  251. // based on the FOV which is in degrees. Remember that
  252. // on planet Valve, x is forward, y left, and z up.
  253. const float tanHalfAngle = tan( m_flLightFOV * ( M_PI/180.0f ) * 0.5f );
  254. const float halfWidthNear = tanHalfAngle * m_flNearZ;
  255. const float halfWidthFar = tanHalfAngle * m_flFarZ;
  256. // now we can build coordinates in local space: the near rectangle is eg
  257. // (0, -halfWidthNear, -halfWidthNear), (0, halfWidthNear, -halfWidthNear),
  258. // (0, halfWidthNear, halfWidthNear), (0, -halfWidthNear, halfWidthNear)
  259. VectorAligned vNearRect[4] = {
  260. VectorAligned( m_flNearZ, -halfWidthNear, -halfWidthNear), VectorAligned( m_flNearZ, halfWidthNear, -halfWidthNear),
  261. VectorAligned( m_flNearZ, halfWidthNear, halfWidthNear), VectorAligned( m_flNearZ, -halfWidthNear, halfWidthNear)
  262. };
  263. VectorAligned vFarRect[4] = {
  264. VectorAligned( m_flFarZ, -halfWidthFar, -halfWidthFar), VectorAligned( m_flFarZ, halfWidthFar, -halfWidthFar),
  265. VectorAligned( m_flFarZ, halfWidthFar, halfWidthFar), VectorAligned( m_flFarZ, -halfWidthFar, halfWidthFar)
  266. };
  267. matrix3x4_t matOrientation( vForward, -vRight, vUp, vPos );
  268. enum
  269. {
  270. kNEAR = 0,
  271. kFAR = 1,
  272. };
  273. VectorAligned vOutRects[2][4];
  274. for ( int i = 0 ; i < 4 ; ++i )
  275. {
  276. VectorTransform( vNearRect[i].Base(), matOrientation, vOutRects[0][i].Base() );
  277. }
  278. for ( int i = 0 ; i < 4 ; ++i )
  279. {
  280. VectorTransform( vFarRect[i].Base(), matOrientation, vOutRects[1][i].Base() );
  281. }
  282. // now take the min and max extents for the bbox, and see if it is visible.
  283. Vector mins = **vOutRects;
  284. Vector maxs = **vOutRects;
  285. for ( int i = 1; i < 8 ; ++i )
  286. {
  287. VectorMin( mins, *(*vOutRects+i), mins );
  288. VectorMax( maxs, *(*vOutRects+i), maxs );
  289. }
  290. #if 0 //for debugging the visibility frustum we just calculated
  291. NDebugOverlay::Triangle( vOutRects[0][0], vOutRects[0][1], vOutRects[0][2], 255, 0, 0, 100, true, 0.0f ); //first tri
  292. NDebugOverlay::Triangle( vOutRects[0][2], vOutRects[0][1], vOutRects[0][0], 255, 0, 0, 100, true, 0.0f ); //make it double sided
  293. NDebugOverlay::Triangle( vOutRects[0][2], vOutRects[0][3], vOutRects[0][0], 255, 0, 0, 100, true, 0.0f ); //second tri
  294. NDebugOverlay::Triangle( vOutRects[0][0], vOutRects[0][3], vOutRects[0][2], 255, 0, 0, 100, true, 0.0f ); //make it double sided
  295. NDebugOverlay::Triangle( vOutRects[1][0], vOutRects[1][1], vOutRects[1][2], 0, 0, 255, 100, true, 0.0f ); //first tri
  296. NDebugOverlay::Triangle( vOutRects[1][2], vOutRects[1][1], vOutRects[1][0], 0, 0, 255, 100, true, 0.0f ); //make it double sided
  297. NDebugOverlay::Triangle( vOutRects[1][2], vOutRects[1][3], vOutRects[1][0], 0, 0, 255, 100, true, 0.0f ); //second tri
  298. NDebugOverlay::Triangle( vOutRects[1][0], vOutRects[1][3], vOutRects[1][2], 0, 0, 255, 100, true, 0.0f ); //make it double sided
  299. NDebugOverlay::Box( vec3_origin, mins, maxs, 0, 255, 0, 100, 0.0f );
  300. #endif
  301. bool bVisible = IsBBoxVisible( mins, maxs );
  302. if (!bVisible)
  303. {
  304. // Spotlight's extents aren't in view
  305. if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE )
  306. {
  307. ShutDownLightHandle();
  308. }
  309. return;
  310. }
  311. }
  312. float flAlpha = m_flCurrentLinearFloatLightAlpha * ( 1.0f / 255.0f );
  313. // Get the current light style value to throttle the brightness by
  314. flAlpha *= engine->LightStyleValue( m_iStyle );
  315. state.m_fQuadraticAtten = 0.0;
  316. state.m_fLinearAtten = 100;
  317. state.m_fConstantAtten = 0.0f;
  318. state.m_FarZAtten = m_flFarZ;
  319. state.m_fBrightnessScale = m_flBrightnessScale;
  320. state.m_Color[0] = m_CurrentLinearFloatLightColor.x * ( 1.0f / 255.0f ) * flAlpha;
  321. state.m_Color[1] = m_CurrentLinearFloatLightColor.y * ( 1.0f / 255.0f ) * flAlpha;
  322. state.m_Color[2] = m_CurrentLinearFloatLightColor.z * ( 1.0f / 255.0f ) * flAlpha;
  323. state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient;
  324. state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();
  325. state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();
  326. state.m_bEnableShadows = m_bEnableShadows;
  327. extern ConVar r_flashlightdepthres;
  328. state.m_flShadowMapResolution = r_flashlightdepthres.GetFloat();
  329. if ( m_bSimpleProjection )
  330. {
  331. state.m_pSpotlightTexture = NULL;
  332. state.m_pProjectedMaterial = m_ProjectedMaterial;
  333. }
  334. else
  335. {
  336. state.m_pSpotlightTexture = m_SpotlightTexture;
  337. state.m_pProjectedMaterial = NULL;
  338. }
  339. state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame;
  340. state.m_flProjectionSize = m_flProjectionSize;
  341. state.m_flProjectionRotation = m_flRotation;
  342. state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality
  343. state.m_bShareBetweenSplitscreenPlayers = true; // projected flashlight entities should always be shared among players, because they live in a map.
  344. if( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE )
  345. {
  346. // Hack: env projected textures don't work like normal flashlights; they're not assigned to a given splitscreen slot,
  347. // but the flashlight code requires this
  348. HACK_GETLOCALPLAYER_GUARD( "Env projected texture" );
  349. if ( m_bSimpleProjection == true )
  350. {
  351. m_LightHandle = g_pClientShadowMgr->CreateProjection( state );
  352. }
  353. else
  354. {
  355. m_LightHandle = g_pClientShadowMgr->CreateFlashlight( state );
  356. }
  357. if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE )
  358. {
  359. m_bForceUpdate = false;
  360. }
  361. }
  362. else
  363. {
  364. if ( m_bSimpleProjection == true )
  365. {
  366. g_pClientShadowMgr->UpdateProjectionState( m_LightHandle, state );
  367. }
  368. else
  369. {
  370. g_pClientShadowMgr->UpdateFlashlightState( m_LightHandle, state );
  371. }
  372. m_bForceUpdate = false;
  373. }
  374. g_pClientShadowMgr->GetFrustumExtents( m_LightHandle, m_vecExtentsMin, m_vecExtentsMax );
  375. m_vecExtentsMin = m_vecExtentsMin - GetAbsOrigin();
  376. m_vecExtentsMax = m_vecExtentsMax - GetAbsOrigin();
  377. }
  378. if( m_bLightOnlyTarget )
  379. {
  380. g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, m_hTargetEntity );
  381. }
  382. else
  383. {
  384. g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, INVALID_EHANDLE );
  385. }
  386. g_pClientShadowMgr->SetFlashlightLightWorld( m_LightHandle, m_bLightWorld );
  387. if ( !asw_perf_wtf.GetBool() && !m_bForceUpdate )
  388. {
  389. g_pClientShadowMgr->UpdateProjectedTexture( m_LightHandle, true );
  390. }
  391. m_bIsCurrentlyProjected = true;
  392. }
  393. bool C_EnvProjectedTexture::Simulate( void )
  394. {
  395. UpdateLight();
  396. BaseClass::Simulate();
  397. return true;
  398. }
  399. bool C_EnvProjectedTexture::IsBBoxVisible( Vector vecExtentsMin, Vector vecExtentsMax )
  400. {
  401. // Z position clamped to the min height (but must be less than the max)
  402. float flVisibleBBoxMinHeight = MIN( vecExtentsMax.z - 1.0f, m_flVisibleBBoxMinHeight );
  403. vecExtentsMin.z = MAX( vecExtentsMin.z, flVisibleBBoxMinHeight );
  404. // Check if the bbox is in the view
  405. return !engine->CullBox( vecExtentsMin, vecExtentsMax );
  406. }