Team Fortress 2 Source Code as on 22/4/2020
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.

475 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "particlemgr.h"
  8. #include "particle_prototype.h"
  9. #include "particle_util.h"
  10. #include "surfinfo.h"
  11. #include "baseparticleentity.h"
  12. #include "materialsystem/imaterialsystemhardwareconfig.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. // ------------------------------------------------------------------------- //
  16. // Definitions
  17. // ------------------------------------------------------------------------- //
  18. #define NUM_AR2_EXPLOSION_PARTICLES 70
  19. #define AR2_DUST_RADIUS 240 // 340
  20. #define AR2_DUST_LIFETIME 4
  21. #define AR2_DUST_LIFETIME_DELTA 6
  22. #define AR2_DUST_SPEED 10000
  23. #define AR2_DUST_STARTSIZE 8
  24. #define AR2_DUST_ENDSIZE 32
  25. #define AR2_DUST_ALPHA 0.5f
  26. #define AR2_DUST_FADE_IN_TIME 0.25f
  27. static Vector g_AR2DustColor1(0.35, 0.345, 0.33 );
  28. static Vector g_AR2DustColor2(0.75, 0.75, 0.7);
  29. // ------------------------------------------------------------------------- //
  30. // Classes
  31. // ------------------------------------------------------------------------- //
  32. class C_AR2Explosion : public C_BaseParticleEntity, public IPrototypeAppEffect
  33. {
  34. public:
  35. DECLARE_CLASS( C_AR2Explosion, C_BaseParticleEntity );
  36. DECLARE_CLIENTCLASS();
  37. C_AR2Explosion();
  38. ~C_AR2Explosion();
  39. private:
  40. class AR2ExplosionParticle : public StandardParticle_t
  41. {
  42. public:
  43. float m_Dist;
  44. Vector m_Start;
  45. float m_Roll;
  46. float m_RollSpeed;
  47. float m_Dwell;
  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. public:
  61. CParticleMgr *m_pParticleMgr;
  62. PMaterialHandle m_MaterialHandle;
  63. private:
  64. char m_szMaterialName[255];
  65. C_AR2Explosion( const C_AR2Explosion & );
  66. };
  67. // Expose to the particle app.
  68. EXPOSE_PROTOTYPE_EFFECT(AR2Explosion, C_AR2Explosion);
  69. IMPLEMENT_CLIENTCLASS_DT(C_AR2Explosion, DT_AR2Explosion, AR2Explosion)
  70. RecvPropString( RECVINFO( m_szMaterialName ) ),
  71. END_RECV_TABLE()
  72. // ------------------------------------------------------------------------- //
  73. // Helpers.
  74. // ------------------------------------------------------------------------- //
  75. // Given a line segment from vStart to vEnd
  76. // and a list of convex polygons in pSurfInfos and nSurfInfos,
  77. // fill in the list of which polygons the segment intersects.
  78. // Returns the number of intersected surfaces.
  79. static int IntersectSegmentWithSurfInfos(
  80. const Vector &vStart,
  81. const Vector &vEnd,
  82. SurfInfo *pSurfInfos,
  83. const int nSurfInfos,
  84. SurfInfo ** pIntersections,
  85. Vector *pIntersectionPositions,
  86. int nMaxIntersections)
  87. {
  88. if(nMaxIntersections == 0)
  89. return 0;
  90. int nIntersections = 0;
  91. for(int i=0; i < nSurfInfos; i++)
  92. {
  93. SurfInfo *pSurf = &pSurfInfos[i];
  94. // Does it intersect the plane?
  95. float dot1 = pSurf->m_Plane.DistTo(vStart);
  96. float dot2 = pSurf->m_Plane.DistTo(vEnd);
  97. if((dot1 > 0) != (dot2 > 0))
  98. {
  99. float t = dot1 / (dot1 - dot2);
  100. Vector vIntersection = vStart + (vEnd - vStart) * t;
  101. // If the intersection is behind any edge plane, then it's not inside the polygon.
  102. unsigned long iEdge;
  103. for(iEdge=0; iEdge < pSurf->m_nVerts; iEdge++)
  104. {
  105. VPlane edgePlane;
  106. edgePlane.m_Normal = pSurf->m_Plane.m_Normal.Cross(pSurf->m_Verts[iEdge] - pSurf->m_Verts[(iEdge+1)%pSurf->m_nVerts]);
  107. VectorNormalize( edgePlane.m_Normal );
  108. edgePlane.m_Dist = edgePlane.m_Normal.Dot(pSurf->m_Verts[iEdge]);
  109. if(edgePlane.DistTo(vIntersection) < 0.0f)
  110. break;
  111. }
  112. if(iEdge == pSurf->m_nVerts)
  113. {
  114. pIntersections[nIntersections] = pSurf;
  115. pIntersectionPositions[nIntersections] = vIntersection;
  116. ++nIntersections;
  117. if(nIntersections >= nMaxIntersections)
  118. break;
  119. }
  120. }
  121. }
  122. return nIntersections;
  123. }
  124. // ------------------------------------------------------------------------- //
  125. // C_AR2Explosion
  126. // ------------------------------------------------------------------------- //
  127. C_AR2Explosion::C_AR2Explosion()
  128. {
  129. m_pParticleMgr = NULL;
  130. m_MaterialHandle = INVALID_MATERIAL_HANDLE;
  131. }
  132. C_AR2Explosion::~C_AR2Explosion()
  133. {
  134. }
  135. void C_AR2Explosion::OnDataChanged(DataUpdateType_t updateType)
  136. {
  137. C_BaseEntity::OnDataChanged(updateType);
  138. if(updateType == DATA_UPDATE_CREATED)
  139. {
  140. Start(ParticleMgr(), NULL);
  141. }
  142. }
  143. static ConVar mat_reduceparticles( "mat_reduceparticles", "0" );
  144. void C_AR2Explosion::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs)
  145. {
  146. m_pParticleMgr = pParticleMgr;
  147. if(!pParticleMgr->AddEffect(&m_ParticleEffect, this))
  148. return;
  149. if (!m_szMaterialName[0])
  150. {
  151. Q_strncpy(m_szMaterialName, "particle/particle_noisesphere", sizeof( m_szMaterialName ) );
  152. }
  153. m_MaterialHandle = m_ParticleEffect.FindOrAddMaterial(m_szMaterialName);
  154. // Precalculate stuff for the particle spawning..
  155. #define NUM_DUSTEMITTER_SURFINFOS 128
  156. SurfInfo surfInfos[NUM_DUSTEMITTER_SURFINFOS];
  157. int nSurfInfos;
  158. // Center of explosion.
  159. Vector vCenter = GetAbsOrigin(); // HACKHACK.. when the engine bug is fixed, use origin.
  160. if ( IsXbox() )
  161. {
  162. m_ParticleEffect.SetBBox( vCenter-Vector(300,300,300), vCenter+Vector(300,300,300) );
  163. }
  164. #ifdef PARTICLEPROTOTYPE_APP
  165. float surfSize = 10000;
  166. nSurfInfos = 1;
  167. surfInfos[0].m_Verts[0].Init(-surfSize,-surfSize,0);
  168. surfInfos[0].m_Verts[1].Init(-surfSize,surfSize,0);
  169. surfInfos[0].m_Verts[2].Init(surfSize, surfSize,0);
  170. surfInfos[0].m_Verts[3].Init(surfSize,-surfSize,0);
  171. surfInfos[0].m_nVerts = 4;
  172. surfInfos[0].m_Plane.m_Normal.Init(0,0,1);
  173. surfInfos[0].m_Plane.m_Dist = -3;
  174. #else
  175. {
  176. nSurfInfos = 0;
  177. C_BaseEntity *ent = cl_entitylist->GetEnt( 0 );
  178. if ( ent )
  179. {
  180. nSurfInfos = engine->GetIntersectingSurfaces(
  181. ent->GetModel(),
  182. vCenter,
  183. AR2_DUST_RADIUS,
  184. true,
  185. surfInfos,
  186. NUM_DUSTEMITTER_SURFINFOS);
  187. }
  188. }
  189. #endif
  190. int nParticles = 0;
  191. int iParticlesToSpawn = NUM_AR2_EXPLOSION_PARTICLES;
  192. // In DX7, much fewer particles
  193. if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 )
  194. {
  195. iParticlesToSpawn *= 0.25;
  196. }
  197. else if ( mat_reduceparticles.GetBool() )
  198. {
  199. iParticlesToSpawn *= 0.025;
  200. }
  201. if( nSurfInfos > 0 )
  202. {
  203. // For nParticles*N, generate a ray and cast it out. If it hits anything, spawn a particle there.
  204. int nTestsPerParticle=3;
  205. for(int i=0; i < iParticlesToSpawn; i++)
  206. {
  207. for(int iTest=0; iTest < nTestsPerParticle; iTest++)
  208. {
  209. Vector randVec = RandomVector(-1,1);
  210. VectorNormalize( randVec );
  211. Vector startPos = vCenter + randVec * AR2_DUST_RADIUS;
  212. randVec = RandomVector(-1,1);
  213. VectorNormalize( randVec );
  214. Vector endPos = vCenter + randVec * AR2_DUST_RADIUS;
  215. #define MAX_SURFINFO_INTERSECTIONS 4
  216. SurfInfo *pIntersected[MAX_SURFINFO_INTERSECTIONS];
  217. Vector vIntersections[MAX_SURFINFO_INTERSECTIONS];
  218. int nIntersections;
  219. nIntersections = IntersectSegmentWithSurfInfos(
  220. startPos,
  221. endPos,
  222. surfInfos,
  223. nSurfInfos,
  224. pIntersected,
  225. vIntersections,
  226. MAX_SURFINFO_INTERSECTIONS);
  227. if(nIntersections)
  228. {
  229. int iIntersection = rand() % nIntersections;
  230. Vector velocity;
  231. //velocity.Init(-1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f, -1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f, -1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f);
  232. //velocity = velocity * FRand(m_MinSpeed, m_MaxSpeed);
  233. Vector direction = (vIntersections[iIntersection] - vCenter );
  234. float dist = VectorNormalize( direction );
  235. if(dist > AR2_DUST_RADIUS)
  236. dist = AR2_DUST_RADIUS;
  237. static float power = 2.0f;
  238. float falloffMul = pow(1.0f - dist / AR2_DUST_RADIUS, power);
  239. Vector reflection = direction - 2 * DotProduct( direction, pIntersected[iIntersection]->m_Plane.m_Normal ) * pIntersected[iIntersection]->m_Plane.m_Normal;
  240. VectorNormalize( reflection );
  241. velocity = reflection * AR2_DUST_SPEED * falloffMul;
  242. // velocity = velocity + (vIntersections[iIntersection] - vCenter) * falloffMul;
  243. /*
  244. debugoverlay->AddLineOverlay( vIntersections[iIntersection],
  245. vIntersections[iIntersection] + reflection * 64,
  246. 128, 128, 255, false, 15.0 );
  247. */
  248. #if 1
  249. AR2ExplosionParticle *pParticle =
  250. (AR2ExplosionParticle*)m_ParticleEffect.AddParticle( sizeof(AR2ExplosionParticle), m_MaterialHandle );
  251. if(pParticle)
  252. {
  253. pParticle->m_Pos = vIntersections[iIntersection];
  254. pParticle->m_Start = pParticle->m_Pos;
  255. pParticle->m_Dist = 8.0;
  256. pParticle->m_Velocity = velocity;
  257. // sound == 13031.496062992125984251968503937ips
  258. pParticle->m_Lifetime = -dist / 13031.5f - 0.1;
  259. pParticle->m_Roll = FRand( 0, M_PI * 2 );
  260. pParticle->m_RollSpeed = FRand( -1, 1 ) * 0.4;
  261. pParticle->m_Dwell = AR2_DUST_LIFETIME + random->RandomFloat( 0, AR2_DUST_LIFETIME_DELTA );
  262. nParticles++;
  263. break;
  264. }
  265. #endif
  266. }
  267. }
  268. }
  269. }
  270. // build interior smoke particles
  271. for(int i=nParticles; i < iParticlesToSpawn; i++)
  272. {
  273. Vector randVec = RandomVector(-1,1);
  274. VectorNormalize( randVec );
  275. Vector endPos = vCenter + randVec * AR2_DUST_RADIUS / 4.0;
  276. Vector direction = (endPos - vCenter );
  277. float dist = VectorNormalize( direction ) + random->RandomFloat( 0, AR2_DUST_RADIUS / 4.0 );
  278. if(dist > AR2_DUST_RADIUS)
  279. dist = AR2_DUST_RADIUS;
  280. static float power = 2.0f;
  281. float falloffMul = pow(1.0f - dist / (AR2_DUST_RADIUS / 2), power);
  282. Vector velocity = direction * AR2_DUST_SPEED * falloffMul;
  283. AR2ExplosionParticle *pParticle =
  284. (AR2ExplosionParticle*)m_ParticleEffect.AddParticle( sizeof(AR2ExplosionParticle), m_MaterialHandle );
  285. if(pParticle)
  286. {
  287. pParticle->m_Pos = endPos;
  288. pParticle->m_Start = pParticle->m_Pos;
  289. pParticle->m_Dist = 8.0;
  290. pParticle->m_Velocity = velocity;
  291. // sound == 13031.496062992125984251968503937ips
  292. pParticle->m_Lifetime = -dist / 13031.5f - 0.1;
  293. pParticle->m_Roll = FRand( 0, M_PI * 2 );
  294. pParticle->m_RollSpeed = FRand( -1, 1 ) * 4.0;
  295. pParticle->m_Dwell = 0.5 * (AR2_DUST_LIFETIME + random->RandomFloat( 0, AR2_DUST_LIFETIME_DELTA ));
  296. }
  297. }
  298. }
  299. void C_AR2Explosion::Update(float fTimeDelta)
  300. {
  301. if(!m_pParticleMgr)
  302. return;
  303. }
  304. void C_AR2Explosion::SimulateParticles( CParticleSimulateIterator *pIterator )
  305. {
  306. float dt = pIterator->GetTimeDelta();
  307. AR2ExplosionParticle *pParticle = (AR2ExplosionParticle*)pIterator->GetFirst();
  308. while ( pParticle )
  309. {
  310. if (dt > 0.05)
  311. dt = 0.05; // yuck, air resistance function craps out at less then 20fps
  312. // Update its lifetime.
  313. pParticle->m_Lifetime += dt; // pDraw->GetTimeDelta();
  314. if(pParticle->m_Lifetime > pParticle->m_Dwell)
  315. {
  316. // faded to nothing....
  317. pIterator->RemoveParticle( pParticle );
  318. }
  319. else
  320. {
  321. // Spin the thing
  322. pParticle->m_Roll += pParticle->m_RollSpeed * pIterator->GetTimeDelta();
  323. // delayed?
  324. if ( pParticle->m_Lifetime >= 0.0f )
  325. {
  326. // Move it (this comes after rendering to make it clear that moving the particle here won't change
  327. // its rendering for this frame since m_TransformedPos has already been set).
  328. pParticle->m_Pos = pParticle->m_Pos + pParticle->m_Velocity * dt;
  329. // keep track of distance traveled
  330. pParticle->m_Dist = pParticle->m_Dist + pParticle->m_Velocity.Length() * dt;
  331. // Dampen velocity.
  332. float dist = pParticle->m_Velocity.Length() * dt;
  333. float r = dist * dist;
  334. // FIXME: this is a really screwy air-resistance function....
  335. pParticle->m_Velocity = pParticle->m_Velocity * (100 / (100 + r ));
  336. // dampen roll
  337. static float dtime;
  338. static float decay;
  339. if (dtime != dt)
  340. {
  341. dtime = dt;
  342. decay = ExponentialDecay( 0.3, 1.0, dtime );
  343. }
  344. if (fabs(pParticle->m_RollSpeed) > 0.2)
  345. pParticle->m_RollSpeed = pParticle->m_RollSpeed * decay;
  346. }
  347. }
  348. pParticle = (AR2ExplosionParticle*)pIterator->GetNext();
  349. }
  350. }
  351. void C_AR2Explosion::RenderParticles( CParticleRenderIterator *pIterator )
  352. {
  353. const AR2ExplosionParticle *pParticle = (const AR2ExplosionParticle *)pIterator->GetFirst();
  354. while ( pParticle )
  355. {
  356. float sortKey = 0;
  357. if ( pParticle->m_Lifetime >= 0.0f )
  358. {
  359. // Draw.
  360. float lifetimePercent = ( pParticle->m_Lifetime - AR2_DUST_FADE_IN_TIME ) / pParticle->m_Dwell;
  361. // FIXME: base color should be a dirty version of the material color
  362. Vector color = g_AR2DustColor1 * (1.0 - lifetimePercent) + g_AR2DustColor2 * lifetimePercent;
  363. Vector tPos;
  364. TransformParticle(m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos);
  365. sortKey = tPos.z;
  366. float alpha;
  367. if ( pParticle->m_Lifetime < AR2_DUST_FADE_IN_TIME )
  368. {
  369. alpha = AR2_DUST_ALPHA * ( pParticle->m_Lifetime / AR2_DUST_FADE_IN_TIME );
  370. }
  371. else
  372. {
  373. alpha = AR2_DUST_ALPHA * ( 1.0f - lifetimePercent );
  374. }
  375. alpha *= GetAlphaDistanceFade( tPos, IsXbox() ? 100 : 50, IsXbox() ? 200 : 150 );
  376. RenderParticle_ColorSizeAngle(
  377. pIterator->GetParticleDraw(),
  378. tPos,
  379. color,
  380. alpha,
  381. pParticle->m_Dist, // size based on how far it's traveled
  382. pParticle->m_Roll);
  383. }
  384. pParticle = (const AR2ExplosionParticle *)pIterator->GetNext( sortKey );
  385. }
  386. }