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.

1845 lines
54 KiB

  1. //===== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "c_rope.h"
  9. #include "beamdraw.h"
  10. #include "view.h"
  11. #include "env_wind_shared.h"
  12. #include "input.h"
  13. #include "rope_helpers.h"
  14. #include "engine/ivmodelinfo.h"
  15. #include "tier0/vprof.h"
  16. #include "c_te_effect_dispatch.h"
  17. #include "collisionutils.h"
  18. #include <keyvalues.h>
  19. #include <bitbuf.h>
  20. #include "utllinkedlist.h"
  21. #include "materialsystem/imaterialsystemhardwareconfig.h"
  22. #include "tier1/callqueue.h"
  23. #include "tier1/memstack.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. //int g_sModelXmasBulb; // holds the sprite index for splattered blood
  27. //Precache the rope shadowdepth material
  28. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheRopes )
  29. PRECACHE( MATERIAL, "cable/rope_shadowdepth" )
  30. //PRECACHE_INDEX( MODEL, "effects/christmas_bulb.vmt", g_sModelXmasBulb )
  31. PRECACHE_REGISTER_END()
  32. void RecvProxy_RecomputeSprings( const CRecvProxyData *pData, void *pStruct, void *pOut )
  33. {
  34. // Have the regular proxy store the data.
  35. RecvProxy_Int32ToInt32( pData, pStruct, pOut );
  36. C_RopeKeyframe *pRope = (C_RopeKeyframe*)pStruct;
  37. pRope->RecomputeSprings();
  38. }
  39. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_RopeKeyframe, DT_RopeKeyframe, CRopeKeyframe )
  40. RecvPropInt( RECVINFO(m_nChangeCount) ),
  41. RecvPropInt( RECVINFO(m_iRopeMaterialModelIndex) ),
  42. RecvPropEHandle( RECVINFO(m_hStartPoint) ),
  43. RecvPropEHandle( RECVINFO(m_hEndPoint) ),
  44. RecvPropInt( RECVINFO(m_iStartAttachment) ),
  45. RecvPropInt( RECVINFO(m_iEndAttachment) ),
  46. RecvPropInt( RECVINFO(m_fLockedPoints) ),
  47. RecvPropInt( RECVINFO(m_Slack), 0, RecvProxy_RecomputeSprings ),
  48. RecvPropInt( RECVINFO(m_RopeLength), 0, RecvProxy_RecomputeSprings ),
  49. RecvPropInt( RECVINFO(m_RopeFlags) ),
  50. RecvPropFloat( RECVINFO(m_TextureScale) ),
  51. RecvPropInt( RECVINFO(m_nSegments) ),
  52. RecvPropBool( RECVINFO(m_bConstrainBetweenEndpoints) ),
  53. RecvPropInt( RECVINFO(m_Subdiv) ),
  54. RecvPropFloat( RECVINFO(m_Width) ),
  55. RecvPropFloat( RECVINFO(m_flScrollSpeed) ),
  56. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  57. RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ),
  58. RecvPropInt( RECVINFO( m_iParentAttachment ) ),
  59. RecvPropInt( RECVINFO( m_iDefaultRopeMaterialModelIndex ) ),
  60. #if 1
  61. // #ifndef _GAMECONSOLE -- X360 client and Win32 XLSP dedicated server need equivalent SendTables
  62. RecvPropInt( RECVINFO( m_nMinCPULevel ) ),
  63. RecvPropInt( RECVINFO( m_nMaxCPULevel ) ),
  64. RecvPropInt( RECVINFO( m_nMinGPULevel ) ),
  65. RecvPropInt( RECVINFO( m_nMaxGPULevel ) ),
  66. #endif
  67. END_RECV_TABLE()
  68. #define ROPE_IMPULSE_SCALE 20
  69. #define ROPE_IMPULSE_DECAY 0.95
  70. static ConVar rope_shake( "rope_shake", "0" );
  71. static ConVar rope_subdiv( "rope_subdiv", "2", FCVAR_MATERIAL_SYSTEM_THREAD, "Rope subdivision amount", true, 0, true, MAX_ROPE_SUBDIVS );
  72. static ConVar rope_collide( "rope_collide", "1", 0, "Collide rope with the world" );
  73. static ConVar rope_smooth( "rope_smooth", "1", 0, "Do an antialiasing effect on ropes" );
  74. static ConVar rope_smooth_enlarge( "rope_smooth_enlarge", "1.4", 0, "How much to enlarge ropes in screen space for antialiasing effect" );
  75. static ConVar rope_smooth_minwidth( "rope_smooth_minwidth", "0.3", 0, "When using smoothing, this is the min screenspace width it lets a rope shrink to" );
  76. static ConVar rope_smooth_minalpha( "rope_smooth_minalpha", "0.2", 0, "Alpha for rope antialiasing effect" );
  77. static ConVar rope_smooth_maxalphawidth( "rope_smooth_maxalphawidth", "1.75" );
  78. static ConVar rope_smooth_maxalpha( "rope_smooth_maxalpha", "0.5", 0, "Alpha for rope antialiasing effect" );
  79. static ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT ); // get it from the engine
  80. static ConVar r_drawropes( "r_drawropes", "1", FCVAR_CHEAT );
  81. static ConVar r_ropetranslucent( "r_ropetranslucent", "1");
  82. static ConVar r_rope_holiday_light_scale( "r_rope_holiday_light_scale", "0.14", FCVAR_DEVELOPMENTONLY );
  83. //static ConVar r_ropes_holiday_lights_allowed( "r_ropes_holiday_lights_allowed", "0", FCVAR_DEVELOPMENTONLY );
  84. static ConVar r_ropes_holiday_lights_type( "r_ropes_holiday_lights_type", "0", FCVAR_DEVELOPMENTONLY, "0 == sprites, 1 == models" );
  85. static ConVar r_ropes_holiday_max_dist_to_draw( "r_ropes_holiday_max_dist_to_draw", "700", FCVAR_DEVELOPMENTONLY, "If a ropes length (from one end to the other, not counting slack), exceeds this distance, it won't have lights." );
  86. static ConVar rope_wind_dist( "rope_wind_dist", "1000", 0, "Don't use CPU applying small wind gusts to ropes when they're past this distance." );
  87. static ConVar rope_averagelight( "rope_averagelight", "1", 0, "Makes ropes use average of cubemap lighting instead of max intensity." );
  88. static ConVar rope_rendersolid( "rope_rendersolid", "1" );
  89. static ConVar rope_solid_minwidth( "rope_solid_minwidth", "0.3" );
  90. static ConVar rope_solid_maxwidth( "rope_solid_maxwidth", "1" );
  91. static ConVar rope_solid_minalpha( "rope_solid_minalpha", "0.0" );
  92. static ConVar rope_solid_maxalpha( "rope_solid_maxalpha", "1" );
  93. static int g_nRopePointsSimulated;
  94. static IMaterial *g_pSplineCableShadowdepth = NULL;
  95. // Active ropes.
  96. CUtlLinkedList<C_RopeKeyframe*, int> g_Ropes;
  97. static Vector g_FullBright_LightValues[ROPE_MAX_SEGMENTS];
  98. class CFullBrightLightValuesInit
  99. {
  100. public:
  101. CFullBrightLightValuesInit()
  102. {
  103. for( int i=0; i < ROPE_MAX_SEGMENTS; i++ )
  104. g_FullBright_LightValues[i].Init( 1, 1, 1 );
  105. }
  106. } g_FullBrightLightValuesInit;
  107. // This can be exposed through the entity if we ever care.
  108. static float g_flLockAmount = 0.1;
  109. static float g_flLockFalloff = 0.3;
  110. class CRopeDelayedEffects
  111. {
  112. public:
  113. float m_flTimeProcessedOnMainThread; // gpGlobals->curtime
  114. CThreadFastMutex m_mtx;
  115. CUtlVector< CEffectData > m_arrEffects;
  116. }
  117. g_RopeDelayedEffects;
  118. //=============================================================================
  119. //
  120. // Rope mananger.
  121. //
  122. class CRopeManager : public IRopeManager
  123. {
  124. public:
  125. CRopeManager();
  126. ~CRopeManager();
  127. void ResetRenderCache( void );
  128. void AddToRenderCache( C_RopeKeyframe *pRope );
  129. void DrawRenderCache( IMatRenderContext *pRenderContext, bool bShadowDepth );
  130. void SetHolidayLightMode( bool bHoliday ) { m_bDrawHolidayLights = bHoliday; }
  131. bool IsHolidayLightMode( void );
  132. int GetHolidayLightStyle( void );
  133. enum { MAX_ROPE_RENDERCACHE = 128 };
  134. void RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope );
  135. private:
  136. struct RopeRenderData_t
  137. {
  138. IMaterial *m_pSolidMaterial;
  139. int m_nCacheCount;
  140. C_RopeKeyframe *m_aCache[MAX_ROPE_RENDERCACHE];
  141. };
  142. struct RopeQueuedRenderCache_t
  143. {
  144. RopeRenderData_t *pCaches;
  145. int iCacheCount;
  146. CThreadFastMutex *m_pRopeDataMutex;
  147. RopeQueuedRenderCache_t( void ) : pCaches(NULL), iCacheCount(0) { };
  148. };
  149. void DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_t *pRenderCache, int nRenderCacheCount, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedData, CThreadFastMutex *pRopeDataMutex );
  150. CUtlVector<RopeRenderData_t> m_aRenderCache;
  151. //in queued material system mode we need to store off data for later use.
  152. IMaterial* m_pDepthWriteMaterial;
  153. CUtlLinkedList<RopeQueuedRenderCache_t> m_RopeQueuedRenderCaches;
  154. CThreadFastMutex m_RopeQueuedRenderCaches_Mutex; //mutex just for changing m_RopeQueuedRenderCaches
  155. bool m_bDrawHolidayLights;
  156. bool m_bHolidayInitialized;
  157. int m_nHolidayLightsStyle;
  158. };
  159. static CRopeManager s_RopeManager;
  160. IRopeManager *RopeManager()
  161. {
  162. return &s_RopeManager;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Purpose:
  166. //-----------------------------------------------------------------------------
  167. CRopeManager::CRopeManager()
  168. {
  169. m_aRenderCache.Purge();
  170. m_pDepthWriteMaterial = NULL;
  171. m_bDrawHolidayLights = false;
  172. m_bHolidayInitialized = false;
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose:
  176. //-----------------------------------------------------------------------------
  177. CRopeManager::~CRopeManager()
  178. {
  179. m_aRenderCache.Purge();
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CRopeManager::ResetRenderCache( void )
  185. {
  186. int nRenderCacheCount = m_aRenderCache.Count();
  187. for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache )
  188. {
  189. m_aRenderCache[iRenderCache].m_nCacheCount = 0;
  190. }
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose:
  194. //-----------------------------------------------------------------------------
  195. void CRopeManager::AddToRenderCache( C_RopeKeyframe *pRope )
  196. {
  197. if( !pRope->GetSolidMaterial() )
  198. {
  199. return;
  200. }
  201. // Find the current rope list.
  202. int iRenderCache = 0;
  203. int nRenderCacheCount = m_aRenderCache.Count();
  204. for ( ; iRenderCache < nRenderCacheCount; ++iRenderCache )
  205. {
  206. if ( pRope->GetSolidMaterial() == m_aRenderCache[iRenderCache].m_pSolidMaterial )
  207. break;
  208. }
  209. // A full rope list should have been generate in CreateRenderCache
  210. // If we didn't find one, then allocate the mofo.
  211. if ( iRenderCache == nRenderCacheCount )
  212. {
  213. int iRenderCache = m_aRenderCache.AddToTail();
  214. m_aRenderCache[iRenderCache].m_pSolidMaterial = pRope->GetSolidMaterial();
  215. m_aRenderCache[iRenderCache].m_nCacheCount = 0;
  216. }
  217. if ( m_aRenderCache[iRenderCache].m_nCacheCount >= MAX_ROPE_RENDERCACHE )
  218. {
  219. Warning( "CRopeManager::AddToRenderCache count to large for cache!\n" );
  220. return;
  221. }
  222. m_aRenderCache[iRenderCache].m_aCache[m_aRenderCache[iRenderCache].m_nCacheCount] = pRope;
  223. ++m_aRenderCache[iRenderCache].m_nCacheCount;
  224. }
  225. #define OUTPUT_2SPLINE_VERTS( v, t, u ) \
  226. meshBuilder.Color4ub( v, nRed, nGreen, nBlue, nAlpha ); \
  227. meshBuilder.Position3f( v, (t), u, 0 ); \
  228. meshBuilder.TexCoord4fv( v, 0, vecP0.Base() ); \
  229. meshBuilder.TexCoord4fv( v, 1, vecP1.Base() ); \
  230. meshBuilder.TexCoord4fv( v, 2, vecP2.Base() ); \
  231. meshBuilder.TexCoord4fv( v, 3, vecP3.Base() ); \
  232. meshBuilder.Color4ub( v + 1, nRed, nGreen, nBlue, nAlpha ); \
  233. meshBuilder.Position3f( v + 1, (t), u, 1 ); \
  234. meshBuilder.TexCoord4fv( v + 1, 0, vecP0.Base() ); \
  235. meshBuilder.TexCoord4fv( v + 1, 1, vecP1.Base() ); \
  236. meshBuilder.TexCoord4fv( v + 1, 2, vecP2.Base() ); \
  237. meshBuilder.TexCoord4fv( v + 1, 3, vecP3.Base() ); \
  238. v += 2;
  239. #define OUTPUT_SPLINE_QUAD( i, c ) \
  240. meshBuilder.FastIndex( i, c ); \
  241. meshBuilder.FastIndex( i + 1, c + 1 ); \
  242. meshBuilder.FastIndex( i + 2, c + 2 ); \
  243. meshBuilder.FastIndex( i + 3, c + 1 ); \
  244. meshBuilder.FastIndex( i + 4, c + 3 ); \
  245. meshBuilder.FastIndex( i + 5, c + 2 ); \
  246. i += 6;
  247. void CRopeManager::DrawRenderCache_NonQueued( bool bShadowDepth, RopeRenderData_t *pRenderCache, int nRenderCacheCount, const Vector &vCurrentViewForward, const Vector &vCurrentViewOrigin, C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedData, CThreadFastMutex *pRopeDataMutex )
  248. {
  249. VPROF_BUDGET( "CRopeManager::DrawRenderCache", VPROF_BUDGETGROUP_ROPES );
  250. CThreadFastMutex dummyMutex;
  251. if( pRopeDataMutex == NULL )
  252. pRopeDataMutex = &dummyMutex;
  253. if ( bShadowDepth && !m_pDepthWriteMaterial && g_pMaterialSystem )
  254. {
  255. KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
  256. pVMTKeyValues->SetInt( "$no_fullbright", 1 );
  257. pVMTKeyValues->SetInt( "$alphatest", 0 );
  258. pVMTKeyValues->SetInt( "$nocull", 1 );
  259. m_pDepthWriteMaterial = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues );
  260. }
  261. CMatRenderContextPtr pRenderContext( materials );
  262. // UNDONE: needs to use the queued data
  263. do
  264. {
  265. AUTO_LOCK_FM( *pRopeDataMutex );
  266. int defaultSubdiv = rope_subdiv.GetInt();
  267. for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache )
  268. {
  269. int nCacheCount = pRenderCache[iRenderCache].m_nCacheCount;
  270. int nTotalVerts = 0;
  271. int nTotalIndices = 0;
  272. for ( int iCache = 0; iCache < nCacheCount; ++iCache )
  273. {
  274. C_RopeKeyframe *pRope = pRenderCache[iRenderCache].m_aCache[iCache];
  275. if ( pRope )
  276. {
  277. int segs = pRope->m_RopePhysics.NumNodes()-1;
  278. int nSubdivCount = (pRope->m_Subdiv != 255 ? pRope->m_Subdiv : defaultSubdiv) + 1;
  279. nTotalVerts += ((2 * nSubdivCount) * segs) + 2;
  280. nTotalIndices += (6 * nSubdivCount) * segs;
  281. }
  282. }
  283. if ( nTotalVerts == 0 )
  284. continue;
  285. IMaterial *pMaterial = bShadowDepth ? g_pSplineCableShadowdepth : pRenderCache[iRenderCache].m_pSolidMaterial;
  286. // Need to make sure that all rope materials use the splinerope shader since there are a lot of assumptions about how the shader interfaces with this code.
  287. AssertOnce( V_stricmp( pMaterial->GetShaderName(), "splinerope" ) == 0 );
  288. pRenderContext->Bind( pMaterial );
  289. int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
  290. int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
  291. IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
  292. CMeshBuilder meshBuilder;
  293. int meshVertCount = MIN(nTotalVerts, nMaxVertices);
  294. int meshIndexCount = MIN(nTotalIndices, nMaxIndices);
  295. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, meshVertCount, meshIndexCount );
  296. if ( meshBuilder.m_ActualVertexSize == 0 )
  297. {
  298. // We're likely in alt+tab, and since we're using fast vertex/index routines, we need to see if we're writing into valid vertex data
  299. meshBuilder.End();
  300. break;
  301. }
  302. int nCurIDX = 0;
  303. int availableVerts = meshVertCount;
  304. int availableIndices = meshIndexCount;
  305. float flLastU = 1.0f;
  306. int nVertices = 0;
  307. int nIndices = 0;
  308. for ( int iCache = 0; iCache < nCacheCount; ++iCache )
  309. {
  310. C_RopeKeyframe *pRope = pRenderCache[iRenderCache].m_aCache[iCache];
  311. if ( pRope )
  312. {
  313. CSimplePhysics::CNode *pNode = pRope->m_RopePhysics.GetFirstNode();
  314. int nSegmentsToRender = pRope->m_RopePhysics.NumNodes()-1;
  315. if ( !nSegmentsToRender )
  316. continue;
  317. int nParticles = pRope->m_RopePhysics.NumNodes();
  318. int nSubdivCount = (pRope->m_Subdiv != 255 ? pRope->m_Subdiv : defaultSubdiv) + 1;
  319. int nNumIndicesPerSegment = 6 * nSubdivCount;
  320. int nNumVerticesPerSegment = 2 * nSubdivCount;
  321. int nSegmentsAvailableInBuffer = MIN( ( availableVerts - 2 ) / nNumVerticesPerSegment,
  322. ( availableIndices ) / nNumIndicesPerSegment );
  323. int segmentsInBuffer = MIN(nSegmentsAvailableInBuffer,nSegmentsToRender);
  324. availableIndices -= nNumIndicesPerSegment * segmentsInBuffer;
  325. availableVerts -= 2 + (nNumVerticesPerSegment * segmentsInBuffer);
  326. float width = pRope->m_Width;
  327. Vector vModColor = pRope->m_vColorMod;
  328. Vector *pColors = pRope->m_LightValues;
  329. // Figure out texture scale.
  330. float flPixelsPerInch = 4.0f / pRope->m_TextureScale;
  331. // This is the total number of texels for the length of the whole rope.
  332. float flTotalTexCoord = flPixelsPerInch * ( pRope->m_RopeLength + pRope->m_Slack + ROPESLACK_FUDGEFACTOR );
  333. int nTotalPoints = (nSegmentsToRender * (nSubdivCount-1)) + 1;
  334. float flDU = ( flTotalTexCoord / nTotalPoints ) / ( float )pRope->m_TextureHeight;
  335. float flU = pRope->m_flCurScroll;
  336. float m_flTStep = 1.0f / float(nSubdivCount);
  337. bool bFirstPoint = true;
  338. // initialize first spline segment
  339. Vector4D vecP1;
  340. Vector4D vecP2;
  341. vecP1.Init( pNode[0].m_vPredicted, pRope->m_Width );
  342. vecP2.Init( pNode[1].m_vPredicted, pRope->m_Width );
  343. Vector4D vecP0 = vecP1;
  344. uint8 nRed = 0;
  345. uint8 nGreen = 0;
  346. uint8 nBlue = 0;
  347. uint8 nAlpha = 255;
  348. Vector4D vecDelta = vecP2;
  349. vecDelta -= vecP1;
  350. vecP0 -= vecDelta;
  351. Vector4D vecP3;
  352. if ( nParticles < 3 )
  353. {
  354. vecP3 = vecP2;
  355. vecP3 += vecDelta;
  356. }
  357. else
  358. {
  359. vecP3.Init( pNode[2].m_vPredicted, width );
  360. }
  361. int nPnt = 3;
  362. int nColor = 1;
  363. Vector vColor0( pColors[0].x * vModColor.x, pColors[0].y * vModColor.y, pColors[0].z * vModColor.z );
  364. Vector vColor1( pColors[1].x * vModColor.x, pColors[1].y * vModColor.y, pColors[1].z * vModColor.z );
  365. float flT = 0;
  366. do
  367. {
  368. if ( ! nSegmentsAvailableInBuffer )
  369. {
  370. meshBuilder.AdvanceVerticesF<VTX_HAVEPOS | VTX_HAVECOLOR, 5>( nVertices );
  371. meshBuilder.AdvanceIndices( nIndices );
  372. meshBuilder.End();
  373. pMesh->Draw();
  374. nTotalVerts -= (meshVertCount - availableVerts);
  375. nTotalIndices -= (meshIndexCount - availableIndices);
  376. meshVertCount = MIN(nTotalVerts, nMaxVertices);
  377. meshIndexCount = MIN(nTotalIndices, nMaxIndices);
  378. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, meshVertCount, meshIndexCount );
  379. nVertices = 0;
  380. nIndices = 0;
  381. availableVerts = meshVertCount;
  382. availableIndices = meshIndexCount;
  383. // copy the last emitted points
  384. OUTPUT_2SPLINE_VERTS( nVertices, flT, flLastU );
  385. nSegmentsAvailableInBuffer = MIN( ( availableVerts - 2 ) / nNumVerticesPerSegment,
  386. availableIndices / nNumIndicesPerSegment );
  387. nCurIDX = 0;
  388. }
  389. nSegmentsAvailableInBuffer--;
  390. flT = 0.;
  391. for( int nSlice = 0 ; nSlice < nSubdivCount; nSlice++ )
  392. {
  393. float omt = 1.0f - flT;
  394. nRed = FastFToC( (vColor0.x * omt) + (vColor1.x*flT) );
  395. nGreen = FastFToC( (vColor0.y * omt) + (vColor1.y*flT) );
  396. nBlue = FastFToC( (vColor0.z * omt) + (vColor1.z*flT) );
  397. OUTPUT_2SPLINE_VERTS( nVertices, flT, flU );
  398. flT += m_flTStep;
  399. flU += flDU;
  400. if ( ! bFirstPoint )
  401. {
  402. OUTPUT_SPLINE_QUAD( nIndices, nCurIDX );
  403. nCurIDX += 2;
  404. }
  405. bFirstPoint = false;
  406. }
  407. // next segment
  408. vColor0 = vColor1;
  409. if ( nColor < nParticles-1 )
  410. {
  411. nColor++;
  412. vColor1.Init( pColors[nColor].x * vModColor.x, pColors[nColor].y * vModColor.y, pColors[nColor].z * vModColor.z );
  413. }
  414. if ( nSegmentsToRender > 1 )
  415. {
  416. vecP0 = vecP1;
  417. vecP1 = vecP2;
  418. vecP2 = vecP3;
  419. if ( nPnt < nParticles )
  420. {
  421. vecP3.AsVector3D() = pNode[nPnt].m_vPredicted;
  422. nPnt++;
  423. }
  424. else
  425. {
  426. // fake last point by extrapolating
  427. vecP3 += vecP2;
  428. vecP3 -= vecP1;
  429. }
  430. }
  431. } while( --nSegmentsToRender );
  432. int nSegsToRender = pRope->m_RopePhysics.NumNodes()-1;
  433. Vector vecRopeStart = pNode[0].m_vPredicted;
  434. Vector vecRopeEnd = pNode[nSegsToRender].m_vPredicted;
  435. float flDist = (vecRopeStart - vecRopeEnd).Length();
  436. if ( flDist < r_ropes_holiday_max_dist_to_draw.GetFloat() )
  437. {
  438. for ( int nSeg = 1; nSeg < nSegsToRender; ++nSeg )
  439. {
  440. CEffectData data;
  441. if ( RopeManager()->IsHolidayLightMode() &&
  442. pRope->m_ropeType == ROPE_TYPE_DEFAULT &&
  443. pRope->m_iDefaultRopeMaterialModelIndex == pRope->m_iRopeMaterialModelIndex &&
  444. pRope->m_RopePhysics.NumNodes() >= 5 &&
  445. m_RopeQueuedRenderCaches.Count() == 1 &&
  446. r_rope_holiday_light_scale.GetFloat() > 0.0f )
  447. {
  448. int xy = ( int )( pNode[nSeg].m_vPredicted.x + pNode[nSeg].m_vPredicted.y );
  449. data.m_nMaterial = xy;
  450. int z = ( int )pNode[nSeg].m_vPredicted.z;
  451. data.m_nHitBox = ( z << 8 );
  452. data.m_flScale = r_rope_holiday_light_scale.GetFloat();
  453. data.m_vOrigin = pNode[nSeg].m_vPredicted;
  454. {
  455. AUTO_LOCK( g_RopeDelayedEffects.m_mtx );
  456. g_RopeDelayedEffects.m_arrEffects.AddToTail( data );
  457. }
  458. }
  459. }
  460. }
  461. // output last piece
  462. OUTPUT_2SPLINE_VERTS( nVertices, 1.0, flU );
  463. OUTPUT_SPLINE_QUAD( nIndices, nCurIDX );
  464. nCurIDX += 4;
  465. flLastU = flU;
  466. }
  467. }
  468. meshBuilder.AdvanceVerticesF<VTX_HAVEPOS | VTX_HAVECOLOR, 5>( nVertices );
  469. meshBuilder.AdvanceIndices( nIndices );
  470. meshBuilder.End();
  471. pMesh->Draw();
  472. }
  473. }
  474. while( false ); //wrap in a single execution loop for bail out scenarios
  475. m_RopeQueuedRenderCaches_Mutex.Lock();
  476. if( pBuildRopeQueuedData && (m_RopeQueuedRenderCaches.Count() != 0) )
  477. {
  478. unsigned short iHeadIndex = m_RopeQueuedRenderCaches.Head();
  479. delete m_RopeQueuedRenderCaches[iHeadIndex].m_pRopeDataMutex;
  480. m_RopeQueuedRenderCaches.Remove( iHeadIndex );
  481. }
  482. m_RopeQueuedRenderCaches_Mutex.Unlock();
  483. }
  484. ConVar r_queued_ropes( "r_queued_ropes", "1" );
  485. //-----------------------------------------------------------------------------
  486. // Purpose:
  487. //-----------------------------------------------------------------------------
  488. void CRopeManager::DrawRenderCache( IMatRenderContext *pRenderContext, bool bShadowDepth )
  489. {
  490. int iRenderCacheCount = m_aRenderCache.Count();
  491. if( iRenderCacheCount == 0 )
  492. return;
  493. // Check to see if we want to render the ropes.
  494. if( !r_drawropes.GetBool() )
  495. return;
  496. Vector vForward = CurrentViewForward();
  497. Vector vOrigin = CurrentViewOrigin();
  498. ICallQueue *pCallQueue;
  499. if( r_queued_ropes.GetBool() && (pCallQueue = pRenderContext->GetCallQueue()) != NULL )
  500. {
  501. //material queue available and desired
  502. CRopeManager::RopeRenderData_t *pRenderCache = m_aRenderCache.Base();
  503. int iRopeCount = 0;
  504. int iNodeCount = 0;
  505. for( int i = 0; i != iRenderCacheCount; ++i )
  506. {
  507. CRopeManager::RopeRenderData_t *pCache = &pRenderCache[i];
  508. int iCacheCount = pCache->m_nCacheCount;
  509. iRopeCount += iCacheCount;
  510. for( int j = 0; j != iCacheCount; ++j )
  511. {
  512. C_RopeKeyframe *pRope = pCache->m_aCache[j];
  513. if( pRope )
  514. iNodeCount += pRope->m_RopePhysics.NumNodes();
  515. else
  516. --iRopeCount;
  517. }
  518. }
  519. if( iRopeCount == 0 )
  520. return; //nothing to draw
  521. size_t iMemoryNeeded = (iRenderCacheCount * sizeof(CRopeManager::RopeRenderData_t)) +
  522. (iRopeCount * sizeof(C_RopeKeyframe::BuildRopeQueuedData_t)) +
  523. (iNodeCount * (sizeof(Vector) * 2));
  524. CMatRenderData< byte > rd(pRenderContext, iMemoryNeeded);
  525. void *pMemory = rd.Base();
  526. CRopeManager::RopeRenderData_t *pRenderCachesStart = (CRopeManager::RopeRenderData_t *)pMemory;
  527. C_RopeKeyframe::BuildRopeQueuedData_t *pBuildRopeQueuedDataStart = (C_RopeKeyframe::BuildRopeQueuedData_t *)(pRenderCachesStart + iRenderCacheCount);
  528. Vector *pVectorDataStart = (Vector *)(pBuildRopeQueuedDataStart + iRopeCount);
  529. //memcpy( pRenderCachesStart, m_aRenderCache.Base(), iRenderCacheCount * sizeof( CRopeManager::RopeRenderData_t ) );
  530. RopeQueuedRenderCache_t cache;
  531. cache.pCaches = pRenderCachesStart;
  532. cache.iCacheCount = iRenderCacheCount;
  533. cache.m_pRopeDataMutex = new CThreadFastMutex;
  534. C_RopeKeyframe::BuildRopeQueuedData_t *pWriteRopeQueuedData = pBuildRopeQueuedDataStart;
  535. Vector *pVectorWrite = (Vector *)pVectorDataStart;
  536. //Setup the rest of our data. This writes to two separate areas of memory at the same time. One area for the C_RopeKeyframe::BuildRopeQueuedData_t array, the other for mini-arrays of vector data
  537. for( int i = 0; i != iRenderCacheCount; ++i )
  538. {
  539. CRopeManager::RopeRenderData_t *pReadCache = &pRenderCache[i];
  540. CRopeManager::RopeRenderData_t *pWriteCache = &pRenderCachesStart[i];
  541. int iCacheCount = pReadCache->m_nCacheCount;
  542. pWriteCache->m_nCacheCount = 0;
  543. pWriteCache->m_pSolidMaterial = pReadCache->m_pSolidMaterial;
  544. for( int j = 0; j != iCacheCount; ++j )
  545. {
  546. C_RopeKeyframe *pRope = pReadCache->m_aCache[j];
  547. if( pRope == NULL )
  548. continue;
  549. pWriteCache->m_aCache[pWriteCache->m_nCacheCount] = pRope;
  550. ++pWriteCache->m_nCacheCount;
  551. int iNodes = pRope->m_RopePhysics.NumNodes();
  552. //setup the C_RopeKeyframe::BuildRopeQueuedData_t struct
  553. pWriteRopeQueuedData->m_iNodeCount = pRope->m_RopePhysics.NumNodes();
  554. pWriteRopeQueuedData->m_vColorMod = pRope->m_vColorMod;
  555. pWriteRopeQueuedData->m_RopeLength = pRope->m_RopeLength;
  556. pWriteRopeQueuedData->m_Slack = pRope->m_Slack;
  557. pWriteRopeQueuedData->m_pPredictedPositions = pVectorWrite;
  558. pWriteRopeQueuedData->m_pLightValues = pVectorWrite + iNodes;
  559. ++pWriteRopeQueuedData;
  560. //make two arrays, one of predicted positions followed immediately by light values
  561. for( int k = 0; k != iNodes; ++k )
  562. {
  563. pVectorWrite[0] = pRope->m_RopePhysics.GetNode( k )->m_vPredicted;
  564. pVectorWrite[iNodes] = pRope->m_LightValues[k];
  565. ++pVectorWrite;
  566. }
  567. pVectorWrite += iNodes; //so we don't overwrite the light values with the next rope's predicted positions
  568. }
  569. }
  570. m_RopeQueuedRenderCaches_Mutex.Lock();
  571. unsigned short iLLIndex = m_RopeQueuedRenderCaches.AddToTail( cache );
  572. CThreadFastMutex *pRopeDataMutex = m_RopeQueuedRenderCaches[iLLIndex].m_pRopeDataMutex;
  573. m_RopeQueuedRenderCaches_Mutex.Unlock();
  574. Assert( ((void *)pVectorWrite == (void *)(((uint8 *)pMemory) + iMemoryNeeded)) && ((void *)pWriteRopeQueuedData == (void *)pVectorDataStart));
  575. pCallQueue->QueueCall( this, &CRopeManager::DrawRenderCache_NonQueued, bShadowDepth, pRenderCachesStart, iRenderCacheCount, vForward, vOrigin, pBuildRopeQueuedDataStart, pRopeDataMutex );
  576. // if ( IsHolidayLightMode() )
  577. // {
  578. // // With holiday lights we need to also build the ropes non-queued without rendering them
  579. // DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL, NULL );
  580. // }
  581. }
  582. else
  583. {
  584. DrawRenderCache_NonQueued( bShadowDepth, m_aRenderCache.Base(), iRenderCacheCount, vForward, vOrigin, NULL, NULL );
  585. }
  586. }
  587. bool CRopeManager::IsHolidayLightMode( void )
  588. {
  589. /* Removed for partner depot */
  590. bool bDrawHolidayLights = false;
  591. return bDrawHolidayLights;
  592. }
  593. int CRopeManager::GetHolidayLightStyle( void )
  594. {
  595. return r_ropes_holiday_lights_type.GetInt();
  596. }
  597. void CRopeManager::RemoveRopeFromQueuedRenderCaches( C_RopeKeyframe *pRope )
  598. {
  599. //remove this rope from queued render caches
  600. AUTO_LOCK_FM( m_RopeQueuedRenderCaches_Mutex );
  601. int index = m_RopeQueuedRenderCaches.Head();
  602. while( m_RopeQueuedRenderCaches.IsValidIndex( index ) )
  603. {
  604. RopeQueuedRenderCache_t &RenderCacheData = m_RopeQueuedRenderCaches[index];
  605. for( int i = 0; i != RenderCacheData.iCacheCount; ++i )
  606. {
  607. RopeRenderData_t *pCache = &RenderCacheData.pCaches[i];
  608. for( int j = 0; j != pCache->m_nCacheCount; ++j )
  609. {
  610. if( pCache->m_aCache[j] == pRope )
  611. {
  612. RenderCacheData.m_pRopeDataMutex->Lock();
  613. pCache->m_aCache[j] = NULL;
  614. RenderCacheData.m_pRopeDataMutex->Unlock();
  615. }
  616. }
  617. }
  618. index = m_RopeQueuedRenderCaches.Next( index );
  619. }
  620. }
  621. //=============================================================================
  622. // ------------------------------------------------------------------------------------ //
  623. // Global functions.
  624. // ------------------------------------------------------------------------------------ //
  625. void Rope_ResetCounters()
  626. {
  627. g_nRopePointsSimulated = 0;
  628. }
  629. // ------------------------------------------------------------------------------------ //
  630. // This handles the rope shake command.
  631. // ------------------------------------------------------------------------------------ //
  632. void ShakeRopesCallback( const CEffectData &data )
  633. {
  634. Vector vCenter = data.m_vOrigin;
  635. float flRadius = data.m_flRadius;
  636. float flMagnitude = data.m_flMagnitude;
  637. // Now find any nearby ropes and shake them.
  638. FOR_EACH_LL( g_Ropes, i )
  639. {
  640. C_RopeKeyframe *pRope = g_Ropes[i];
  641. pRope->ShakeRope( vCenter, flRadius, flMagnitude );
  642. }
  643. }
  644. DECLARE_CLIENT_EFFECT( ShakeRopes, ShakeRopesCallback )
  645. // ------------------------------------------------------------------------------------ //
  646. // C_RopeKeyframe::CPhysicsDelegate
  647. // ------------------------------------------------------------------------------------ //
  648. #define WIND_FORCE_FACTOR 10
  649. void C_RopeKeyframe::CPhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel )
  650. {
  651. // Gravity.
  652. if ( !( m_pKeyframe->GetRopeFlags() & ROPE_NO_GRAVITY ) )
  653. {
  654. pAccel->Init( ROPE_GRAVITY );
  655. }
  656. if( !m_pKeyframe->m_LinksTouchingSomething[iNode] && m_pKeyframe->m_bApplyWind)
  657. {
  658. Vector vecWindVel;
  659. GetWindspeedAtTime(gpGlobals->curtime, vecWindVel);
  660. if ( vecWindVel.LengthSqr() > 0 )
  661. {
  662. Vector vecWindAccel;
  663. VectorMA( *pAccel, WIND_FORCE_FACTOR, vecWindVel, *pAccel );
  664. }
  665. else
  666. {
  667. if ( ( m_pKeyframe->m_flCurrentGustLifetime != 0.0f ) && ( m_pKeyframe->m_flCurrentGustTimer < m_pKeyframe->m_flCurrentGustLifetime ) )
  668. {
  669. float div = m_pKeyframe->m_flCurrentGustTimer / m_pKeyframe->m_flCurrentGustLifetime;
  670. float scale = 1 - cos( div * M_PI );
  671. *pAccel += m_pKeyframe->m_vWindDir * scale;
  672. }
  673. }
  674. }
  675. // HACK.. shake the rope around.
  676. static float scale=15000;
  677. if( rope_shake.GetInt() )
  678. {
  679. *pAccel += RandomVector( -scale, scale );
  680. }
  681. // Apply any instananeous forces and reset
  682. *pAccel += ROPE_IMPULSE_SCALE * m_pKeyframe->m_vecImpulse;
  683. m_pKeyframe->m_vecImpulse *= ROPE_IMPULSE_DECAY;
  684. if ( m_pKeyframe->m_vecImpulse.LengthSqr() < 0.1f )
  685. {
  686. m_pKeyframe->m_vecImpulse = vec3_origin;
  687. }
  688. }
  689. void LockNodeDirection(
  690. CSimplePhysics::CNode *pNodes,
  691. int parity,
  692. int nFalloffNodes,
  693. float flLockAmount,
  694. float flLockFalloff,
  695. const Vector &vIdealDir )
  696. {
  697. for ( int i=0; i < nFalloffNodes; i++ )
  698. {
  699. Vector &v0 = pNodes[i*parity].m_vPos;
  700. Vector &v1 = pNodes[(i+1)*parity].m_vPos;
  701. Vector vDir = v1 - v0;
  702. float len = vDir.Length();
  703. if ( len > 0.0001f )
  704. {
  705. vDir /= len;
  706. Vector vActual;
  707. VectorLerp( vDir, vIdealDir, flLockAmount, vActual );
  708. v1 = v0 + vActual * len;
  709. flLockAmount *= flLockFalloff;
  710. }
  711. }
  712. }
  713. void C_RopeKeyframe::CPhysicsDelegate::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes )
  714. {
  715. VPROF( "CPhysicsDelegate::ApplyConstraints" );
  716. // Collide with the world.
  717. if( ((m_pKeyframe->m_RopeFlags & ROPE_COLLIDE) &&
  718. rope_collide.GetInt()) ||
  719. (rope_collide.GetInt() == 2) )
  720. {
  721. CTraceFilterWorldOnly traceFilter;
  722. for( int i=0; i < nNodes; i++ )
  723. {
  724. CSimplePhysics::CNode *pNode = &pNodes[i];
  725. int iIteration;
  726. const int nIterations = 10;
  727. for( iIteration=0; iIteration < nIterations; iIteration++ )
  728. {
  729. trace_t trace;
  730. UTIL_TraceHull( pNode->m_vPrevPos, pNode->m_vPos,
  731. Vector(-2,-2,-2), Vector(2,2,2), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  732. if( trace.fraction == 1 )
  733. break;
  734. if( trace.fraction == 0 || trace.allsolid || trace.startsolid )
  735. {
  736. m_pKeyframe->m_LinksTouchingSomething[i] = true;
  737. pNode->m_vPos = pNode->m_vPrevPos;
  738. break;
  739. }
  740. // Apply some friction.
  741. const float flSlowFactor = 0.3f;
  742. pNode->m_vPos -= (pNode->m_vPos - pNode->m_vPrevPos) * flSlowFactor;
  743. // Move it out along the face normal.
  744. float distBehind = trace.plane.normal.Dot( pNode->m_vPos ) - trace.plane.dist;
  745. pNode->m_vPos += trace.plane.normal * (-distBehind + 2.2);
  746. m_pKeyframe->m_LinksTouchingSomething[i] = true;
  747. }
  748. if( iIteration == nIterations )
  749. pNodes[i].m_vPos = pNodes[i].m_vPrevPos;
  750. }
  751. }
  752. // Lock the endpoints.
  753. QAngle angles;
  754. if( m_pKeyframe->m_fLockedPoints & ROPE_LOCK_START_POINT )
  755. {
  756. m_pKeyframe->GetEndPointAttachment( 0, pNodes[0].m_vPos, angles );
  757. if (( m_pKeyframe->m_fLockedPoints & ROPE_LOCK_START_DIRECTION ) && (nNodes > 3))
  758. {
  759. Vector forward;
  760. AngleVectors( angles, &forward );
  761. int parity = 1;
  762. int nFalloffNodes = MIN( 2, nNodes - 2 );
  763. LockNodeDirection( pNodes, parity, nFalloffNodes, g_flLockAmount, g_flLockFalloff, forward );
  764. }
  765. }
  766. if( m_pKeyframe->m_fLockedPoints & ROPE_LOCK_END_POINT )
  767. {
  768. m_pKeyframe->GetEndPointAttachment( 1, pNodes[nNodes-1].m_vPos, angles );
  769. if( m_pKeyframe->m_fLockedPoints & ROPE_LOCK_END_DIRECTION && (nNodes > 3))
  770. {
  771. Vector forward;
  772. AngleVectors( angles, &forward );
  773. int parity = -1;
  774. int nFalloffNodes = MIN( 2, nNodes - 2 );
  775. LockNodeDirection( &pNodes[nNodes-1], parity, nFalloffNodes, g_flLockAmount, g_flLockFalloff, forward );
  776. }
  777. }
  778. }
  779. // ------------------------------------------------------------------------------------ //
  780. // C_RopeKeyframe
  781. // ------------------------------------------------------------------------------------ //
  782. C_RopeKeyframe::C_RopeKeyframe()
  783. {
  784. m_bEndPointAttachmentPositionsDirty = true;
  785. m_bEndPointAttachmentAnglesDirty = true;
  786. m_PhysicsDelegate.m_pKeyframe = this;
  787. m_pMaterial = NULL;
  788. m_bPhysicsInitted = false;
  789. m_RopeFlags = 0;
  790. m_TextureHeight = 1;
  791. m_hStartPoint = m_hEndPoint = INVALID_EHANDLE;
  792. m_iStartAttachment = m_iEndAttachment = 0;
  793. m_vColorMod.Init( 1, 1, 1 );
  794. m_nLinksTouchingSomething = 0;
  795. m_Subdiv = 255; // default to using the cvar
  796. m_flCurrentGustLifetime = 0.0f;
  797. m_flCurrentGustTimer = 0.0f;
  798. m_fLockedPoints = 0;
  799. m_fPrevLockedPoints = 0;
  800. m_iForcePointMoveCounter = 0;
  801. m_flCurScroll = m_flScrollSpeed = 0;
  802. m_TextureScale = 4; // 4:1
  803. m_vecImpulse.Init();
  804. m_ropeType = ROPE_TYPE_DEFAULT;
  805. g_Ropes.AddToTail( this );
  806. }
  807. C_RopeKeyframe::~C_RopeKeyframe()
  808. {
  809. s_RopeManager.RemoveRopeFromQueuedRenderCaches( this );
  810. g_Ropes.FindAndRemove( this );
  811. }
  812. C_RopeKeyframe* C_RopeKeyframe::Create(
  813. C_BaseEntity *pStartEnt,
  814. C_BaseEntity *pEndEnt,
  815. int iStartAttachment,
  816. int iEndAttachment,
  817. float ropeWidth,
  818. const char *pMaterialName,
  819. int numSegments,
  820. int ropeFlags,
  821. rope_type ropeType
  822. )
  823. {
  824. C_RopeKeyframe *pRope = new C_RopeKeyframe;
  825. pRope->InitializeAsClientEntity( NULL, false );
  826. if ( pStartEnt )
  827. {
  828. pRope->m_hStartPoint = pStartEnt;
  829. pRope->m_fLockedPoints |= ROPE_LOCK_START_POINT;
  830. }
  831. if ( pEndEnt )
  832. {
  833. pRope->m_hEndPoint = pEndEnt;
  834. pRope->m_fLockedPoints |= ROPE_LOCK_END_POINT;
  835. }
  836. pRope->m_iStartAttachment = iStartAttachment;
  837. pRope->m_iEndAttachment = iEndAttachment;
  838. pRope->m_Width = ropeWidth;
  839. pRope->m_nSegments = clamp( numSegments, 2, ROPE_MAX_SEGMENTS );
  840. pRope->m_RopeFlags = ropeFlags;
  841. pRope->m_ropeType = ropeType;
  842. pRope->FinishInit( pMaterialName );
  843. return pRope;
  844. }
  845. C_RopeKeyframe* C_RopeKeyframe::CreateFromKeyValues( C_BaseAnimating *pEnt, KeyValues *pValues )
  846. {
  847. C_RopeKeyframe *pRope = C_RopeKeyframe::Create(
  848. pEnt,
  849. pEnt,
  850. pEnt->LookupAttachment( pValues->GetString( "StartAttachment" ) ),
  851. pEnt->LookupAttachment( pValues->GetString( "EndAttachment" ) ),
  852. pValues->GetFloat( "Width", 0.5 ),
  853. pValues->GetString( "Material" ),
  854. pValues->GetInt( "NumSegments" ),
  855. 0 );
  856. if ( pRope )
  857. {
  858. if ( pValues->GetInt( "Gravity", 1 ) == 0 )
  859. {
  860. pRope->m_RopeFlags |= ROPE_NO_GRAVITY;
  861. }
  862. pRope->m_RopeLength = pValues->GetInt( "Length" );
  863. pRope->m_TextureScale = pValues->GetFloat( "TextureScale", pRope->m_TextureScale );
  864. pRope->m_Slack = 0;
  865. pRope->m_RopeFlags |= ROPE_SIMULATE;
  866. }
  867. return pRope;
  868. }
  869. int C_RopeKeyframe::GetRopesIntersectingAABB( C_RopeKeyframe **pRopes, int nMaxRopes, const Vector &vAbsMin, const Vector &vAbsMax )
  870. {
  871. if ( nMaxRopes == 0 )
  872. return 0;
  873. int nRopes = 0;
  874. FOR_EACH_LL( g_Ropes, i )
  875. {
  876. C_RopeKeyframe *pRope = g_Ropes[i];
  877. Vector v1, v2;
  878. if ( pRope->GetEndPointPos( 0, v1 ) && pRope->GetEndPointPos( 1, v2 ) )
  879. {
  880. if ( IsBoxIntersectingRay( v1, v2-v1, vAbsMin, vAbsMax, 0.1f ) )
  881. {
  882. pRopes[nRopes++] = pRope;
  883. if ( nRopes == nMaxRopes )
  884. break;
  885. }
  886. }
  887. }
  888. return nRopes;
  889. }
  890. void C_RopeKeyframe::SetSlack( int slack )
  891. {
  892. m_Slack = slack;
  893. RecomputeSprings();
  894. }
  895. void C_RopeKeyframe::SetRopeFlags( int flags )
  896. {
  897. m_RopeFlags = flags;
  898. UpdateVisibility();
  899. }
  900. void C_RopeKeyframe::AddRopeFlags( int flags )
  901. {
  902. m_RopeFlags |= flags;
  903. UpdateVisibility();
  904. }
  905. int C_RopeKeyframe::GetRopeFlags() const
  906. {
  907. return m_RopeFlags;
  908. }
  909. void C_RopeKeyframe::SetupHangDistance( float flHangDist )
  910. {
  911. C_BaseEntity *pEnt1 = m_hStartPoint;
  912. C_BaseEntity *pEnt2 = m_hEndPoint;
  913. if ( !pEnt1 || !pEnt2 )
  914. return;
  915. QAngle dummyAngles;
  916. // Calculate starting conditions so we can force it to hang down N inches.
  917. Vector v1 = pEnt1->GetAbsOrigin();
  918. pEnt1->GetAttachment( m_iStartAttachment, v1, dummyAngles );
  919. Vector v2 = pEnt2->GetAbsOrigin();
  920. pEnt2->GetAttachment( m_iEndAttachment, v2, dummyAngles );
  921. float flSlack, flLen;
  922. CalcRopeStartingConditions( v1, v2, ROPE_MAX_SEGMENTS, flHangDist, &flLen, &flSlack );
  923. m_RopeLength = (int)flLen;
  924. m_Slack = (int)flSlack;
  925. RecomputeSprings();
  926. }
  927. void C_RopeKeyframe::SetStartEntity( C_BaseEntity *pEnt )
  928. {
  929. m_hStartPoint = pEnt;
  930. }
  931. void C_RopeKeyframe::SetEndEntity( C_BaseEntity *pEnt )
  932. {
  933. m_hEndPoint = pEnt;
  934. }
  935. C_BaseEntity* C_RopeKeyframe::GetStartEntity() const
  936. {
  937. return m_hStartPoint;
  938. }
  939. C_BaseEntity* C_RopeKeyframe::GetEndEntity() const
  940. {
  941. return m_hEndPoint;
  942. }
  943. CSimplePhysics::IHelper* C_RopeKeyframe::HookPhysics( CSimplePhysics::IHelper *pHook )
  944. {
  945. m_RopePhysics.SetDelegate( pHook );
  946. return &m_PhysicsDelegate;
  947. }
  948. void C_RopeKeyframe::SetColorMod( const Vector &vColorMod )
  949. {
  950. m_vColorMod = vColorMod;
  951. }
  952. void C_RopeKeyframe::RecomputeSprings()
  953. {
  954. m_RopePhysics.ResetSpringLength(
  955. (m_RopeLength + m_Slack + ROPESLACK_FUDGEFACTOR) / (m_RopePhysics.NumNodes() - 1) );
  956. }
  957. void C_RopeKeyframe::ShakeRope( const Vector &vCenter, float flRadius, float flMagnitude )
  958. {
  959. // Sum up whatever it would apply to all of our points.
  960. bool bWantsThink = false;
  961. for ( int i=0; i < m_nSegments; i++ )
  962. {
  963. CSimplePhysics::CNode *pNode = m_RopePhysics.GetNode( i );
  964. float flDist = (pNode->m_vPos - vCenter).Length();
  965. float flShakeAmount = 1.0f - flDist / flRadius;
  966. if ( flShakeAmount >= 0 )
  967. {
  968. m_vecImpulse.z += flShakeAmount * flMagnitude;
  969. bWantsThink = true;
  970. }
  971. }
  972. if ( bWantsThink )
  973. {
  974. SetNextClientThink( CLIENT_THINK_ALWAYS );
  975. }
  976. }
  977. void C_RopeKeyframe::OnDataChanged( DataUpdateType_t updateType )
  978. {
  979. BaseClass::OnDataChanged( updateType );
  980. m_bNewDataThisFrame = true;
  981. SetNextClientThink( CLIENT_THINK_ALWAYS );
  982. if( updateType != DATA_UPDATE_CREATED )
  983. return;
  984. // Figure out the material name.
  985. char str[512];
  986. const model_t *pModel = modelinfo->GetModel( m_iRopeMaterialModelIndex );
  987. if ( pModel )
  988. {
  989. Q_strncpy( str, modelinfo->GetModelName( pModel ), sizeof( str ) );
  990. // Get rid of the extension because the material system doesn't want it.
  991. char *pExt = Q_stristr( str, ".vmt" );
  992. if ( pExt )
  993. pExt[0] = 0;
  994. }
  995. else
  996. {
  997. Q_strncpy( str, "missing_rope_material", sizeof( str ) );
  998. }
  999. FinishInit( str );
  1000. }
  1001. void C_RopeKeyframe::FinishInit( const char *pMaterialName )
  1002. {
  1003. // Get the material from the material system.
  1004. m_pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER );
  1005. if ( !g_pSplineCableShadowdepth )
  1006. {
  1007. g_pSplineCableShadowdepth = g_pMaterialSystem->FindMaterial( "cable/rope_shadowdepth", TEXTURE_GROUP_OTHER );
  1008. g_pSplineCableShadowdepth->IncrementReferenceCount();
  1009. }
  1010. if( m_pMaterial )
  1011. m_TextureHeight = m_pMaterial->GetMappingHeight();
  1012. else
  1013. m_TextureHeight = 1;
  1014. // Init rope physics.
  1015. m_nSegments = clamp( m_nSegments, 2, ROPE_MAX_SEGMENTS );
  1016. m_RopePhysics.SetNumNodes( m_nSegments );
  1017. SetCollisionBounds( Vector( -10, -10, -10 ), Vector( 10, 10, 10 ) );
  1018. // We want to think every frame.
  1019. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1020. }
  1021. void C_RopeKeyframe::RunRopeSimulation( float flSeconds )
  1022. {
  1023. // First, forget about links touching things.
  1024. for ( int i=0; i < m_nSegments; i++ )
  1025. m_LinksTouchingSomething[i] = false;
  1026. // Simulate, and it will mark which links touched things.
  1027. m_RopePhysics.Simulate( flSeconds );
  1028. // Now count how many links touched something.
  1029. m_nLinksTouchingSomething = 0;
  1030. for ( int i=0; i < m_nSegments; i++ )
  1031. {
  1032. if ( m_LinksTouchingSomething[i] )
  1033. ++m_nLinksTouchingSomething;
  1034. }
  1035. }
  1036. Vector C_RopeKeyframe::ConstrainNode( const Vector &vNormal, const Vector &vNodePosition, const Vector &vMidpiont, float fNormalLength )
  1037. {
  1038. // Get triangle edges formed
  1039. Vector vMidpointToNode = vNodePosition - vMidpiont;
  1040. Vector vMidpointToNodeProjected = vMidpointToNode.Dot( vNormal ) * vNormal;
  1041. float fMidpointToNodeLengh = VectorNormalize( vMidpointToNode );
  1042. float fMidpointToNodeProjectedLengh = VectorNormalize( vMidpointToNodeProjected );
  1043. // See if it's past an endpoint
  1044. if ( fMidpointToNodeProjectedLengh < fNormalLength + 1.0f )
  1045. return vNodePosition;
  1046. // Apply the ratio between the triangles
  1047. return vMidpiont + vMidpointToNode * fMidpointToNodeLengh * ( fNormalLength / fMidpointToNodeProjectedLengh );
  1048. }
  1049. void C_RopeKeyframe::ConstrainNodesBetweenEndpoints( void )
  1050. {
  1051. if ( !m_bConstrainBetweenEndpoints )
  1052. return;
  1053. // Get midpoint and normals
  1054. Vector vMidpiont = ( m_vCachedEndPointAttachmentPos[ 0 ] + m_vCachedEndPointAttachmentPos[ 1 ] ) / 2.0f;
  1055. Vector vNormal = vMidpiont - m_vCachedEndPointAttachmentPos[ 0 ];
  1056. float fNormalLength = VectorNormalize( vNormal );
  1057. // Loop through all the middle segments and ensure their positions are constrained between the endpoints
  1058. for ( int i = 1; i < m_RopePhysics.NumNodes() - 1; ++i )
  1059. {
  1060. // Fix the current position
  1061. m_RopePhysics.GetNode( i )->m_vPos = ConstrainNode( vNormal, m_RopePhysics.GetNode( i )->m_vPos, vMidpiont, fNormalLength );
  1062. // Fix the predicted position
  1063. m_RopePhysics.GetNode( i )->m_vPredicted = ConstrainNode( vNormal, m_RopePhysics.GetNode( i )->m_vPredicted, vMidpiont, fNormalLength );
  1064. }
  1065. }
  1066. void C_RopeKeyframe::ClientThink()
  1067. {
  1068. // Only recalculate the endpoint attachments once per frame.
  1069. m_bEndPointAttachmentPositionsDirty = true;
  1070. m_bEndPointAttachmentAnglesDirty = true;
  1071. // update the holiday lights here even if they aren't simulated
  1072. if ( m_ropeType == ROPE_TYPE_DEFAULT )
  1073. UpdateHolidayLights();
  1074. if( !InitRopePhysics() ) // init if not already
  1075. return;
  1076. if( !r_drawropes.GetBool() )
  1077. return;
  1078. if ( DetectRestingState( m_bApplyWind ) )
  1079. {
  1080. if ( ( m_RopeFlags & ROPE_USE_WIND ) == 0 )
  1081. {
  1082. SetNextClientThink( CLIENT_THINK_NEVER );
  1083. }
  1084. return;
  1085. }
  1086. // Update the simulation.
  1087. RunRopeSimulation( gpGlobals->frametime );
  1088. g_nRopePointsSimulated += m_RopePhysics.NumNodes();
  1089. m_bNewDataThisFrame = false;
  1090. // Setup a new wind gust?
  1091. if ( m_bApplyWind )
  1092. {
  1093. m_flCurrentGustTimer += gpGlobals->frametime;
  1094. m_flTimeToNextGust -= gpGlobals->frametime;
  1095. if( m_flTimeToNextGust <= 0 )
  1096. {
  1097. m_vWindDir = RandomVector( -1, 1 );
  1098. VectorNormalize( m_vWindDir );
  1099. static float basicScale = 50;
  1100. m_vWindDir *= basicScale;
  1101. m_vWindDir *= RandomFloat( -1.0f, 1.0f );
  1102. m_flCurrentGustTimer = 0;
  1103. m_flCurrentGustLifetime = RandomFloat( 2.0f, 3.0f );
  1104. m_flTimeToNextGust = RandomFloat( 3.0f, 4.0f );
  1105. }
  1106. }
  1107. UpdateBBox();
  1108. }
  1109. int C_RopeKeyframe::DrawModel( int flags, const RenderableInstance_t &instance )
  1110. {
  1111. VPROF_BUDGET( "C_RopeKeyframe::DrawModel", VPROF_BUDGETGROUP_ROPES );
  1112. if( !InitRopePhysics() )
  1113. return 0;
  1114. if ( !m_bReadyToDraw )
  1115. return 0;
  1116. // Resize the rope
  1117. if( m_RopeFlags & ROPE_RESIZE )
  1118. {
  1119. RecomputeSprings();
  1120. }
  1121. // If our start & end entities have models, but are nodraw, then we don't draw
  1122. if ( m_hStartPoint && m_hStartPoint->IsDormant() && m_hEndPoint && m_hEndPoint->IsDormant() )
  1123. {
  1124. // Check models because rope endpoints are point entities
  1125. if ( m_hStartPoint->GetModelIndex() && m_hEndPoint->GetModelIndex() )
  1126. return 0;
  1127. }
  1128. ConstrainNodesBetweenEndpoints();
  1129. RopeManager()->AddToRenderCache( this );
  1130. return 1;
  1131. }
  1132. bool C_RopeKeyframe::ShouldDraw()
  1133. {
  1134. if( !r_ropetranslucent.GetBool() )
  1135. return false;
  1136. if( !(m_RopeFlags & ROPE_SIMULATE) )
  1137. return false;
  1138. if ( !IsGameConsole() )
  1139. {
  1140. CPULevel_t nCPULevel = GetCPULevel();
  1141. bool bNoDraw = ( GetMinCPULevel() && GetMinCPULevel()-1 > nCPULevel );
  1142. bNoDraw = bNoDraw || ( GetMaxCPULevel() && GetMaxCPULevel()-1 < nCPULevel );
  1143. if ( bNoDraw )
  1144. return false;
  1145. GPULevel_t nGPULevel = GetGPULevel();
  1146. bNoDraw = ( GetMinGPULevel() && GetMinGPULevel()-1 > nGPULevel );
  1147. bNoDraw = bNoDraw || ( GetMaxGPULevel() && GetMaxGPULevel()-1 < nGPULevel );
  1148. if ( bNoDraw )
  1149. return false;
  1150. }
  1151. return true;
  1152. }
  1153. const Vector& C_RopeKeyframe::WorldSpaceCenter( ) const
  1154. {
  1155. return GetAbsOrigin();
  1156. }
  1157. bool C_RopeKeyframe::GetAttachment( int number, matrix3x4_t &matrix )
  1158. {
  1159. int nNodes = m_RopePhysics.NumNodes();
  1160. if ( (number != ROPE_ATTACHMENT_START_POINT && number != ROPE_ATTACHMENT_END_POINT) || nNodes < 2 )
  1161. return false;
  1162. // Now setup the orientation based on the last segment.
  1163. Vector vForward, origin;
  1164. if ( number == ROPE_ATTACHMENT_START_POINT )
  1165. {
  1166. origin = m_RopePhysics.GetNode( 0 )->m_vPredicted;
  1167. vForward = m_RopePhysics.GetNode( 0 )->m_vPredicted - m_RopePhysics.GetNode( 1 )->m_vPredicted;
  1168. }
  1169. else
  1170. {
  1171. origin = m_RopePhysics.GetNode( nNodes-1 )->m_vPredicted;
  1172. vForward = m_RopePhysics.GetNode( nNodes-1 )->m_vPredicted - m_RopePhysics.GetNode( nNodes-2 )->m_vPredicted;
  1173. }
  1174. VectorMatrix( vForward, matrix );
  1175. PositionMatrix( origin, matrix );
  1176. return true;
  1177. }
  1178. bool C_RopeKeyframe::GetAttachment( int number, Vector &origin )
  1179. {
  1180. int nNodes = m_RopePhysics.NumNodes();
  1181. if ( (number != ROPE_ATTACHMENT_START_POINT && number != ROPE_ATTACHMENT_END_POINT) || nNodes < 2 )
  1182. return false;
  1183. // Now setup the orientation based on the last segment.
  1184. if ( number == ROPE_ATTACHMENT_START_POINT )
  1185. {
  1186. origin = m_RopePhysics.GetNode( 0 )->m_vPredicted;
  1187. }
  1188. else
  1189. {
  1190. origin = m_RopePhysics.GetNode( nNodes-1 )->m_vPredicted;
  1191. }
  1192. return true;
  1193. }
  1194. bool C_RopeKeyframe::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel )
  1195. {
  1196. Assert(0);
  1197. return false;
  1198. }
  1199. bool C_RopeKeyframe::GetAttachment( int number, Vector &origin, QAngle &angles )
  1200. {
  1201. int nNodes = m_RopePhysics.NumNodes();
  1202. if ( (number == ROPE_ATTACHMENT_START_POINT || number == ROPE_ATTACHMENT_END_POINT) && nNodes >= 2 )
  1203. {
  1204. // Now setup the orientation based on the last segment.
  1205. Vector vForward;
  1206. if ( number == ROPE_ATTACHMENT_START_POINT )
  1207. {
  1208. origin = m_RopePhysics.GetNode( 0 )->m_vPredicted;
  1209. vForward = m_RopePhysics.GetNode( 0 )->m_vPredicted - m_RopePhysics.GetNode( 1 )->m_vPredicted;
  1210. }
  1211. else
  1212. {
  1213. origin = m_RopePhysics.GetNode( nNodes-1 )->m_vPredicted;
  1214. vForward = m_RopePhysics.GetNode( nNodes-1 )->m_vPredicted - m_RopePhysics.GetNode( nNodes-2 )->m_vPredicted;
  1215. }
  1216. VectorAngles( vForward, angles );
  1217. return true;
  1218. }
  1219. return false;
  1220. }
  1221. bool C_RopeKeyframe::AnyPointsMoved()
  1222. {
  1223. int nNodeCount = m_RopePhysics.NumNodes();
  1224. for( int i=0; i < nNodeCount; i++ )
  1225. {
  1226. CSimplePhysics::CNode *pNode = m_RopePhysics.GetNode( i );
  1227. float flMoveDistSqr = pNode->m_vPos.DistToSqr( pNode->m_vPrevPos );
  1228. if( flMoveDistSqr > 0.25f )
  1229. {
  1230. if ( m_iForcePointMoveCounter < 5 )
  1231. {
  1232. m_iForcePointMoveCounter = 5;
  1233. }
  1234. return true;
  1235. }
  1236. }
  1237. if( m_iForcePointMoveCounter >= 0 )
  1238. {
  1239. --m_iForcePointMoveCounter;
  1240. return true;
  1241. }
  1242. return false;
  1243. }
  1244. inline bool C_RopeKeyframe::DidEndPointMove( int iPt )
  1245. {
  1246. // If this point isn't locked anyway, just break out.
  1247. if( !( m_fLockedPoints & (1 << iPt) ) )
  1248. return false;
  1249. bool bOld = m_bPrevEndPointPos[iPt];
  1250. Vector vOld = m_vPrevEndPointPos[iPt];
  1251. m_bPrevEndPointPos[iPt] = GetEndPointPos( iPt, m_vPrevEndPointPos[iPt] );
  1252. // If it wasn't and isn't attached to anything, don't register a change.
  1253. if( !bOld && !m_bPrevEndPointPos[iPt] )
  1254. return true;
  1255. // Register a change if the endpoint moves.
  1256. if( !VectorsAreEqual( vOld, m_vPrevEndPointPos[iPt], 0.1 ) )
  1257. return true;
  1258. return false;
  1259. }
  1260. bool C_RopeKeyframe::DetectRestingState( bool &bApplyWind )
  1261. {
  1262. bApplyWind = false;
  1263. if( m_fPrevLockedPoints != m_fLockedPoints )
  1264. {
  1265. // Force it to move the points for some number of frames when they get detached or
  1266. // after we get new data. This allows them to accelerate from gravity.
  1267. m_iForcePointMoveCounter = 10;
  1268. m_fPrevLockedPoints = m_fLockedPoints;
  1269. return false;
  1270. }
  1271. if( m_bNewDataThisFrame )
  1272. {
  1273. // Simulate if anything about us changed this frame, such as our position due to hierarchy.
  1274. // FIXME: this won't work when hierarchy is client side
  1275. return false;
  1276. }
  1277. // Make sure our attachment points haven't moved.
  1278. if( DidEndPointMove( 0 ) || DidEndPointMove( 1 ) )
  1279. return false;
  1280. // See how close we are to the line.
  1281. Vector &vEnd1 = m_RopePhysics.GetFirstNode()->m_vPos;
  1282. Vector &vEnd2 = m_RopePhysics.GetLastNode()->m_vPos;
  1283. if ( m_RopeFlags & ROPE_USE_WIND )
  1284. {
  1285. // Don't apply wind if more than half of the nodes are touching something.
  1286. float flDist1 = FLT_MAX;
  1287. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  1288. {
  1289. // ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  1290. float d = CalcDistanceToLineSegment( MainViewOrigin( hh ), vEnd1, vEnd2 );
  1291. if ( d < flDist1 )
  1292. {
  1293. flDist1 = d;
  1294. }
  1295. }
  1296. if( m_nLinksTouchingSomething < (m_RopePhysics.NumNodes() >> 1) )
  1297. {
  1298. bApplyWind = flDist1 < rope_wind_dist.GetFloat();
  1299. }
  1300. }
  1301. if ( m_vecPreviousImpulse != m_vecImpulse )
  1302. {
  1303. m_vecPreviousImpulse = m_vecImpulse;
  1304. return false;
  1305. }
  1306. return !AnyPointsMoved() && !bApplyWind && !rope_shake.GetInt();
  1307. }
  1308. // simple struct to precompute basis for catmull rom splines for faster evaluation
  1309. struct catmull_t
  1310. {
  1311. Vector t3;
  1312. Vector t2;
  1313. Vector t;
  1314. Vector c;
  1315. };
  1316. // bake out the terms of the catmull rom spline
  1317. void Catmull_Rom_Spline_Matrix( const Vector &p1, const Vector &p2, const Vector &p3, const Vector &p4, catmull_t &output )
  1318. {
  1319. output.t3 = 0.5f * ((-1*p1) + (3*p2) + (-3*p3) + p4); // 0.5 t^3 * [ (-1*p1) + ( 3*p2) + (-3*p3) + p4 ]
  1320. output.t2 = 0.5f * ((2*p1) + (-5*p2) + (4*p3) - p4); // 0.5 t^2 * [ ( 2*p1) + (-5*p2) + ( 4*p3) - p4 ]
  1321. output.t = 0.5f * ((-1*p1) + p3); // 0.5 t * [ (-1*p1) + p3 ]
  1322. output.c = p2; // p2
  1323. }
  1324. // evaluate one point on the spline, t is a vector of (t, t^2, t^3)
  1325. inline void Catmull_Rom_Eval( const catmull_t &spline, const Vector &t, Vector &output )
  1326. {
  1327. Assert(spline.c.IsValid());
  1328. Assert(spline.t.IsValid());
  1329. Assert(spline.t2.IsValid());
  1330. Assert(spline.t3.IsValid());
  1331. output = spline.c + (t.x * spline.t) + (t.y*spline.t2) + (t.z * spline.t3);
  1332. }
  1333. void C_RopeKeyframe::UpdateBBox()
  1334. {
  1335. Vector &vStart = m_RopePhysics.GetFirstNode()->m_vPos;
  1336. Vector &vEnd = m_RopePhysics.GetLastNode()->m_vPos;
  1337. Vector mins, maxs;
  1338. VectorMin( vStart, vEnd, mins );
  1339. VectorMax( vStart, vEnd, maxs );
  1340. for( int i=1; i < m_RopePhysics.NumNodes()-1; i++ )
  1341. {
  1342. const Vector &vPos = m_RopePhysics.GetNode(i)->m_vPos;
  1343. AddPointToBounds( vPos, mins, maxs );
  1344. }
  1345. mins -= GetAbsOrigin();
  1346. maxs -= GetAbsOrigin();
  1347. SetCollisionBounds( mins, maxs );
  1348. }
  1349. bool C_RopeKeyframe::InitRopePhysics()
  1350. {
  1351. if( !(m_RopeFlags & ROPE_SIMULATE) )
  1352. return 0;
  1353. if( m_bPhysicsInitted )
  1354. {
  1355. return true;
  1356. }
  1357. // Must have both entities to work.
  1358. m_bPrevEndPointPos[0] = GetEndPointPos( 0, m_vPrevEndPointPos[0] );
  1359. if( !m_bPrevEndPointPos[0] )
  1360. return false;
  1361. // They're allowed to not have an end attachment point so the rope can dangle.
  1362. m_bPrevEndPointPos[1] = GetEndPointPos( 1, m_vPrevEndPointPos[1] );
  1363. if( !m_bPrevEndPointPos[1] )
  1364. m_vPrevEndPointPos[1] = m_vPrevEndPointPos[0];
  1365. const Vector &vStart = m_vPrevEndPointPos[0];
  1366. const Vector &vAttached = m_vPrevEndPointPos[1];
  1367. m_RopePhysics.SetupSimulation( 0, &m_PhysicsDelegate );
  1368. RecomputeSprings();
  1369. m_RopePhysics.Restart();
  1370. // Initialize the positions of the nodes.
  1371. for( int i=0; i < m_RopePhysics.NumNodes(); i++ )
  1372. {
  1373. CSimplePhysics::CNode *pNode = m_RopePhysics.GetNode( i );
  1374. float t = (float)i / (m_RopePhysics.NumNodes() - 1);
  1375. VectorLerp( vStart, vAttached, t, pNode->m_vPos );
  1376. pNode->m_vPrevPos = pNode->m_vPos;
  1377. }
  1378. // Simulate for a bit to let it sag.
  1379. if ( m_RopeFlags & ROPE_INITIAL_HANG )
  1380. {
  1381. RunRopeSimulation( 5 );
  1382. }
  1383. CalcLightValues();
  1384. // Set our bounds for visibility.
  1385. UpdateBBox();
  1386. m_flTimeToNextGust = RandomFloat( 1.0f, 3.0f );
  1387. m_bPhysicsInitted = true;
  1388. return true;
  1389. }
  1390. bool C_RopeKeyframe::CalculateEndPointAttachment( C_BaseEntity *pEnt, int iAttachment, Vector &vPos, QAngle *pAngles )
  1391. {
  1392. VPROF_BUDGET( "C_RopeKeyframe::CalculateEndPointAttachment", VPROF_BUDGETGROUP_ROPES );
  1393. if( !pEnt )
  1394. return false;
  1395. if ( m_RopeFlags & ROPE_PLAYER_WPN_ATTACH )
  1396. {
  1397. C_BasePlayer *pPlayer = ToBasePlayer( pEnt );
  1398. if ( pPlayer )
  1399. {
  1400. C_BaseAnimating *pModel = pPlayer->GetRenderedWeaponModel();
  1401. if ( !pModel )
  1402. return false;
  1403. int iAttachment = pModel->LookupAttachment( "buff_attach" );
  1404. if ( pAngles )
  1405. return pModel->GetAttachment( iAttachment, vPos, *pAngles );
  1406. return pModel->GetAttachment( iAttachment, vPos );
  1407. }
  1408. }
  1409. if( iAttachment > 0 )
  1410. {
  1411. bool bOk;
  1412. if ( pAngles )
  1413. {
  1414. bOk = pEnt->GetAttachment( iAttachment, vPos, *pAngles );
  1415. }
  1416. else
  1417. {
  1418. bOk = pEnt->GetAttachment( iAttachment, vPos );
  1419. }
  1420. if ( bOk )
  1421. return true;
  1422. }
  1423. vPos = pEnt->WorldSpaceCenter( );
  1424. if ( pAngles )
  1425. {
  1426. *pAngles = pEnt->GetAbsAngles();
  1427. }
  1428. return true;
  1429. }
  1430. bool C_RopeKeyframe::GetEndPointPos( int iPt, Vector &vPos )
  1431. {
  1432. // By caching the results here, we avoid doing this a bunch of times per frame.
  1433. if ( m_bEndPointAttachmentPositionsDirty )
  1434. {
  1435. CalculateEndPointAttachment( m_hStartPoint, m_iStartAttachment, m_vCachedEndPointAttachmentPos[0], NULL );
  1436. CalculateEndPointAttachment( m_hEndPoint, m_iEndAttachment, m_vCachedEndPointAttachmentPos[1], NULL );
  1437. m_bEndPointAttachmentPositionsDirty = false;
  1438. }
  1439. Assert( iPt == 0 || iPt == 1 );
  1440. vPos = m_vCachedEndPointAttachmentPos[iPt];
  1441. return true;
  1442. }
  1443. bool C_RopeKeyframe::GetEndPointAttachment( int iPt, Vector &vPos, QAngle &angle )
  1444. {
  1445. // By caching the results here, we avoid doing this a bunch of times per frame.
  1446. if ( m_bEndPointAttachmentPositionsDirty || m_bEndPointAttachmentAnglesDirty )
  1447. {
  1448. CalculateEndPointAttachment( m_hStartPoint, m_iStartAttachment, m_vCachedEndPointAttachmentPos[0], &m_vCachedEndPointAttachmentAngle[0] );
  1449. CalculateEndPointAttachment( m_hEndPoint, m_iEndAttachment, m_vCachedEndPointAttachmentPos[1], &m_vCachedEndPointAttachmentAngle[1] );
  1450. m_bEndPointAttachmentPositionsDirty = false;
  1451. m_bEndPointAttachmentAnglesDirty = false;
  1452. }
  1453. Assert( iPt == 0 || iPt == 1 );
  1454. vPos = m_vCachedEndPointAttachmentPos[iPt];
  1455. angle = m_vCachedEndPointAttachmentAngle[iPt];
  1456. return true;
  1457. }
  1458. void C_RopeKeyframe::UpdateHolidayLights( void )
  1459. {
  1460. if ( !RopeManager()->IsHolidayLightMode() )
  1461. return;
  1462. if ( ( gpGlobals->curtime != g_RopeDelayedEffects.m_flTimeProcessedOnMainThread ) && g_RopeDelayedEffects.m_arrEffects.Count() )
  1463. {
  1464. g_RopeDelayedEffects.m_flTimeProcessedOnMainThread = gpGlobals->curtime;
  1465. AUTO_LOCK( g_RopeDelayedEffects.m_mtx );
  1466. FOR_EACH_VEC( g_RopeDelayedEffects.m_arrEffects, iEffect )
  1467. {
  1468. DispatchEffect( "CS_HolidayLight", g_RopeDelayedEffects.m_arrEffects[iEffect] );
  1469. }
  1470. g_RopeDelayedEffects.m_arrEffects.RemoveAll();
  1471. }
  1472. }
  1473. void C_RopeKeyframe::CalcLightValues()
  1474. {
  1475. Vector boxColors[6];
  1476. for( int i=0; i < m_RopePhysics.NumNodes(); i++ )
  1477. {
  1478. const Vector &vPos = m_RopePhysics.GetNode(i)->m_vPredicted;
  1479. engine->ComputeLighting( vPos, NULL, true, m_LightValues[i], boxColors );
  1480. if ( !rope_averagelight.GetInt() )
  1481. {
  1482. // The engine averages the lighting across the 6 box faces, but we would rather just get the MAX intensity
  1483. // since we do our own half-lambert lighting in the rope shader to simulate directionality.
  1484. //
  1485. // So here, we take the average of all the incoming light, and scale it to use the max intensity of all the box sides.
  1486. float flMaxIntensity = 0;
  1487. for ( int iSide=0; iSide < 6; iSide++ )
  1488. {
  1489. float flLen = boxColors[iSide].Length();
  1490. flMaxIntensity = MAX( flMaxIntensity, flLen );
  1491. }
  1492. VectorNormalize( m_LightValues[i] );
  1493. m_LightValues[i] *= flMaxIntensity;
  1494. float flMax = MAX( m_LightValues[i].x, MAX( m_LightValues[i].y, m_LightValues[i].z ) );
  1495. if ( flMax > 1 )
  1496. m_LightValues[i] /= flMax;
  1497. }
  1498. }
  1499. }
  1500. //------------------------------------------------------------------------------
  1501. // Purpose :
  1502. // Input :
  1503. // Output :
  1504. //------------------------------------------------------------------------------
  1505. void C_RopeKeyframe::ReceiveMessage( int classID, bf_read &msg )
  1506. {
  1507. if ( classID != GetClientClass()->m_ClassID )
  1508. {
  1509. // message is for subclass
  1510. BaseClass::ReceiveMessage( classID, msg );
  1511. return;
  1512. }
  1513. // Read instantaneous fore data
  1514. m_vecImpulse.x = msg.ReadFloat();
  1515. m_vecImpulse.y = msg.ReadFloat();
  1516. m_vecImpulse.z = msg.ReadFloat();
  1517. }