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.

644 lines
18 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "c_smoke_trail.h"
  9. #include "smoke_fog_overlay.h"
  10. #include "engine/IEngineTrace.h"
  11. #include "view.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. #define SF_EMISSIVE 0x00000001
  15. // ------------------------------------------------------------------------- //
  16. // Definitions
  17. // ------------------------------------------------------------------------- //
  18. static Vector s_FadePlaneDirections[] =
  19. {
  20. Vector( 1,0,0),
  21. Vector(-1,0,0),
  22. Vector(0, 1,0),
  23. Vector(0,-1,0),
  24. Vector(0,0, 1),
  25. Vector(0,0,-1)
  26. };
  27. #define NUM_FADE_PLANES (sizeof(s_FadePlaneDirections)/sizeof(s_FadePlaneDirections[0]))
  28. // ------------------------------------------------------------------------- //
  29. // Classes
  30. // ------------------------------------------------------------------------- //
  31. class C_FuncSmokeVolume : public C_BaseParticleEntity, public IPrototypeAppEffect
  32. {
  33. public:
  34. DECLARE_CLASS( C_FuncSmokeVolume, C_BaseParticleEntity );
  35. DECLARE_CLIENTCLASS();
  36. C_FuncSmokeVolume();
  37. ~C_FuncSmokeVolume();
  38. int IsEmissive( void ) { return ( m_spawnflags & SF_EMISSIVE ); }
  39. private:
  40. class SmokeGrenadeParticle : public Particle
  41. {
  42. public:
  43. float m_RotationFactor;
  44. float m_CurRotation;
  45. float m_FadeAlpha; // Set as it moves around.
  46. unsigned char m_ColorInterp; // Amount between min and max colors.
  47. unsigned char m_Color[4];
  48. };
  49. // C_BaseEntity.
  50. public:
  51. virtual void OnDataChanged( DataUpdateType_t updateType );
  52. // IPrototypeAppEffect.
  53. public:
  54. virtual void Start( CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs );
  55. // IParticleEffect.
  56. public:
  57. virtual void Update(float fTimeDelta);
  58. virtual void RenderParticles( CParticleRenderIterator *pIterator );
  59. virtual void SimulateParticles( CParticleSimulateIterator *pIterator );
  60. virtual void NotifyRemove();
  61. private:
  62. // The SmokeEmitter represents a grid in 3D space.
  63. class SmokeParticleInfo
  64. {
  65. public:
  66. SmokeGrenadeParticle *m_pParticle;
  67. int m_TradeIndex; // -1 if not exchanging yet.
  68. float m_TradeClock; // How long since they started trading.
  69. float m_TradeDuration; // How long the trade will take to finish.
  70. float m_FadeAlpha; // Calculated from nearby world geometry.
  71. unsigned char m_Color[4];
  72. };
  73. inline int GetSmokeParticleIndex(int x, int y, int z)
  74. {
  75. Assert( IsValidXYZCoords( x, y, z ) );
  76. return z*m_xCount*m_yCount+y*m_xCount+x;
  77. }
  78. inline SmokeParticleInfo *GetSmokeParticleInfo(int x, int y, int z)
  79. {
  80. Assert( IsValidXYZCoords( x, y, z ) );
  81. return &m_pSmokeParticleInfos[GetSmokeParticleIndex(x,y,z)];
  82. }
  83. inline void GetParticleInfoXYZ(int index, int &x, int &y, int &z)
  84. {
  85. Assert( index >= 0 && index < m_xCount * m_yCount * m_zCount );
  86. z = index / (m_xCount*m_yCount);
  87. int zIndex = z*m_xCount*m_yCount;
  88. y = (index - zIndex) / m_xCount;
  89. int yIndex = y*m_xCount;
  90. x = index - zIndex - yIndex;
  91. Assert( IsValidXYZCoords( x, y, z ) );
  92. }
  93. inline bool IsValidXYZCoords(int x, int y, int z)
  94. {
  95. return x >= 0 && y >= 0 && z >= 0 && x < m_xCount && y < m_yCount && z < m_zCount;
  96. }
  97. inline Vector GetSmokeParticlePos(int x, int y, int z )
  98. {
  99. return WorldAlignMins() +
  100. Vector( x * m_SpacingRadius * 2 + m_SpacingRadius,
  101. y * m_SpacingRadius * 2 + m_SpacingRadius,
  102. z * m_SpacingRadius * 2 + m_SpacingRadius );
  103. }
  104. inline Vector GetSmokeParticlePosIndex(int index)
  105. {
  106. int x, y, z;
  107. GetParticleInfoXYZ(index, x, y, z);
  108. return GetSmokeParticlePos(x, y, z);
  109. }
  110. // Start filling the smoke volume
  111. void FillVolume();
  112. private:
  113. // State variables from server.
  114. color32 m_Color1;
  115. color32 m_Color2;
  116. char m_MaterialName[255];
  117. float m_ParticleDrawWidth;
  118. float m_ParticleSpacingDistance;
  119. float m_DensityRampSpeed;
  120. float m_RotationSpeed;
  121. float m_MovementSpeed;
  122. float m_Density;
  123. float m_maxDrawDistance;
  124. int m_spawnflags;
  125. private:
  126. C_FuncSmokeVolume( const C_FuncSmokeVolume & );
  127. float m_CurrentDensity;
  128. float m_ParticleRadius;
  129. bool m_bStarted;
  130. PMaterialHandle m_MaterialHandle;
  131. SmokeParticleInfo *m_pSmokeParticleInfos;
  132. int m_xCount, m_yCount, m_zCount;
  133. float m_SpacingRadius;
  134. Vector m_MinColor;
  135. Vector m_MaxColor;
  136. Vector m_vLastOrigin;
  137. QAngle m_vLastAngles;
  138. bool m_bFirstUpdate;
  139. };
  140. IMPLEMENT_CLIENTCLASS_DT( C_FuncSmokeVolume, DT_FuncSmokeVolume, CFuncSmokeVolume )
  141. RecvPropInt( RECVINFO( m_Color1 ), 0, RecvProxy_Int32ToColor32 ),
  142. RecvPropInt( RECVINFO( m_Color2 ), 0, RecvProxy_Int32ToColor32 ),
  143. RecvPropString( RECVINFO( m_MaterialName ) ),
  144. RecvPropFloat( RECVINFO( m_ParticleDrawWidth ) ),
  145. RecvPropFloat( RECVINFO( m_ParticleSpacingDistance ) ),
  146. RecvPropFloat( RECVINFO( m_DensityRampSpeed ) ),
  147. RecvPropFloat( RECVINFO( m_RotationSpeed ) ),
  148. RecvPropFloat( RECVINFO( m_MovementSpeed ) ),
  149. RecvPropFloat( RECVINFO( m_Density ) ),
  150. RecvPropFloat( RECVINFO( m_maxDrawDistance ) ),
  151. RecvPropInt( RECVINFO( m_spawnflags ) ),
  152. RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ),
  153. END_RECV_TABLE()
  154. // Helpers.
  155. // ------------------------------------------------------------------------- //
  156. static inline void InterpColor(unsigned char dest[4], unsigned char src1[4], unsigned char src2[4], float percent)
  157. {
  158. dest[0] = (unsigned char)(src1[0] + (src2[0] - src1[0]) * percent);
  159. dest[1] = (unsigned char)(src1[1] + (src2[1] - src1[1]) * percent);
  160. dest[2] = (unsigned char)(src1[2] + (src2[2] - src1[2]) * percent);
  161. }
  162. static inline int GetWorldPointContents(const Vector &vPos)
  163. {
  164. #if defined(PARTICLEPROTOTYPE_APP)
  165. return 0;
  166. #else
  167. return enginetrace->GetPointContents( vPos );
  168. #endif
  169. }
  170. static inline void WorldTraceLine( const Vector &start, const Vector &end, int contentsMask, trace_t *trace )
  171. {
  172. #if defined(PARTICLEPROTOTYPE_APP)
  173. trace->fraction = 1;
  174. #else
  175. UTIL_TraceLine(start, end, contentsMask, NULL, COLLISION_GROUP_NONE, trace);
  176. #endif
  177. }
  178. static inline Vector EngineGetLightForPoint(const Vector &vPos)
  179. {
  180. #if defined(PARTICLEPROTOTYPE_APP)
  181. return Vector(1,1,1);
  182. #else
  183. return engine->GetLightForPoint(vPos, true);
  184. #endif
  185. }
  186. static inline Vector& EngineGetVecRenderOrigin()
  187. {
  188. #if defined(PARTICLEPROTOTYPE_APP)
  189. static Vector dummy(0,0,0);
  190. return dummy;
  191. #else
  192. extern Vector g_vecRenderOrigin[ MAX_SPLITSCREEN_PLAYERS ];
  193. return g_vecRenderOrigin[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
  194. #endif
  195. }
  196. static inline float& EngineGetSmokeFogOverlayAlpha()
  197. {
  198. #if defined(PARTICLEPROTOTYPE_APP)
  199. static float dummy;
  200. return dummy;
  201. #else
  202. return g_SmokeFogOverlayAlpha;
  203. #endif
  204. }
  205. static inline C_BaseEntity* ParticleGetEntity( int index )
  206. {
  207. #if defined(PARTICLEPROTOTYPE_APP)
  208. return NULL;
  209. #else
  210. return cl_entitylist->GetEnt( index );
  211. #endif
  212. }
  213. // ------------------------------------------------------------------------- //
  214. // C_FuncSmokeVolume
  215. // ------------------------------------------------------------------------- //
  216. C_FuncSmokeVolume::C_FuncSmokeVolume()
  217. {
  218. m_bFirstUpdate = true;
  219. m_vLastOrigin.Init();
  220. m_vLastAngles.Init();
  221. m_pSmokeParticleInfos = NULL;
  222. m_SpacingRadius = 0.0f;;
  223. m_ParticleRadius = 0.0f;
  224. m_MinColor.Init( 1.0, 1.0, 1.0 );
  225. m_MaxColor.Init( 1.0, 1.0, 1.0 );
  226. }
  227. C_FuncSmokeVolume::~C_FuncSmokeVolume()
  228. {
  229. delete [] m_pSmokeParticleInfos;
  230. }
  231. void C_FuncSmokeVolume::OnDataChanged( DataUpdateType_t updateType )
  232. {
  233. m_MinColor[0] = ( 1.0f / 255.0f ) * m_Color1.r;
  234. m_MinColor[1] = ( 1.0f / 255.0f ) * m_Color1.g;
  235. m_MinColor[2] = ( 1.0f / 255.0f ) * m_Color1.b;
  236. m_MaxColor[0] = ( 1.0f / 255.0f ) * m_Color2.r;
  237. m_MaxColor[1] = ( 1.0f / 255.0f ) * m_Color2.g;
  238. m_MaxColor[2] = ( 1.0f / 255.0f ) * m_Color2.b;
  239. m_ParticleRadius = m_ParticleDrawWidth * 0.5f;
  240. m_SpacingRadius = m_ParticleSpacingDistance * 0.5f;
  241. m_ParticleEffect.SetParticleCullRadius( m_ParticleRadius );
  242. // Warning( "m_Density: %f\n", m_Density );
  243. // Warning( "m_MovementSpeed: %f\n", m_MovementSpeed );
  244. if(updateType == DATA_UPDATE_CREATED)
  245. {
  246. Vector size = WorldAlignMaxs() - WorldAlignMins();
  247. m_xCount = 0.5f + ( size.x / ( m_SpacingRadius * 2.0f ) );
  248. m_yCount = 0.5f + ( size.y / ( m_SpacingRadius * 2.0f ) );
  249. m_zCount = 0.5f + ( size.z / ( m_SpacingRadius * 2.0f ) );
  250. m_CurrentDensity = m_Density;
  251. delete [] m_pSmokeParticleInfos;
  252. m_pSmokeParticleInfos = new SmokeParticleInfo[m_xCount * m_yCount * m_zCount];
  253. Start( ParticleMgr(), NULL );
  254. }
  255. BaseClass::OnDataChanged( updateType );
  256. }
  257. void C_FuncSmokeVolume::Start( CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs )
  258. {
  259. if( !pParticleMgr->AddEffect( &m_ParticleEffect, this ) )
  260. return;
  261. m_MaterialHandle = m_ParticleEffect.FindOrAddMaterial( m_MaterialName );
  262. FillVolume();
  263. m_bStarted = true;
  264. }
  265. void C_FuncSmokeVolume::Update( float fTimeDelta )
  266. {
  267. // Update our world space bbox if we've moved at all.
  268. // We do this manually because sometimes people make HUGE bboxes, and if they're constantly changing because their
  269. // particles wander outside the current bounds sometimes, it'll be linking them into all the leaves repeatedly.
  270. const Vector &curOrigin = GetAbsOrigin();
  271. const QAngle &curAngles = GetAbsAngles();
  272. if ( !VectorsAreEqual( curOrigin, m_vLastOrigin, 0.1 ) ||
  273. fabs( curAngles.x - m_vLastAngles.x ) > 0.1 ||
  274. fabs( curAngles.y - m_vLastAngles.y ) > 0.1 ||
  275. fabs( curAngles.z - m_vLastAngles.z ) > 0.1 ||
  276. m_bFirstUpdate )
  277. {
  278. m_bFirstUpdate = false;
  279. m_vLastAngles = curAngles;
  280. m_vLastOrigin = curOrigin;
  281. Vector vWorldMins, vWorldMaxs;
  282. CollisionProp()->WorldSpaceAABB( &vWorldMins, &vWorldMaxs );
  283. vWorldMins -= Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius );
  284. vWorldMaxs += Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius );
  285. m_ParticleEffect.SetBBox( vWorldMins, vWorldMaxs );
  286. }
  287. float minViewOrigingDistance = FLT_MAX;
  288. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  289. {
  290. float d = CollisionProp()->CalcDistanceFromPoint( MainViewOrigin(hh) );
  291. if ( d < minViewOrigingDistance )
  292. minViewOrigingDistance = d;
  293. }
  294. float targetDensity = m_Density;
  295. if ( m_maxDrawDistance > 0 && minViewOrigingDistance > m_maxDrawDistance )
  296. {
  297. targetDensity = 0.0f;
  298. }
  299. // lerp m_CurrentDensity towards m_Density at a rate of m_DensityRampSpeed
  300. if( m_CurrentDensity < targetDensity )
  301. {
  302. m_CurrentDensity += m_DensityRampSpeed * fTimeDelta;
  303. if( m_CurrentDensity > targetDensity )
  304. {
  305. m_CurrentDensity = targetDensity;
  306. }
  307. }
  308. else if( m_CurrentDensity > targetDensity )
  309. {
  310. m_CurrentDensity -= m_DensityRampSpeed * fTimeDelta;
  311. if( m_CurrentDensity < targetDensity )
  312. {
  313. m_CurrentDensity = targetDensity;
  314. }
  315. }
  316. if( m_CurrentDensity == 0.0f )
  317. {
  318. return;
  319. }
  320. // This is used to randomize the direction it chooses to move a particle in.
  321. int offsetLookup[3] = {-1,0,1};
  322. float tradeDurationMax = m_ParticleSpacingDistance / ( m_MovementSpeed + 0.1f );
  323. float tradeDurationMin = tradeDurationMax * 0.5f;
  324. if ( IS_NAN( tradeDurationMax ) || IS_NAN( tradeDurationMin ) )
  325. return;
  326. // Warning( "tradeDuration: [%f,%f]\n", tradeDurationMin, tradeDurationMax );
  327. // Update all the moving traders and establish new ones.
  328. int nTotal = m_xCount * m_yCount * m_zCount;
  329. for( int i=0; i < nTotal; i++ )
  330. {
  331. SmokeParticleInfo *pInfo = &m_pSmokeParticleInfos[i];
  332. if(!pInfo->m_pParticle)
  333. continue;
  334. if(pInfo->m_TradeIndex == -1)
  335. {
  336. pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha;
  337. pInfo->m_pParticle->m_Color[0] = pInfo->m_Color[0];
  338. pInfo->m_pParticle->m_Color[1] = pInfo->m_Color[1];
  339. pInfo->m_pParticle->m_Color[2] = pInfo->m_Color[2];
  340. // Is there an adjacent one that's not trading?
  341. int x, y, z;
  342. GetParticleInfoXYZ(i, x, y, z);
  343. int xCountOffset = RandomInt( 0, VALVE_RAND_MAX );
  344. int yCountOffset = RandomInt( 0, VALVE_RAND_MAX );
  345. int zCountOffset = RandomInt( 0, VALVE_RAND_MAX );
  346. bool bFound = false;
  347. for(int xCount=0; xCount < 3 && !bFound; xCount++)
  348. {
  349. for(int yCount=0; yCount < 3 && !bFound; yCount++)
  350. {
  351. for(int zCount=0; zCount < 3; zCount++)
  352. {
  353. int testX = x + offsetLookup[(xCount+xCountOffset) % 3];
  354. int testY = y + offsetLookup[(yCount+yCountOffset) % 3];
  355. int testZ = z + offsetLookup[(zCount+zCountOffset) % 3];
  356. if(testX == x && testY == y && testZ == z)
  357. continue;
  358. if(IsValidXYZCoords(testX, testY, testZ))
  359. {
  360. SmokeParticleInfo *pOther = GetSmokeParticleInfo(testX, testY, testZ);
  361. if(pOther->m_pParticle && pOther->m_TradeIndex == -1)
  362. {
  363. // Ok, this one is looking to trade also.
  364. pInfo->m_TradeIndex = GetSmokeParticleIndex(testX, testY, testZ);
  365. pOther->m_TradeIndex = i;
  366. pInfo->m_TradeClock = pOther->m_TradeClock = 0;
  367. pOther->m_TradeDuration = pInfo->m_TradeDuration = FRand( tradeDurationMin, tradeDurationMax );
  368. bFound = true;
  369. break;
  370. }
  371. }
  372. }
  373. }
  374. }
  375. }
  376. else
  377. {
  378. SmokeParticleInfo *pOther = &m_pSmokeParticleInfos[pInfo->m_TradeIndex];
  379. assert(pOther->m_TradeIndex == i);
  380. // This makes sure the trade only gets updated once per frame.
  381. if(pInfo < pOther)
  382. {
  383. // Increment the trade clock..
  384. pInfo->m_TradeClock = (pOther->m_TradeClock += fTimeDelta);
  385. int x, y, z;
  386. GetParticleInfoXYZ(i, x, y, z);
  387. Vector myPos = GetSmokeParticlePos(x, y, z);
  388. int otherX, otherY, otherZ;
  389. GetParticleInfoXYZ(pInfo->m_TradeIndex, otherX, otherY, otherZ);
  390. Vector otherPos = GetSmokeParticlePos(otherX, otherY, otherZ);
  391. // Is the trade finished?
  392. if(pInfo->m_TradeClock >= pInfo->m_TradeDuration)
  393. {
  394. pInfo->m_TradeIndex = pOther->m_TradeIndex = -1;
  395. pInfo->m_pParticle->m_Pos = otherPos;
  396. pOther->m_pParticle->m_Pos = myPos;
  397. SmokeGrenadeParticle *temp = pInfo->m_pParticle;
  398. pInfo->m_pParticle = pOther->m_pParticle;
  399. pOther->m_pParticle = temp;
  400. }
  401. else
  402. {
  403. // Ok, move them closer.
  404. float percent = (float)cos(pInfo->m_TradeClock * 2 * 1.57079632f / pInfo->m_TradeDuration);
  405. percent = percent * 0.5 + 0.5;
  406. pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * (1 - percent);
  407. pOther->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * percent;
  408. InterpColor(pInfo->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, 1-percent);
  409. InterpColor(pOther->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, percent);
  410. pInfo->m_pParticle->m_Pos = myPos + (otherPos - myPos) * (1 - percent);
  411. pOther->m_pParticle->m_Pos = myPos + (otherPos - myPos) * percent;
  412. }
  413. }
  414. }
  415. }
  416. }
  417. void C_FuncSmokeVolume::RenderParticles( CParticleRenderIterator *pIterator )
  418. {
  419. if ( m_CurrentDensity == 0 )
  420. return;
  421. const SmokeGrenadeParticle *pParticle = (const SmokeGrenadeParticle*)pIterator->GetFirst();
  422. while ( pParticle )
  423. {
  424. Vector renderPos = pParticle->m_Pos;
  425. // Fade out globally.
  426. float alpha = m_CurrentDensity;
  427. // Apply the precalculated fade alpha from world geometry.
  428. alpha *= pParticle->m_FadeAlpha;
  429. // TODO: optimize this whole routine!
  430. Vector color = m_MinColor + (m_MaxColor - m_MinColor) * (pParticle->m_ColorInterp / 255.1f);
  431. if ( IsEmissive() )
  432. {
  433. color.x += pParticle->m_Color[0] / 255.0f;
  434. color.y += pParticle->m_Color[1] / 255.0f;
  435. color.z += pParticle->m_Color[2] / 255.0f;
  436. color.x = clamp( color.x, 0.0f, 1.0f );
  437. color.y = clamp( color.y, 0.0f, 1.0f );
  438. color.z = clamp( color.z, 0.0f, 1.0f );
  439. }
  440. else
  441. {
  442. color.x *= pParticle->m_Color[0] / 255.0f;
  443. color.y *= pParticle->m_Color[1] / 255.0f;
  444. color.z *= pParticle->m_Color[2] / 255.0f;
  445. }
  446. Vector tRenderPos;
  447. TransformParticle( ParticleMgr()->GetModelView(), renderPos, tRenderPos );
  448. float sortKey = 1;//tRenderPos.z;
  449. RenderParticle_ColorSizeAngle(
  450. pIterator->GetParticleDraw(),
  451. tRenderPos,
  452. color,
  453. alpha * GetAlphaDistanceFade(tRenderPos, 10, 30), // Alpha
  454. m_ParticleRadius,
  455. pParticle->m_CurRotation
  456. );
  457. pParticle = (const SmokeGrenadeParticle*)pIterator->GetNext( sortKey );
  458. }
  459. }
  460. void C_FuncSmokeVolume::SimulateParticles( CParticleSimulateIterator *pIterator )
  461. {
  462. if ( m_CurrentDensity == 0 )
  463. return;
  464. SmokeGrenadeParticle *pParticle = (SmokeGrenadeParticle*)pIterator->GetFirst();
  465. while ( pParticle )
  466. {
  467. pParticle->m_CurRotation += pParticle->m_RotationFactor * ( M_PI / 180.0f ) * m_RotationSpeed * pIterator->GetTimeDelta();
  468. pParticle = (SmokeGrenadeParticle*)pIterator->GetNext();
  469. }
  470. }
  471. void C_FuncSmokeVolume::NotifyRemove()
  472. {
  473. m_xCount = m_yCount = m_zCount = 0;
  474. }
  475. void C_FuncSmokeVolume::FillVolume()
  476. {
  477. Vector vPos;
  478. for(int x=0; x < m_xCount; x++)
  479. {
  480. for(int y=0; y < m_yCount; y++)
  481. {
  482. for(int z=0; z < m_zCount; z++)
  483. {
  484. vPos = GetSmokeParticlePos( x, y, z );
  485. if(SmokeParticleInfo *pInfo = GetSmokeParticleInfo(x,y,z))
  486. {
  487. int contents = GetWorldPointContents(vPos);
  488. if(contents & CONTENTS_SOLID)
  489. {
  490. pInfo->m_pParticle = NULL;
  491. }
  492. else
  493. {
  494. SmokeGrenadeParticle *pParticle =
  495. (SmokeGrenadeParticle*)m_ParticleEffect.AddParticle(sizeof(SmokeGrenadeParticle), m_MaterialHandle);
  496. if(pParticle)
  497. {
  498. pParticle->m_Pos = vPos;
  499. pParticle->m_ColorInterp = (unsigned char)( (rand() * 255) / VALVE_RAND_MAX );
  500. pParticle->m_RotationFactor = FRand( -1.0f, 1.0f ); // Rotation factor.
  501. pParticle->m_CurRotation = FRand( -m_RotationSpeed, m_RotationSpeed );
  502. }
  503. #ifdef _DEBUG
  504. int testX, testY, testZ;
  505. int index = GetSmokeParticleIndex(x,y,z);
  506. GetParticleInfoXYZ(index, testX, testY, testZ);
  507. assert(testX == x && testY == y && testZ == z);
  508. #endif
  509. Vector vColor = EngineGetLightForPoint(vPos);
  510. pInfo->m_Color[0] = LinearToTexture( vColor.x );
  511. pInfo->m_Color[1] = LinearToTexture( vColor.y );
  512. pInfo->m_Color[2] = LinearToTexture( vColor.z );
  513. // Cast some rays and if it's too close to anything, fade its alpha down.
  514. pInfo->m_FadeAlpha = 1;
  515. for(int i=0; i < NUM_FADE_PLANES; i++)
  516. {
  517. trace_t trace;
  518. WorldTraceLine(vPos, vPos + s_FadePlaneDirections[i] * 100, MASK_SOLID_BRUSHONLY, &trace);
  519. if(trace.fraction < 1.0f)
  520. {
  521. float dist = DotProduct(trace.plane.normal, vPos) - trace.plane.dist;
  522. if(dist < 0)
  523. {
  524. pInfo->m_FadeAlpha = 0;
  525. }
  526. else if(dist < m_ParticleRadius)
  527. {
  528. float alphaScale = dist / m_ParticleRadius;
  529. alphaScale *= alphaScale * alphaScale;
  530. pInfo->m_FadeAlpha *= alphaScale;
  531. }
  532. }
  533. }
  534. pInfo->m_pParticle = pParticle;
  535. pInfo->m_TradeIndex = -1;
  536. }
  537. }
  538. }
  539. }
  540. }
  541. }