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.

544 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: particle system code
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "particles/particles.h"
  8. #include "filesystem.h"
  9. #include "tier2/tier2.h"
  10. #include "tier2/fileutils.h"
  11. #include "tier1/UtlStringMap.h"
  12. #include "tier1/strtools.h"
  13. #ifdef USE_BLOBULATOR
  14. // TODO: These should be in public by the time the SDK ships
  15. #include "../common/blobulator/Physics/PhysParticle.h"
  16. #include "../common/blobulator/Physics/PhysParticleCache_inl.h"
  17. #include "../common/blobulator/Physics/PhysTiler.h"
  18. #endif
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. class C_OP_RandomForce : public CParticleOperatorInstance
  22. {
  23. DECLARE_PARTICLE_OPERATOR( C_OP_RandomForce );
  24. uint32 GetWrittenAttributes( void ) const
  25. {
  26. return 0;
  27. }
  28. uint32 GetReadAttributes( void ) const
  29. {
  30. return 0;
  31. }
  32. virtual void AddForces( FourVectors *pAccumulatedForces,
  33. CParticleCollection *pParticles,
  34. int nBlocks,
  35. float flStrength,
  36. void *pContext ) const;
  37. Vector m_MinForce;
  38. Vector m_MaxForce;
  39. };
  40. void C_OP_RandomForce::AddForces( FourVectors *pAccumulatedForces,
  41. CParticleCollection *pParticles,
  42. int nBlocks,
  43. float flStrength,
  44. void *pContext ) const
  45. {
  46. FourVectors box_min,box_max;
  47. box_min.DuplicateVector( m_MinForce * flStrength );
  48. box_max.DuplicateVector( m_MaxForce * flStrength);
  49. box_max -= box_min;
  50. int nContext = GetSIMDRandContext();
  51. for(int i=0;i<nBlocks;i++)
  52. {
  53. pAccumulatedForces->x = AddSIMD(
  54. pAccumulatedForces->x, AddSIMD( box_min.x, MulSIMD( box_max.x, RandSIMD( nContext) ) ) );
  55. pAccumulatedForces->y = AddSIMD(
  56. pAccumulatedForces->y, AddSIMD( box_min.y, MulSIMD( box_max.y, RandSIMD( nContext) ) ) );
  57. pAccumulatedForces->z = AddSIMD(
  58. pAccumulatedForces->z, AddSIMD( box_min.z, MulSIMD( box_max.z, RandSIMD( nContext) ) ) );
  59. pAccumulatedForces++;
  60. }
  61. ReleaseSIMDRandContext( nContext );
  62. }
  63. DEFINE_PARTICLE_OPERATOR( C_OP_RandomForce, "random force", OPERATOR_GENERIC );
  64. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RandomForce )
  65. DMXELEMENT_UNPACK_FIELD( "min force", "0 0 0", Vector, m_MinForce )
  66. DMXELEMENT_UNPACK_FIELD( "max force", "0 0 0", Vector, m_MaxForce )
  67. END_PARTICLE_OPERATOR_UNPACK( C_OP_RandomForce )
  68. class C_OP_TwistAroundAxis : public CParticleOperatorInstance
  69. {
  70. DECLARE_PARTICLE_OPERATOR( C_OP_TwistAroundAxis );
  71. uint32 GetWrittenAttributes( void ) const
  72. {
  73. return 0;
  74. }
  75. uint32 GetReadAttributes( void ) const
  76. {
  77. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  78. }
  79. virtual void AddForces( FourVectors *pAccumulatedForces,
  80. CParticleCollection *pParticles,
  81. int nBlocks,
  82. float flStrength,
  83. void *pContext ) const;
  84. float m_fForceAmount;
  85. Vector m_TwistAxis;
  86. bool m_bLocalSpace;
  87. };
  88. void C_OP_TwistAroundAxis::AddForces( FourVectors *pAccumulatedForces,
  89. CParticleCollection *pParticles,
  90. int nBlocks,
  91. float flStrength,
  92. void *pContext ) const
  93. {
  94. FourVectors Twist_AxisInWorldSpace;
  95. Twist_AxisInWorldSpace.DuplicateVector( pParticles->TransformAxis( m_TwistAxis, m_bLocalSpace ) );
  96. Twist_AxisInWorldSpace.VectorNormalize();
  97. Vector vecCenter;
  98. pParticles->GetControlPointAtTime( 0, pParticles->m_flCurTime, &vecCenter );
  99. FourVectors Center;
  100. Center.DuplicateVector( vecCenter );
  101. size_t nPosStride;
  102. fltx4 ForceScale = ReplicateX4( m_fForceAmount * flStrength );
  103. const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
  104. for(int i=0;i<nBlocks;i++)
  105. {
  106. FourVectors ofs=*pPos;
  107. ofs -= Center;
  108. fltx4 bGoodLen = CmpGtSIMD( ofs*ofs, Four_Epsilons );
  109. ofs.VectorNormalize();
  110. FourVectors parallel_comp=ofs;
  111. parallel_comp *= ( ofs*Twist_AxisInWorldSpace );
  112. ofs-=parallel_comp;
  113. bGoodLen = AndSIMD( bGoodLen, CmpGtSIMD( ofs*ofs, Four_Epsilons ) );
  114. ofs.VectorNormalize();
  115. FourVectors TangentialForce = ofs ^ Twist_AxisInWorldSpace;
  116. TangentialForce *= ForceScale;
  117. TangentialForce.x = AndSIMD( TangentialForce.x, bGoodLen );
  118. TangentialForce.y = AndSIMD( TangentialForce.y, bGoodLen );
  119. TangentialForce.z = AndSIMD( TangentialForce.z, bGoodLen );
  120. *(pAccumulatedForces++) += TangentialForce;
  121. pPos += nPosStride;
  122. }
  123. }
  124. DEFINE_PARTICLE_OPERATOR( C_OP_TwistAroundAxis, "twist around axis", OPERATOR_GENERIC );
  125. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_TwistAroundAxis )
  126. DMXELEMENT_UNPACK_FIELD( "amount of force", "0", float, m_fForceAmount )
  127. DMXELEMENT_UNPACK_FIELD( "twist axis", "0 0 1", Vector, m_TwistAxis )
  128. DMXELEMENT_UNPACK_FIELD( "object local space axis 0/1","0", bool, m_bLocalSpace )
  129. END_PARTICLE_OPERATOR_UNPACK( C_OP_TwistAroundAxis )
  130. class C_OP_AttractToControlPoint : public CParticleOperatorInstance
  131. {
  132. DECLARE_PARTICLE_OPERATOR( C_OP_AttractToControlPoint );
  133. uint32 GetWrittenAttributes( void ) const
  134. {
  135. return 0;
  136. }
  137. uint32 GetReadAttributes( void ) const
  138. {
  139. return 0;
  140. }
  141. virtual uint64 GetReadControlPointMask() const
  142. {
  143. return 1ULL << m_nControlPointNumber;
  144. }
  145. virtual void AddForces( FourVectors *pAccumulatedForces,
  146. CParticleCollection *pParticles,
  147. int nBlocks,
  148. float flStrength,
  149. void *pContext ) const;
  150. float m_fForceAmount;
  151. float m_fFalloffPower;
  152. int m_nControlPointNumber;
  153. };
  154. void C_OP_AttractToControlPoint::AddForces( FourVectors *pAccumulatedForces,
  155. CParticleCollection *pParticles,
  156. int nBlocks,
  157. float flStrength,
  158. void *pContext ) const
  159. {
  160. int power_frac=-4.0*m_fFalloffPower; // convert to what pow_fixedpoint_exponent_simd wants
  161. fltx4 fForceScale=ReplicateX4( -m_fForceAmount * flStrength );
  162. Vector vecCenter;
  163. pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecCenter );
  164. FourVectors Center;
  165. Center.DuplicateVector( vecCenter );
  166. size_t nPosStride;
  167. const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
  168. for(int i=0;i<nBlocks;i++)
  169. {
  170. FourVectors ofs=*pPos;
  171. ofs -= Center;
  172. fltx4 len = ofs.length();
  173. ofs *= MulSIMD( fForceScale, ReciprocalSaturateSIMD( len )); // normalize and scale
  174. ofs *= Pow_FixedPoint_Exponent_SIMD( len, power_frac ); // * 1/pow(dist, exponent)
  175. fltx4 bGood = CmpGtSIMD( len, Four_Epsilons );
  176. ofs.x = AndSIMD( bGood, ofs.x );
  177. ofs.y = AndSIMD( bGood, ofs.y );
  178. ofs.z = AndSIMD( bGood, ofs.z );
  179. *(pAccumulatedForces++) += ofs;
  180. pPos += nPosStride;
  181. }
  182. }
  183. DEFINE_PARTICLE_OPERATOR( C_OP_AttractToControlPoint, "Pull towards control point", OPERATOR_GENERIC );
  184. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_AttractToControlPoint )
  185. DMXELEMENT_UNPACK_FIELD( "amount of force", "0", float, m_fForceAmount )
  186. DMXELEMENT_UNPACK_FIELD( "falloff power", "2", float, m_fFalloffPower )
  187. DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
  188. END_PARTICLE_OPERATOR_UNPACK( C_OP_AttractToControlPoint )
  189. #undef USE_BLOBULATOR // TODO (Ilya): Must fix this code
  190. #ifdef USE_BLOBULATOR
  191. class C_OP_LennardJonesForce : public CParticleOperatorInstance
  192. {
  193. DECLARE_PARTICLE_OPERATOR( C_OP_LennardJonesForce );
  194. uint32 GetWrittenAttributes( void ) const
  195. {
  196. return 0;
  197. }
  198. uint32 GetReadAttributes( void ) const
  199. {
  200. return 0;
  201. }
  202. void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
  203. {
  204. //m_pParticleCache = new ParticleCache(m_fInteractionRadius);
  205. m_pPhysTiler = new PhysTiler(m_fInteractionRadius);
  206. }
  207. virtual void AddForces( FourVectors *pAccumulatedForces,
  208. CParticleCollection *pParticles,
  209. int nBlocks,
  210. float flStrength,
  211. void *pContext ) const;
  212. // TODO: Have to destroy PhysTiler in destructor somewhere!!!!
  213. //ParticleCache* m_pParticleCache;
  214. PhysTiler* m_pPhysTiler;
  215. float m_fInteractionRadius;
  216. float m_fSurfaceTension;
  217. float m_fLennardJonesRepulsion;
  218. float m_fLennardJonesAttraction;
  219. float m_fMaxRepulsion;
  220. float m_fMaxAttraction;
  221. private:
  222. //virtual void addParticleForce(PhysParticle* a, PhysParticleCacheNode* bcn, float flStrength, float ts) const;
  223. virtual void addParticleForce(PhysParticle* a, PhysParticle* b, float distSq, float flStrength, float ts) const;
  224. };
  225. // TODO: I should make sure I don't have divide by zero errors.
  226. // TODO: ts is not used
  227. void C_OP_LennardJonesForce::addParticleForce(PhysParticle* a, PhysParticle* b, float distSq, float flStrength, float ts) const
  228. {
  229. float d = sqrtf(distSq);
  230. //========================================================
  231. // based on equation of force between two molecules which is
  232. // factor * ((distance/bond_length)^-7 - (distance/bond_length)^-13)
  233. float f;
  234. if(a->group == b->group) // In the same group
  235. {
  236. float p = a->radius * 2.0f / (d+FLT_EPSILON);
  237. float p2 = p * p;
  238. float p4 = p2 * p2;
  239. // Surface tension:
  240. //Notes:
  241. // Can average the neighbor count between the two particles...
  242. // I tried this, and discovered that rather than averaging, I can take maybe take the
  243. // larger of the two neighbor counts, so the attraction between two particles on the surface will be strong, but
  244. // the attraction between a particle inside and a particle on the surface will be weak. I can also try
  245. // taking the min so that the attraction between a particle on the surface and a particle inside the fluid will
  246. // be strong, but the attraction between two particles completely on the inside will be weak.
  247. //
  248. // int symmetric_neighbor_count = min(a->neighbor_count, b->neighbor_count);
  249. //
  250. // Can try having neighbors only cause stronger attraction (no repulsion)
  251. // Can try lower exponents for the LennardJones forces.
  252. // This is a trick to prevent single particles from floating off... the less neighbors a particle has.. the more it sticks
  253. // This also tends to simulate surface tension
  254. float surface_tension_modifier = ((24.0f * m_fSurfaceTension) / (a->neighbor_count + b->neighbor_count + 0.1f)) + 1.0f;
  255. //float lennard_jones_force = fLennardJones * 2.0f * (p2 - (p4 * p4));
  256. float lennard_jones_force = m_fLennardJonesAttraction * p2 - m_fLennardJonesRepulsion*p4;
  257. f = surface_tension_modifier * lennard_jones_force;
  258. // This is some older code:
  259. //f = ((35.0f * LampScene::simulationSurfaceTension) / (a->neighbor_count + 0.1f)) * (p2 - (p4 * p4));
  260. // used to be 68'
  261. //float factor = (b->neighbor_count < 13 && neighbor_count < 13 ? 4.0f : 0.5f);
  262. //f = factor * (p2 - (p2 * p2 * p2 * p2));
  263. }
  264. else
  265. {
  266. // This was 3.5 ... made 3.0 so particles get closer when they collide
  267. if(d > a->radius * 3.0f) return;
  268. float p = a->radius * 4.0f / d;
  269. f = -1.0f * p * p;
  270. }
  271. // These checks are great to have, but are they really necessary?
  272. // It might also be good to have a limit on velocity
  273. // Attraction is a positive value.
  274. // Repulsion is negative.
  275. if(f < -m_fMaxRepulsion) f = -m_fMaxRepulsion;
  276. if(f > m_fMaxAttraction) f = m_fMaxAttraction;
  277. Point3D scaledr = (b->center - a->center) * (f/(d+FLT_EPSILON)); // Dividing by d scales distance down to a unit vector
  278. a->force.add(scaledr);
  279. b->force.subtract(scaledr);
  280. }
  281. void C_OP_LennardJonesForce::AddForces( FourVectors *pAccumulatedForces,
  282. CParticleCollection *pParticles,
  283. int nBlocks,
  284. float flStrength,
  285. void *pContext ) const
  286. {
  287. int nParticles = pParticles->m_nActiveParticles; // Not sure if this is correct!
  288. size_t nPosStride;
  289. const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
  290. // The +4 is because particles are stored by PET in blocks of 4
  291. // However, not every block is full. Thus, nParticles may be
  292. // less than nBlocks*4. Could get rid of this if the swizzling/unswizzling
  293. // loop were better written.
  294. static SmartArray<PhysParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
  295. while(imp_particles_sa.size < nParticles+4)
  296. {
  297. imp_particles_sa.pushAutoSize(PhysParticle());
  298. }
  299. /*
  300. size_t nPrevPosStride;
  301. const FourVectors *pPrevPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &nPrevPosStride );
  302. */
  303. //m_pParticleCache->beginFrame();
  304. //m_pParticleCache->beginTile(nParticles);
  305. m_pPhysTiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f));
  306. // Unswizzle from the FourVectors format into particles
  307. for(int i=0, p=0;i<nBlocks;i++)
  308. {
  309. FourVectors ofs=*pPos;
  310. PhysParticle* particle = &(imp_particles_sa[p]);
  311. particle->force.clear();
  312. if(p < nParticles)
  313. {
  314. particle->center = ofs.Vec(0);
  315. particle->group = 0;
  316. particle->neighbor_count = 0;
  317. m_pPhysTiler->insertParticle(particle);
  318. }
  319. p++;
  320. particle = &(imp_particles_sa[p]);
  321. particle->force.clear();
  322. if(p < nParticles)
  323. {
  324. particle->center = ofs.Vec(1);
  325. particle->group = 0;
  326. particle->neighbor_count = 0;
  327. m_pPhysTiler->insertParticle(particle);
  328. }
  329. p++;
  330. particle = &(imp_particles_sa[p]);
  331. particle->force.clear();
  332. if(p < nParticles)
  333. {
  334. particle->center = ofs.Vec(2);
  335. particle->group = 0;
  336. particle->neighbor_count = 0;
  337. m_pPhysTiler->insertParticle(particle);
  338. }
  339. p++;
  340. particle = &(imp_particles_sa[p]);
  341. particle->force.clear();
  342. if(p < nParticles)
  343. {
  344. particle->center = ofs.Vec(3);
  345. particle->group = 0;
  346. particle->neighbor_count = 0;
  347. m_pPhysTiler->insertParticle(particle);
  348. }
  349. p++;
  350. pPos += nPosStride;
  351. }
  352. m_pPhysTiler->processTiles();
  353. float timeStep = 1.0f; // This should be customizable
  354. float nearNeighborInteractionRadius = 2.3f;
  355. float nearNeighborInteractionRadiusSq = nearNeighborInteractionRadius * nearNeighborInteractionRadius;
  356. PhysParticleCache* pCache = m_pPhysTiler->getParticleCache();
  357. // Calculate number of near neighbors for each particle
  358. for(int i = 0; i < nParticles; i++)
  359. {
  360. PhysParticle *b1 = &(imp_particles_sa[i]);
  361. PhysParticleAndDist* node = pCache->get(b1);
  362. while(node->particle != NULL)
  363. {
  364. PhysParticle* b2 = node->particle;
  365. // Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
  366. if(b1 < b2 && node->distSq < nearNeighborInteractionRadiusSq)
  367. {
  368. b1->neighbor_count++;
  369. b2->neighbor_count++;
  370. }
  371. node++;
  372. }
  373. }
  374. // Calculate forces on particles due to other particles
  375. for(int i = 0; i < nParticles; i++)
  376. {
  377. PhysParticle *b1 = &(imp_particles_sa[i]);
  378. PhysParticleAndDist* node = pCache->get(b1);
  379. while(node->particle != NULL)
  380. {
  381. PhysParticle* b2 = node->particle;
  382. // Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
  383. if(b1 < b2)
  384. {
  385. addParticleForce(b1, b2, node->distSq, flStrength, timeStep);
  386. }
  387. node++;
  388. }
  389. }
  390. /*
  391. for(ParticleListNode* bit3 = particles; bit3; bit3 = bit3->next)
  392. {
  393. Particle* b = bit3->particle;
  394. b->prev_group = b->group; // Set prev group
  395. //b1->addDirDragForce();
  396. b->move(ts); // Move the particle (it should never be used again until next iteration)
  397. }
  398. */
  399. m_pPhysTiler->endFrame();
  400. // Swizzle forces back into FourVectors format
  401. for(int i=0;i<nBlocks;i++)
  402. {
  403. pAccumulatedForces->X(0) += imp_particles_sa[i*4].force[0];
  404. pAccumulatedForces->Y(0) += imp_particles_sa[i*4].force[1];
  405. pAccumulatedForces->Z(0) += imp_particles_sa[i*4].force[2];
  406. pAccumulatedForces->X(1) += imp_particles_sa[i*4+1].force[0];
  407. pAccumulatedForces->Y(1) += imp_particles_sa[i*4+1].force[1];
  408. pAccumulatedForces->Z(1) += imp_particles_sa[i*4+1].force[2];
  409. pAccumulatedForces->X(2) += imp_particles_sa[i*4+2].force[0];
  410. pAccumulatedForces->Y(2) += imp_particles_sa[i*4+2].force[1];
  411. pAccumulatedForces->Z(2) += imp_particles_sa[i*4+2].force[2];
  412. pAccumulatedForces->X(3) += imp_particles_sa[i*4+3].force[0];
  413. pAccumulatedForces->Y(3) += imp_particles_sa[i*4+3].force[1];
  414. pAccumulatedForces->Z(3) += imp_particles_sa[i*4+3].force[2];
  415. pAccumulatedForces++;
  416. }
  417. }
  418. DEFINE_PARTICLE_OPERATOR( C_OP_LennardJonesForce, "lennard jones force", OPERATOR_GENERIC );
  419. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_LennardJonesForce )
  420. DMXELEMENT_UNPACK_FIELD( "interaction radius", "4", float, m_fInteractionRadius )
  421. DMXELEMENT_UNPACK_FIELD( "surface tension", "1", float, m_fSurfaceTension )
  422. DMXELEMENT_UNPACK_FIELD( "lennard jones attractive force", "1", float, m_fLennardJonesAttraction )
  423. DMXELEMENT_UNPACK_FIELD( "lennard jones repulsive force", "1", float, m_fLennardJonesRepulsion )
  424. DMXELEMENT_UNPACK_FIELD( "max repulsion", "100", float, m_fMaxRepulsion )
  425. DMXELEMENT_UNPACK_FIELD( "max attraction", "100", float, m_fMaxAttraction )
  426. END_PARTICLE_OPERATOR_UNPACK( C_OP_LennardJonesForce )
  427. #endif
  428. void AddBuiltInParticleForceGenerators( void )
  429. {
  430. REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_RandomForce );
  431. REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_TwistAroundAxis );
  432. REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_AttractToControlPoint );
  433. #ifdef USE_BLOBULATOR
  434. REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_LennardJonesForce );
  435. #endif
  436. }