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.

450 lines
17 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. //NOTE: Mirrors with models require an attachment named "MirrorSurface_Attach" with x facing out of the mirror plane.
  7. //They also require that the mirror surface be in a bodygroup by itself named "MirrorSurface" with the first index being the mirror, second being empty.
  8. //Lastly, they require that all non-mirror geometry be in bodygroups that have the second entry as empty.
  9. //It's a good idea to put a cubemap on the mirror surface material because they're not infinitely recursive
  10. #include "cbase.h"
  11. #include "baseanimating.h"
  12. #include "pvs_extender.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //#define TEST_ANIMATION //uncomment to run "testanim" in a loop
  16. class CProp_Mirror : public CBaseAnimating, CPVS_Extender
  17. {
  18. public:
  19. DECLARE_CLASS( CProp_Mirror, CBaseAnimating );
  20. DECLARE_SERVERCLASS();
  21. DECLARE_DATADESC();
  22. CProp_Mirror( void );
  23. virtual void Precache( void );
  24. virtual void Spawn( void );
  25. virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); }
  26. virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | (m_bPhysicsEnabled ? FCAP_IMPULSE_USE : 0); };
  27. virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  28. void UpdateReflectionPlane( void );
  29. void UpdateReflectionPolygon( void );
  30. virtual CServerNetworkProperty *GetExtenderNetworkProp( void ) { return NetworkProp(); }
  31. virtual const edict_t *GetExtenderEdict( void ) const { return edict(); }
  32. virtual Vector GetExtensionPVSOrigin( void ) { return GetAbsOrigin(); }
  33. virtual bool IsExtenderValid( void ) { return true; }
  34. int ComputeFrustumThroughPolygon( const Vector &vVisOrigin, const VPlane *pInputFrustum, int iInputFrustumPlanes, VPlane *pOutputFrustum, int iOutputFrustumMaxPlanes );
  35. //This portal is decidedly visible, recursively extend the visibility problem
  36. virtual void ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft );
  37. #if defined( TEST_ANIMATION )
  38. virtual void Think( void );
  39. #endif
  40. Vector m_LocalSpaceReflectionPolygonVerts[10]; //best guess at the reflection polygon by intersecting the reflection plane with the local space OBB
  41. int m_LocalSpaceReflectionPolygonVertCount;
  42. struct ReflectPlaneCachedData_t
  43. {
  44. Vector vAttachmentOrigin;
  45. QAngle qAttachmentAngle;
  46. bool bModel;
  47. Vector vLocalSpaceAttachmentOrigin;
  48. QAngle qLocalSpaceAttachmentAngles;
  49. Vector vLocalOBB_Mins;
  50. Vector vLocalOBB_Maxs;
  51. };
  52. ReflectPlaneCachedData_t m_CachedReflectedData;
  53. VMatrix m_matReflection;
  54. CNetworkVar( float, m_fWidth );
  55. CNetworkVar( float, m_fHeight );
  56. int m_iMirrorFaceAttachment;
  57. bool m_bModel;
  58. bool m_bPhysicsEnabled;
  59. };
  60. BEGIN_DATADESC( CProp_Mirror )
  61. DEFINE_KEYFIELD( m_fWidth, FIELD_FLOAT, "Width" ),
  62. DEFINE_KEYFIELD( m_fHeight, FIELD_FLOAT, "Height" ),
  63. DEFINE_FIELD( m_iMirrorFaceAttachment, FIELD_INTEGER ),
  64. DEFINE_FIELD( m_bModel, FIELD_BOOLEAN ),
  65. DEFINE_KEYFIELD( m_bPhysicsEnabled, FIELD_BOOLEAN, "PhysicsEnabled" ),
  66. END_DATADESC()
  67. IMPLEMENT_SERVERCLASS_ST( CProp_Mirror, DT_Prop_Mirror )
  68. SendPropFloat( SENDINFO(m_fWidth) ),
  69. SendPropFloat( SENDINFO(m_fHeight) ),
  70. END_SEND_TABLE()
  71. LINK_ENTITY_TO_CLASS( prop_mirror, CProp_Mirror );
  72. CProp_Mirror::CProp_Mirror( void )
  73. {
  74. m_matReflection.m[3][0] = 0.0f;
  75. m_matReflection.m[3][1] = 0.0f;
  76. m_matReflection.m[3][2] = 0.0f;
  77. m_matReflection.m[3][3] = 1.0f;
  78. m_CachedReflectedData.vAttachmentOrigin.Invalidate();
  79. m_CachedReflectedData.qAttachmentAngle.Invalidate();
  80. m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
  81. m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
  82. m_CachedReflectedData.vLocalOBB_Maxs.Invalidate();
  83. m_CachedReflectedData.vLocalOBB_Mins.Invalidate();
  84. }
  85. void CProp_Mirror::Precache( void )
  86. {
  87. BaseClass::Precache();
  88. if( (m_ModelName.ToCStr() != NULL) && (m_ModelName.ToCStr()[0] != '\0') )
  89. {
  90. PrecacheModel( m_ModelName.ToCStr() );
  91. }
  92. }
  93. void CProp_Mirror::Spawn( void )
  94. {
  95. Precache();
  96. BaseClass::Spawn();
  97. if( m_ModelName.ToCStr() != NULL && m_ModelName.ToCStr()[0] != '\0' )
  98. {
  99. SetModel( m_ModelName.ToCStr() );
  100. SetSolid( SOLID_VPHYSICS );
  101. SetCollisionGroup( COLLISION_GROUP_INTERACTIVE );
  102. if( m_bPhysicsEnabled )
  103. {
  104. SetMoveType( MOVETYPE_VPHYSICS );
  105. VPhysicsInitNormal( GetSolid(), GetSolidFlags(), false );
  106. }
  107. else
  108. {
  109. SetMoveType( MOVETYPE_NONE );
  110. }
  111. #if defined( TEST_ANIMATION )
  112. ResetSequence( LookupSequence( "testanim" ) );
  113. ResetSequenceInfo();
  114. SetPlaybackRate( 0.1f );
  115. SetNextThink( gpGlobals->curtime + 1.0f );
  116. #endif
  117. m_iMirrorFaceAttachment = LookupAttachment( "MirrorSurface_Attach" );
  118. m_bModel = ( m_iMirrorFaceAttachment > 0 ); //0 is an invalid attachment index according to LookupAttachment()
  119. }
  120. else
  121. {
  122. Vector vExtent( 2.0f, m_fWidth/2.0f, m_fHeight/2.0f );
  123. SetSize( -vExtent, vExtent );
  124. m_bModel = false;
  125. }
  126. }
  127. void CProp_Mirror::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  128. {
  129. if( m_bPhysicsEnabled )
  130. {
  131. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  132. if ( pPlayer )
  133. {
  134. pPlayer->PickupObject( this );
  135. }
  136. }
  137. else
  138. {
  139. BaseClass::Use( pActivator, pCaller, useType, value );
  140. }
  141. }
  142. void CProp_Mirror::UpdateReflectionPlane( void )
  143. {
  144. Vector vMirrorAttachmentOrigin;
  145. QAngle qMirrorAttachmentAngles;
  146. if( m_bModel )
  147. {
  148. GetAttachment( m_iMirrorFaceAttachment, vMirrorAttachmentOrigin, qMirrorAttachmentAngles );
  149. }
  150. else
  151. {
  152. vMirrorAttachmentOrigin = GetAbsOrigin();
  153. qMirrorAttachmentAngles = GetAbsAngles();
  154. }
  155. if( (m_CachedReflectedData.vAttachmentOrigin != vMirrorAttachmentOrigin) || (m_CachedReflectedData.qAttachmentAngle != qMirrorAttachmentAngles) )
  156. {
  157. m_CachedReflectedData.vAttachmentOrigin = vMirrorAttachmentOrigin;
  158. m_CachedReflectedData.qAttachmentAngle = qMirrorAttachmentAngles;
  159. Vector vOrigin = m_CachedReflectedData.vAttachmentOrigin;
  160. Vector vForward, vRight, vUp;
  161. AngleVectors( qMirrorAttachmentAngles, &vForward, &vRight, &vUp );
  162. Vector vToOrigin( vOrigin.Dot( vForward ), vOrigin.Dot( vRight ), -vOrigin.Dot( vUp ) );
  163. //generate mirroring matrix. Move mirror to origin using base vectors, flip on forward axis, move back to position and orientation
  164. {
  165. m_matReflection.m[0][0] = (-vForward.x * vForward.x) + (vRight.x * vRight.x) + (vUp.x * vUp.x);
  166. m_matReflection.m[0][1] = (-vForward.x * vForward.y) + (vRight.x * vRight.y) + (vUp.x * vUp.y);
  167. m_matReflection.m[0][2] = (-vForward.x * vForward.z) + (vRight.x * vRight.z) + (vUp.x * vUp.z);
  168. m_matReflection.m[0][3] = (vToOrigin.x * vForward.x) - (vToOrigin.y * vRight.x) + (vToOrigin.z * vUp.x) + vOrigin.x;
  169. m_matReflection.m[1][0] = m_matReflection.m[0][1]; //rotation portion of the matrix is equal to it's own transpose
  170. m_matReflection.m[1][1] = (-vForward.y * vForward.y) + (vRight.y * vRight.y) + (vUp.y * vUp.y);
  171. m_matReflection.m[1][2] = (-vForward.y * vForward.z) + (vRight.y * vRight.z) + (vUp.y * vUp.z);
  172. m_matReflection.m[1][3] = (vToOrigin.x * vForward.y) - (vToOrigin.y * vRight.y) + (vToOrigin.z * vUp.y) + vOrigin.y;
  173. m_matReflection.m[2][0] = m_matReflection.m[0][2]; //rotation portion of the matrix is equal to it's own transpose
  174. m_matReflection.m[2][1] = m_matReflection.m[1][2]; //rotation portion of the matrix is equal to it's own transpose
  175. m_matReflection.m[2][2] = (-vForward.z * vForward.z) + (vRight.z * vRight.z) + (vUp.z * vUp.z);
  176. m_matReflection.m[2][3] = (vToOrigin.x * vForward.z) - (vToOrigin.y * vRight.z) + (vToOrigin.z * vUp.z) + vOrigin.z;
  177. }
  178. UpdateReflectionPolygon();
  179. }
  180. }
  181. void CProp_Mirror::UpdateReflectionPolygon( void )
  182. {
  183. if( m_bModel != m_CachedReflectedData.bModel )
  184. {
  185. m_CachedReflectedData.qAttachmentAngle.Invalidate();
  186. m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
  187. m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
  188. m_CachedReflectedData.vLocalOBB_Maxs.Invalidate();
  189. m_CachedReflectedData.bModel = m_bModel;
  190. }
  191. if( m_bModel )
  192. {
  193. Vector vMins, vMaxs;
  194. vMins = WorldAlignMins();
  195. vMaxs = WorldAlignMaxs();
  196. Vector vLocalAttachmentOrigin;
  197. QAngle qLocalAttachmentAngles;
  198. GetAttachmentLocal( m_iMirrorFaceAttachment, vLocalAttachmentOrigin, qLocalAttachmentAngles );
  199. if( (vMins == m_CachedReflectedData.vLocalOBB_Mins) && (vMaxs == m_CachedReflectedData.vLocalOBB_Maxs) &&
  200. (vLocalAttachmentOrigin == m_CachedReflectedData.vLocalSpaceAttachmentOrigin) && (qLocalAttachmentAngles == m_CachedReflectedData.qLocalSpaceAttachmentAngles) )
  201. {
  202. return; //nothing to update
  203. }
  204. m_CachedReflectedData.vLocalOBB_Mins = vMins;
  205. m_CachedReflectedData.vLocalOBB_Maxs = vMaxs;
  206. m_CachedReflectedData.vLocalSpaceAttachmentOrigin = vLocalAttachmentOrigin;
  207. m_CachedReflectedData.qLocalSpaceAttachmentAngles = qLocalAttachmentAngles;
  208. Vector vAttachmentVectors[3];
  209. AngleVectors( qLocalAttachmentAngles, &vAttachmentVectors[0], &vAttachmentVectors[1], &vAttachmentVectors[2] );
  210. float fLargestOBBDiff = vMaxs.x - vMins.x;
  211. for( int i = 1; i != 3; ++i )
  212. {
  213. float fDiff = vMaxs[i] - vMins[i];
  214. if( fDiff > fLargestOBBDiff )
  215. {
  216. fLargestOBBDiff = fDiff;
  217. }
  218. }
  219. fLargestOBBDiff *= 4.0f; //to easily cover diagonal intersection and then some
  220. Vector vClipBuffers[2][10]; //4 starting points, possible to create 1 extra point per cut, 6 cuts
  221. vClipBuffers[0][0] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
  222. vClipBuffers[0][1] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
  223. vClipBuffers[0][2] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
  224. vClipBuffers[0][3] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
  225. int iVertCount = 4;
  226. VPlane vClipPlanes[6];
  227. vClipPlanes[0].Init( Vector( 1.0f, 0.0f, 0.0f ), vMins.x );
  228. vClipPlanes[1].Init( Vector( -1.0f, 0.0f, 0.0f ), -vMaxs.x );
  229. vClipPlanes[2].Init( Vector( 0.0f, 1.0f, 0.0f ), vMins.y );
  230. vClipPlanes[3].Init( Vector( 0.0f, -1.0f, 0.0f ), -vMaxs.y );
  231. vClipPlanes[4].Init( Vector( 0.0f, 0.0f, 1.0f ), vMins.z );
  232. vClipPlanes[5].Init( Vector( 0.0f, 0.0f, -1.0f ), -vMaxs.z );
  233. for( int i = 0; i != 6; ++i )
  234. {
  235. iVertCount = ClipPolyToPlane( vClipBuffers[i & 1], iVertCount, vClipBuffers[(i & 1) ^ 1], vClipPlanes[i].m_Normal, vClipPlanes[i].m_Dist, 0.01f );
  236. }
  237. Assert( iVertCount >= 3 );
  238. m_LocalSpaceReflectionPolygonVertCount = iVertCount;
  239. memcpy( m_LocalSpaceReflectionPolygonVerts, vClipBuffers[0], sizeof( Vector ) * iVertCount );
  240. }
  241. else
  242. {
  243. if( (m_CachedReflectedData.vLocalOBB_Maxs.x == m_fWidth) && (m_CachedReflectedData.vLocalOBB_Maxs.y == m_fHeight) )
  244. return;
  245. m_LocalSpaceReflectionPolygonVertCount = 4;
  246. float fHalfWidth = m_fWidth / 2.0f;
  247. float fHalfHeight = m_fHeight / 2.0f;
  248. m_LocalSpaceReflectionPolygonVerts[0].Init( 0.0f, fHalfWidth, fHalfHeight );
  249. m_LocalSpaceReflectionPolygonVerts[1].Init( 0.0f, -fHalfWidth, fHalfHeight );
  250. m_LocalSpaceReflectionPolygonVerts[2].Init( 0.0f, -fHalfWidth, -fHalfHeight );
  251. m_LocalSpaceReflectionPolygonVerts[3].Init( 0.0f, fHalfWidth, -fHalfHeight );
  252. }
  253. }
  254. #if defined( TEST_ANIMATION )
  255. void CProp_Mirror::Think( void )
  256. {
  257. StudioFrameAdvance();
  258. DispatchAnimEvents( this );
  259. if (IsSequenceFinished() && !SequenceLoops())
  260. {
  261. // ResetSequenceInfo();
  262. // hack to avoid reloading model every frame
  263. m_flAnimTime = gpGlobals->curtime;
  264. m_flPlaybackRate = 0.1f;
  265. m_bSequenceFinished = false;
  266. m_flLastEventCheck = 0;
  267. m_flCycle = 0;
  268. }
  269. SetNextThink( gpGlobals->curtime + 0.1f );
  270. }
  271. #endif
  272. int CProp_Mirror::ComputeFrustumThroughPolygon( const Vector &vVisOrigin, const VPlane *pInputFrustum, int iInputFrustumPlanes, VPlane *pOutputFrustum, int iOutputFrustumMaxPlanes )
  273. {
  274. Vector vTransformedPolyVerts[10];
  275. const matrix3x4_t &matLocalToWorld = CollisionProp()->CollisionToWorldTransform();
  276. for( int i = 0; i != m_LocalSpaceReflectionPolygonVertCount; ++i )
  277. {
  278. VectorTransform( &m_LocalSpaceReflectionPolygonVerts[i].x, matLocalToWorld, &vTransformedPolyVerts[i].x );
  279. }
  280. int iReturnedPlanes = UTIL_CalcFrustumThroughConvexPolygon( vTransformedPolyVerts, m_LocalSpaceReflectionPolygonVertCount, vVisOrigin, pInputFrustum, iInputFrustumPlanes, pOutputFrustum, iOutputFrustumMaxPlanes, 0 );
  281. if( (iReturnedPlanes < iOutputFrustumMaxPlanes) && (iReturnedPlanes != 0) )
  282. {
  283. Vector vForward;
  284. AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward );
  285. vForward = -vForward;
  286. //add the reflection plane as a near plane
  287. pOutputFrustum[iReturnedPlanes].Init( vForward, vForward.Dot( m_CachedReflectedData.vAttachmentOrigin ) );
  288. ++iReturnedPlanes;
  289. }
  290. return iReturnedPlanes;
  291. }
  292. void CProp_Mirror::ComputeSubVisibility( CPVS_Extender **pExtenders, int iExtenderCount, unsigned char *outputPVS, int pvssize, const Vector &vVisOrigin, const VPlane *pVisFrustum, int iVisFrustumPlanes, VisExtensionChain_t *pVisChain, int iAreasNetworked[MAX_MAP_AREAS], int iMaxRecursionsLeft )
  293. {
  294. if( iAreasNetworked[MAX_MAP_AREAS - 1] != -1 ) //early out, can't add any more data if we wanted to
  295. return;
  296. UpdateReflectionPlane();
  297. Vector vForward;
  298. AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward );
  299. if( vForward.Dot( vVisOrigin ) < vForward.Dot( m_CachedReflectedData.vAttachmentOrigin ) )
  300. return; //vis origin is behind the reflection plane
  301. //both test if the portal is within the view frustum, and calculate the new one at the same time
  302. int iFrustumPlanesMax = (iVisFrustumPlanes + m_LocalSpaceReflectionPolygonVertCount + 1);
  303. VPlane *pNewFrustum = (VPlane *)stackalloc( sizeof( VPlane ) * iFrustumPlanesMax );
  304. int iNewFrustumPlanes = ComputeFrustumThroughPolygon( vVisOrigin, pVisFrustum, iVisFrustumPlanes, pNewFrustum, iFrustumPlanesMax );
  305. if( iNewFrustumPlanes == 0 )
  306. {
  307. return;
  308. }
  309. //NDebugOverlay::EntityBounds( this, 0, 255, 0, 100, 0.0f );
  310. int iArea = NetworkProp()->AreaNum();
  311. unsigned char *pPVS = m_pExtenderData->iPVSBits;
  312. if( !m_pExtenderData->bAddedToPVSAlready )
  313. {
  314. bool bFound = false;
  315. for( int i = 0; i != MAX_MAP_AREAS; ++i )
  316. {
  317. if( iAreasNetworked[i] == iArea )
  318. {
  319. bFound = true;
  320. break;
  321. }
  322. if( iAreasNetworked[i] == -1 )
  323. {
  324. bFound = true; //we found it by adding it
  325. iAreasNetworked[i] = iArea;
  326. int iOutputPVSIntSize = pvssize / sizeof( unsigned int );
  327. for( int j = 0; j != iOutputPVSIntSize; ++j )
  328. {
  329. ((unsigned int *)outputPVS)[j] |= ((unsigned int *)pPVS)[j];
  330. }
  331. for( int j = iOutputPVSIntSize * sizeof( unsigned int ); j != pvssize; ++j )
  332. {
  333. outputPVS[j] |= pPVS[j];
  334. }
  335. break;
  336. }
  337. }
  338. Vector vForward;
  339. AngleVectors( m_CachedReflectedData.qAttachmentAngle, &vForward, NULL, NULL );
  340. engine->AddOriginToPVS( m_CachedReflectedData.vAttachmentOrigin + vForward );
  341. m_pExtenderData->bAddedToPVSAlready = true;
  342. }
  343. --iMaxRecursionsLeft;
  344. if( iMaxRecursionsLeft == 0 )
  345. return;
  346. edict_t *myEdict = edict();
  347. VisExtensionChain_t chainNode;
  348. chainNode.m_nArea = iArea;
  349. chainNode.pParentChain = pVisChain;
  350. //transform vis origin to linked space
  351. Vector vTransformedVisOrigin = m_matReflection * vVisOrigin;
  352. Vector vTranslation = m_matReflection.GetTranslation();
  353. //transform the planes into the linked portal space
  354. for( int i = 0; i != iNewFrustumPlanes; ++i )
  355. {
  356. pNewFrustum[i].m_Normal = m_matReflection.ApplyRotation( pNewFrustum[i].m_Normal );
  357. pNewFrustum[i].m_Dist += pNewFrustum[i].m_Normal.Dot( vTranslation );
  358. }
  359. Assert( pPVS != NULL );
  360. //extend the vis by what the linked portal can see
  361. for( int i = 0; i != iExtenderCount; ++i )
  362. {
  363. CPVS_Extender *pExtender = pExtenders[i];
  364. if ( pExtender->GetExtenderEdict() == myEdict )
  365. continue;
  366. if ( pExtender->GetExtenderNetworkProp()->IsInPVS( myEdict, pPVS, (MAX_MAP_LEAFS/8) ) ) //test against linked portal PVS, not aggregate PVS
  367. {
  368. chainNode.pExtender = pExtender;
  369. pExtender->ComputeSubVisibility( pExtenders, iExtenderCount, outputPVS, pvssize, vTransformedVisOrigin, pNewFrustum, iNewFrustumPlanes, &chainNode, iAreasNetworked, iMaxRecursionsLeft );
  370. }
  371. }
  372. }