CommonParticle.fxh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. //////////////////////////////////////////////////////////////////////////////
  2. // ©2006 Electronic Arts Inc
  3. //
  4. // Utility code for GPU vertex particle shaders
  5. //////////////////////////////////////////////////////////////////////////////
  6. #ifndef _COMMON_PARTICLE_FXH_
  7. #define _COMMON_PARTICLE_FXH_
  8. #include "Random.fxh"
  9. //
  10. // The particles can either be built out of 4, 5 or 9 verts:
  11. //
  12. // 0-------1
  13. // |\ /|
  14. // | 5---6 |
  15. // | |\ /| |
  16. // | | 4 | |
  17. // | |/ \| |
  18. // | 8---7 |
  19. // |/ \|
  20. // 3-------2
  21. //
  22. // With this vertex layout, the following geometry can be built:
  23. // Vertex 0-3 span a simple quad (2 triangles)
  24. // Vertex 0-4 are a quad with center point, forming 4 triangles
  25. // Vertex 0-8 builds two "concentric" quads (as in the diagram), with 12 triangles
  26. //
  27. #define MAX_VERTICES_PER_PARTICLE 9
  28. STATICARRAY const float2 VertexCorners[MAX_VERTICES_PER_PARTICLE] =
  29. {
  30. float2(-0.5f, -0.5f),
  31. float2(0.5f, -0.5f),
  32. float2(0.5f, 0.5f),
  33. float2(-0.5f, 0.5f),
  34. float2(0, 0),
  35. float2(-0.25, -0.25),
  36. float2(0.25, -0.25),
  37. float2(0.25, 0.25),
  38. float2(-0.25, 0.25)
  39. };
  40. float2 GetVertexTexCoord(float2 vertexCorner)
  41. {
  42. return vertexCorner * float2(1, -1) + float2(0.5, 0.5);
  43. }
  44. // Client frame rate used in time conversion
  45. static const float CLIENT_FRAMES_PER_SECOND = 30.0;
  46. //
  47. // Draw module data
  48. //
  49. // We allow only four keyframes on color and no alpha yet
  50. #define MAX_KEYFRAMES 4
  51. struct ParticleDraw
  52. {
  53. float4 VideoTex_NumPerRow_LastFrame_SingleRow_isRand;
  54. float4 ColorAnimationFunctions[(MAX_KEYFRAMES - 1) * 2];
  55. float4 TimeKeys;
  56. int ShaderType;
  57. float SpeedMultiplier;
  58. float2 ColorScaleRange;
  59. };
  60. // Common values for ParticleDraw.ShaderType. Based on fxpscommon.inc.
  61. static const int ShaderType_Additive = 1;
  62. static const int ShaderType_AdditiveAlphaTest = 2;
  63. static const int ShaderType_Alpha = 3;
  64. static const int ShaderType_AlphaTest = 4;
  65. static const int ShaderType_Multiply = 5;
  66. #if !EXPRESSION_EVALUATOR_ENABLED
  67. #define SETUP_ALPHA_BLEND_AND_TEST(ShaderType) \
  68. SrcBlend = (ShaderType == ShaderType_Additive || ShaderType == ShaderType_AdditiveAlphaTest || ShaderType == ShaderType_AlphaTest \
  69. ? D3DBLEND_ONE \
  70. : (ShaderType == ShaderType_Alpha \
  71. ? D3DBLEND_SRCALPHA \
  72. : /* ShaderType == ShaderType_Multiply */ D3DBLEND_ZERO)); \
  73. DestBlend = (ShaderType == ShaderType_Additive || ShaderType == ShaderType_AdditiveAlphaTest \
  74. ? D3DBLEND_ONE \
  75. : (ShaderType == ShaderType_Alpha \
  76. ? D3DBLEND_INVSRCALPHA \
  77. : (ShaderType == ShaderType_Multiply \
  78. ? D3DBLEND_INVSRCCOLOR \
  79. : /* ShaderType == ShaderType_AlphaTest */ D3DBLEND_ZERO))); \
  80. AlphaTestEnable = (ShaderType == ShaderType_AdditiveAlphaTest || ShaderType == ShaderType_AlphaTest); \
  81. AlphaBlendEnable = true; \
  82. AlphaFunc = GreaterEqual; \
  83. AlphaRef = 0x60
  84. #else
  85. #define SETUP_ALPHA_BLEND_AND_TEST(ShaderType) \
  86. AlphaBlendEnable = true; \
  87. AlphaFunc = GreaterEqual; \
  88. AlphaRef = 0x60
  89. #endif
  90. ParticleDraw Draw
  91. <
  92. string UIWidget = "None";
  93. string SasBindAddress = "Particle.Draw";
  94. >
  95. #if !defined(EA_PLATFORM_PS3) // PS3 TODO - Does not like this being initialized.
  96. = {
  97. float4(4.0f, 15.0f, -1.0f, 0.0f),
  98. float4(0.0f, 0.0f, 0.0f, 1.0f),
  99. float4(0.0f, 0.0f, 0.0f, 1.0f),
  100. float4(0.0f, 0.0f, 0.0f, 1.0f),
  101. float4(0.0f, 0.0f, 0.0f, 1.0f),
  102. float4(0.0f, 0.0f, 0.0f, 1.0f),
  103. float4(0.0f, 0.0f, 0.0f, 1.0f),
  104. float4(0.0f, 0.0f, 0.0f, 0.0f),
  105. ShaderType_Additive,
  106. 1.0f,
  107. float2(-0.1f, 0.2f)
  108. }
  109. #endif
  110. ;
  111. float4 Particle_ComputeColor(float relativeAge, float particleSeed, bool allowRandomize)
  112. {
  113. // compute color
  114. float4 color;
  115. if (relativeAge < Draw.TimeKeys.y)
  116. {
  117. color = Draw.ColorAnimationFunctions[0] * relativeAge + Draw.ColorAnimationFunctions[1];
  118. }
  119. else if (relativeAge < Draw.TimeKeys.z)
  120. {
  121. color = Draw.ColorAnimationFunctions[2] * relativeAge + Draw.ColorAnimationFunctions[3];
  122. }
  123. else
  124. {
  125. color = Draw.ColorAnimationFunctions[4] * relativeAge + Draw.ColorAnimationFunctions[5];
  126. }
  127. if (allowRandomize)
  128. {
  129. color *= GetRandomFloatValue(Draw.ColorScaleRange, particleSeed, 4);
  130. }
  131. return color;
  132. }
  133. float2 Particle_ComputeVideoTextureDefault(float frameNumber, float2 cornerTexCoord)
  134. {
  135. float numPerRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.x;
  136. float lastFrame = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.y;
  137. frameNumber = fmod(frameNumber, lastFrame);
  138. float2 uvOffset = float2(fmod(frameNumber, numPerRow), frameNumber / numPerRow);
  139. uvOffset -= frac(uvOffset); // Make this into an integer. More efficient here than performing the whole calculation with integers.
  140. return (uvOffset + cornerTexCoord) / numPerRow;
  141. }
  142. float2 Particle_ComputeVideoTextureSingleRow(float colNum, float2 cornerTexCoord,int rowNum)
  143. {
  144. float numPerRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.x;
  145. float2 uvOffset = float2(colNum, rowNum);
  146. uvOffset -= frac(uvOffset); // Make this into an integer. More efficient here than performing the whole calculation with integers.
  147. return (uvOffset + cornerTexCoord) / numPerRow;
  148. }
  149. void Particle_ComputeVideoTexture(float age, float seed, float2 vertexCorner, float2 texCoord, out float2 particleTexCoord,
  150. out float3 nextFrameTexCoord)
  151. {
  152. // Texture coordinate
  153. float currentTexFrame = age * Draw.SpeedMultiplier;
  154. float numFramesPerRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.x;
  155. float singleRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.z;
  156. float isRand = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.a;
  157. //Random Frame....Get a random number between 0 - 3 (for the row and column) and finally plays a random texture between 0-15
  158. int randNumRow = GetRandomFloatValue(float2(0, numFramesPerRow),seed, 2);
  159. int randNumCol = GetRandomFloatValue(float2(0, numFramesPerRow),seed, 7);
  160. numFramesPerRow--;
  161. float frameToPlay = min(currentTexFrame, numFramesPerRow);
  162. float nextFrameToPlay = min(currentTexFrame + 1.0, numFramesPerRow);
  163. // Plays a single row of the texture specified by 'SingleRow' and stops at the last frame until the particle dies.
  164. if(singleRow > 0 && !isRand)
  165. {
  166. particleTexCoord = Particle_ComputeVideoTextureSingleRow(frameToPlay, texCoord, singleRow - 1);
  167. nextFrameTexCoord.xy = Particle_ComputeVideoTextureSingleRow(nextFrameToPlay, texCoord, singleRow - 1);
  168. nextFrameTexCoord.z = frac(frameToPlay);
  169. }
  170. // Plays a random frame for the lifetime of the particle.
  171. else if(isRand)
  172. {
  173. particleTexCoord = Particle_ComputeVideoTextureSingleRow(randNumCol, texCoord, randNumRow);
  174. nextFrameTexCoord.xy = particleTexCoord.xy;
  175. nextFrameTexCoord.z = 0;
  176. }
  177. // Plays a random row and stops at the last frame until the particle dies.
  178. else if(singleRow == -1)
  179. {
  180. particleTexCoord = Particle_ComputeVideoTextureSingleRow(frameToPlay, texCoord, randNumRow);
  181. nextFrameTexCoord.xy = Particle_ComputeVideoTextureSingleRow(nextFrameToPlay, texCoord, randNumRow);
  182. nextFrameTexCoord.z = frac(frameToPlay);
  183. }
  184. // Plays the sequential animation of the texture.
  185. else
  186. {
  187. particleTexCoord = Particle_ComputeVideoTextureDefault(currentTexFrame, texCoord);
  188. nextFrameTexCoord.xy = Particle_ComputeVideoTextureDefault(currentTexFrame + 1.0, texCoord);
  189. nextFrameTexCoord.z = frac(currentTexFrame);
  190. }
  191. }
  192. void Particle_ComputeVideoTextureNoNextFrame(float age, float seed, float2 texCoord, out float2 particleTexCoord)
  193. {
  194. // Texture coordinate
  195. float currentTexFrame = age * Draw.SpeedMultiplier;
  196. float numFramesPerRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.x;
  197. float singleRow = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.z;
  198. float isRand = Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.a;
  199. //Random Frame....Get a random number between 0 - 3 (for the row and column) and finally plays a random texture between 0-15
  200. int randNumRow = GetRandomFloatValue(float2(0, numFramesPerRow),seed, 2);
  201. int randNumCol = GetRandomFloatValue(float2(0, numFramesPerRow),seed, 7);
  202. numFramesPerRow--;
  203. float frameToPlay = min(currentTexFrame, numFramesPerRow);
  204. // Plays a single row of the texture specified by 'SingleRow' and stops at the last frame until the particle dies.
  205. if(singleRow > 0 && !isRand)
  206. {
  207. particleTexCoord = Particle_ComputeVideoTextureSingleRow(frameToPlay, texCoord, singleRow - 1);
  208. }
  209. // Plays a random frame for the lifetime of the particle.
  210. else if(isRand)
  211. {
  212. particleTexCoord = Particle_ComputeVideoTextureSingleRow(randNumCol, texCoord, randNumRow);
  213. }
  214. // Plays a random row and stops at the last frame until the particle dies.
  215. else if(singleRow == -1)
  216. {
  217. particleTexCoord = Particle_ComputeVideoTextureSingleRow(frameToPlay, texCoord, randNumRow);
  218. }
  219. // Plays the sequential animation of the texture.
  220. else
  221. {
  222. particleTexCoord = Particle_ComputeVideoTextureDefault(currentTexFrame, texCoord);
  223. }
  224. }
  225. //
  226. // Physics data
  227. //
  228. struct ParticlePhysics
  229. {
  230. float3 Gravity;
  231. float3 DriftVelocity;
  232. float2 VelocityDampingRange; // Range with minimum in x and spread (= max - min) in y
  233. };
  234. ParticlePhysics Physics
  235. <
  236. string UIWidget = "None";
  237. string SasBindAddress = "Particle.Physics";
  238. >
  239. #if !defined(EA_PLATFORM_PS3) // PS3 TODO - Does not like this being initialized.
  240. =
  241. {
  242. float3(0.0f, 0.0f, 0.0f),
  243. float3(0.0f, 0.0f, 0.0f),
  244. float2(1.0f, 0.0f)
  245. }
  246. #endif // #if !defined(EA_PLATFORM_PS3) // PS3 TODO - Does not like this being initialized.
  247. ;
  248. //
  249. // Update params -- other things that update every frame :)
  250. //
  251. struct ParticleUpdate
  252. {
  253. float3 Size_Rate_Damping__Min;
  254. float3 Size_Rate_Damping__Spread;
  255. float3 XYRotation_Rate_Damping__Min;
  256. float3 XYRotation_Rate_Damping__Spread;
  257. float3 ZRotation_Rate_Damping__Min;
  258. float3 ZRotation_Rate_Damping__Spread;
  259. };
  260. ParticleUpdate Update
  261. <
  262. string UIWidget = "None";
  263. string SasBindAddress = "Particle.Update";
  264. >
  265. #if !defined(EA_PLATFORM_PS3) // PS3 TODO - Does not like this being initialized.
  266. =
  267. {
  268. float3(0, 0, 1),
  269. float3(10, 0, 0),
  270. float3(0, 0, 1),
  271. float3(0, 0, 0),
  272. float3(0, 0, 1),
  273. float3(0, 0, 0)
  274. }
  275. #endif // #if !defined(EA_PLATFORM_PS3) // PS3 TODO - Does not like this being initialized.
  276. ;
  277. float CalculateDampingIntegral(float damping, float age)
  278. {
  279. // The following computation is derived from this:
  280. // In the iterative integration we do: v = v' * damp, with v: new velocity, v' old velocity
  281. // To get the fixed function solution based only starting values, we need to integrate from 0 to t over the integral((damp ^ u) du).
  282. // The solution to the integral is (damp ^ u) / ln(damp).
  283. // Entering the two borders (0 and t) into it leads to: (damp ^ t - 1) / ln(damp)
  284. // The integral is undefined at 1.0, but it's approaching a good value (= age). Be pragmatic.
  285. //if (abs(damping - 1.0) < 0.0001)
  286. if (damping == 1.0)
  287. damping = 1.0001;
  288. return (pow(damping, age) - 1) / log(damping);
  289. }
  290. void Particle_ComputePhysics(out float3 particlePosition, out float size, out float2x2 zRotationMatrix,
  291. float age, float3 startPosition, float3 startVelocity, float particleSeed)
  292. {
  293. // size animation
  294. float3 sizeRandoms = GetRandomFloatValues(Update.Size_Rate_Damping__Min,
  295. Update.Size_Rate_Damping__Spread, particleSeed, 3);
  296. size = sizeRandoms.x;
  297. float sizeRate = sizeRandoms.y;
  298. float sizeRateDamping = sizeRandoms.z;
  299. size += sizeRate * CalculateDampingIntegral(sizeRateDamping, age);
  300. // update particle position
  301. float velocityDamping = GetRandomFloatValue(Physics.VelocityDampingRange,
  302. particleSeed, 13);
  303. float integratedDampedVelocity = CalculateDampingIntegral(velocityDamping, age);
  304. particlePosition = startPosition
  305. + startVelocity * integratedDampedVelocity
  306. + (Physics.DriftVelocity + 0.5 * age * Physics.Gravity) * age;
  307. // apply little bit of xy-rotation
  308. float2 xyVec = particlePosition.xy - startPosition.xy;
  309. float3 xyRotRandoms = GetRandomFloatValues(Update.XYRotation_Rate_Damping__Min,
  310. Update.XYRotation_Rate_Damping__Spread, particleSeed, 9);
  311. float xyRotation = xyRotRandoms.x;
  312. float xyRotationRate = xyRotRandoms.y;
  313. float xyRotationDamping = xyRotRandoms.z;
  314. xyRotation += xyRotationRate * CalculateDampingIntegral(xyRotationDamping, age);
  315. float2x2 xyRotationMatrix = { 1.0f, 0.0f, 1.0f, 0.0f };
  316. xyRotationMatrix[0][0] = cos(xyRotation);
  317. xyRotationMatrix[0][1] = -sin(xyRotation);
  318. xyRotationMatrix[1][1] = xyRotationMatrix[0][0];
  319. xyRotationMatrix[1][0] = -xyRotationMatrix[0][1];
  320. particlePosition.xy = startPosition.xy + mul(xyVec, xyRotationMatrix);
  321. // apply z rotation
  322. float3 zRotRandoms = GetRandomFloatValues(Update.ZRotation_Rate_Damping__Min,
  323. Update.ZRotation_Rate_Damping__Spread, particleSeed, 2);
  324. float zRotation = zRotRandoms.x;
  325. float zRotationRate = zRotRandoms.y;
  326. float zRotationDamping = zRotRandoms.z;
  327. zRotation += zRotationRate * CalculateDampingIntegral(zRotationDamping, age);
  328. zRotationMatrix[0][0] = cos(zRotation);
  329. zRotationMatrix[0][1] = -sin(zRotation);
  330. zRotationMatrix[1][1] = zRotationMatrix[0][0];
  331. zRotationMatrix[1][0] = -zRotationMatrix[0][1];
  332. }
  333. void Particle_ComputePhysics_Simplified(out float3 particlePosition, out float size, out float2x2 zRotationMatrix,
  334. float age, float3 startPosition, float3 startVelocity, float particleSeed)
  335. {
  336. float4 sizePositionRandoms = GetRandomFloatValues(float4(Update.Size_Rate_Damping__Min, Physics.VelocityDampingRange.x),
  337. float4(Update.Size_Rate_Damping__Spread, Physics.VelocityDampingRange.y), particleSeed, 3);
  338. // size animation
  339. size = sizePositionRandoms.x;
  340. float sizeRate = sizePositionRandoms.y;
  341. float sizeRateDamping = sizePositionRandoms.z;
  342. size += sizeRate * CalculateDampingIntegral(sizeRateDamping, age);
  343. // update particle position
  344. float velocityDamping = sizePositionRandoms.w;
  345. float integratedDampedVelocity = CalculateDampingIntegral(velocityDamping, age);
  346. particlePosition = startPosition
  347. + startVelocity * integratedDampedVelocity
  348. + (Physics.DriftVelocity + 0.5 * age * Physics.Gravity) * age;
  349. // Find random values for xy and z rotation
  350. float4 rotationRandoms = GetRandomFloatValues(
  351. float4(Update.XYRotation_Rate_Damping__Min.xy, Update.ZRotation_Rate_Damping__Min.xy),
  352. float4(Update.XYRotation_Rate_Damping__Spread.xy, Update.ZRotation_Rate_Damping__Spread.xy),
  353. particleSeed, 9);
  354. // apply little bit of xy-rotation
  355. float2 xyVec = particlePosition.xy - startPosition.xy;
  356. float xyRotation = rotationRandoms.x;
  357. float xyRotationRate = rotationRandoms.y;
  358. xyRotation += xyRotationRate * age;
  359. float2x2 xyRotationMatrix = { 1.0f, 0.0f, 1.0f, 0.0f };
  360. xyRotationMatrix[0][0] = cos(xyRotation);
  361. xyRotationMatrix[0][1] = -sin(xyRotation);
  362. xyRotationMatrix[1][1] = xyRotationMatrix[0][0];
  363. xyRotationMatrix[1][0] = -xyRotationMatrix[0][1];
  364. particlePosition.xy = startPosition.xy + mul(xyVec, xyRotationMatrix);
  365. // apply z rotation
  366. float zRotation = rotationRandoms.z;
  367. float zRotationRate = rotationRandoms.w;
  368. zRotation += zRotationRate * age;
  369. zRotationMatrix[0][0] = cos(zRotation);
  370. zRotationMatrix[0][1] = -sin(zRotation);
  371. zRotationMatrix[1][1] = zRotationMatrix[0][0];
  372. zRotationMatrix[1][0] = -zRotationMatrix[0][1];
  373. }
  374. //
  375. // Vertex data
  376. //
  377. #endif // _COMMON_PARTICLE_FXH_