UIParticleUpdater.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. using System;
  2. using System.Collections.Generic;
  3. using Coffee.UIParticleExtensions;
  4. using UnityEngine;
  5. using UnityEngine.Profiling;
  6. namespace Coffee.UIExtensions
  7. {
  8. internal static class UIParticleUpdater
  9. {
  10. static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
  11. static MaterialPropertyBlock s_Mpb;
  12. static ParticleSystem.Particle[] s_Particles = new ParticleSystem.Particle[2048];
  13. private static int frameCount = 0;
  14. public static void Register(UIParticle particle)
  15. {
  16. if (!particle) return;
  17. s_ActiveParticles.Add(particle);
  18. }
  19. public static void Unregister(UIParticle particle)
  20. {
  21. if (!particle) return;
  22. s_ActiveParticles.Remove(particle);
  23. }
  24. #if UNITY_EDITOR
  25. [UnityEditor.InitializeOnLoadMethod]
  26. #endif
  27. [RuntimeInitializeOnLoadMethod]
  28. private static void InitializeOnLoad()
  29. {
  30. MeshHelper.Init();
  31. MeshPool.Init();
  32. CombineInstanceArrayPool.Init();
  33. Canvas.willRenderCanvases -= Refresh;
  34. Canvas.willRenderCanvases += Refresh;
  35. }
  36. private static void Refresh()
  37. {
  38. // Do not allow it to be called in the same frame.
  39. if (frameCount == Time.frameCount) return;
  40. frameCount = Time.frameCount;
  41. Profiler.BeginSample("[UIParticle] Refresh");
  42. for (var i = 0; i < s_ActiveParticles.Count; i++)
  43. {
  44. try
  45. {
  46. Refresh(s_ActiveParticles[i]);
  47. }
  48. catch (Exception e)
  49. {
  50. Debug.LogException(e);
  51. }
  52. }
  53. Profiler.EndSample();
  54. }
  55. private static void Refresh(UIParticle particle)
  56. {
  57. if (!particle || !particle.bakedMesh || !particle.canvas || !particle.canvasRenderer) return;
  58. Profiler.BeginSample("[UIParticle] Modify scale");
  59. ModifyScale(particle);
  60. Profiler.EndSample();
  61. Profiler.BeginSample("[UIParticle] Bake mesh");
  62. BakeMesh(particle);
  63. Profiler.EndSample();
  64. // if (QualitySettings.activeColorSpace == ColorSpace.Linear)
  65. // {
  66. // Profiler.BeginSample("[UIParticle] Modify color space to linear");
  67. // particle.bakedMesh.ModifyColorSpaceToLinear();
  68. // Profiler.EndSample();
  69. // }
  70. Profiler.BeginSample("[UIParticle] Set mesh to CanvasRenderer");
  71. particle.canvasRenderer.SetMesh(particle.bakedMesh);
  72. Profiler.EndSample();
  73. Profiler.BeginSample("[UIParticle] Update Animatable Material Properties");
  74. particle.UpdateMaterialProperties();
  75. Profiler.EndSample();
  76. }
  77. private static void ModifyScale(UIParticle particle)
  78. {
  79. if (!particle.ignoreCanvasScaler || !particle.canvas) return;
  80. // Ignore Canvas scaling.
  81. var s = particle.canvas.rootCanvas.transform.localScale;
  82. var modifiedScale = new Vector3(
  83. Mathf.Approximately(s.x, 0) ? 1 : 1 / s.x,
  84. Mathf.Approximately(s.y, 0) ? 1 : 1 / s.y,
  85. Mathf.Approximately(s.z, 0) ? 1 : 1 / s.z);
  86. // Scale is already modified.
  87. var transform = particle.transform;
  88. if (Mathf.Approximately((transform.localScale - modifiedScale).sqrMagnitude, 0)) return;
  89. transform.localScale = modifiedScale;
  90. }
  91. private static Matrix4x4 GetScaledMatrix(ParticleSystem particle)
  92. {
  93. var transform = particle.transform;
  94. var main = particle.main;
  95. var space = main.simulationSpace;
  96. if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace)
  97. space = ParticleSystemSimulationSpace.Local;
  98. switch (space)
  99. {
  100. case ParticleSystemSimulationSpace.Local:
  101. return Matrix4x4.Rotate(transform.rotation).inverse
  102. * Matrix4x4.Scale(transform.lossyScale).inverse;
  103. case ParticleSystemSimulationSpace.World:
  104. return transform.worldToLocalMatrix;
  105. case ParticleSystemSimulationSpace.Custom:
  106. // #78: Support custom simulation space.
  107. return transform.worldToLocalMatrix
  108. * Matrix4x4.Translate(main.customSimulationSpace.position);
  109. default:
  110. return Matrix4x4.identity;
  111. }
  112. }
  113. private static void BakeMesh(UIParticle particle)
  114. {
  115. // Clear mesh before bake.
  116. Profiler.BeginSample("[UIParticle] Bake Mesh > Clear mesh before bake");
  117. MeshHelper.Clear();
  118. particle.bakedMesh.Clear(false);
  119. Profiler.EndSample();
  120. // Get camera for baking mesh.
  121. var camera = BakingCamera.GetCamera(particle.canvas);
  122. var root = particle.transform;
  123. var rootMatrix = Matrix4x4.Rotate(root.rotation).inverse
  124. * Matrix4x4.Scale(root.lossyScale).inverse;
  125. var scale = particle.ignoreCanvasScaler
  126. ? Vector3.Scale(particle.canvas.rootCanvas.transform.localScale, particle.scale3D)
  127. : particle.scale3D;
  128. var scaleMatrix = Matrix4x4.Scale(scale);
  129. // Cache position
  130. var position = particle.transform.position;
  131. var diff = position - particle.cachedPosition;
  132. diff.x *= 1f - 1f / Mathf.Max(0.001f, scale.x);
  133. diff.y *= 1f - 1f / Mathf.Max(0.001f, scale.y);
  134. diff.z *= 1f - 1f / Mathf.Max(0.001f, scale.z);
  135. particle.cachedPosition = position;
  136. if (particle.activeMeshIndices.CountFast() == 0)
  137. diff = Vector3.zero;
  138. for (var i = 0; i < particle.particles.Count; i++)
  139. {
  140. Profiler.BeginSample("[UIParticle] Bake Mesh > Push index");
  141. MeshHelper.activeMeshIndices.Add(false);
  142. MeshHelper.activeMeshIndices.Add(false);
  143. Profiler.EndSample();
  144. // No particle to render.
  145. var currentPs = particle.particles[i];
  146. if (!currentPs || !currentPs.IsAlive() || currentPs.particleCount == 0) continue;
  147. var r = currentPs.GetComponent<ParticleSystemRenderer>();
  148. if (!r.sharedMaterial && !r.trailMaterial) continue;
  149. // Calc matrix.
  150. Profiler.BeginSample("[UIParticle] Bake Mesh > Calc matrix");
  151. var matrix = rootMatrix;
  152. if (currentPs.transform != root)
  153. {
  154. if (currentPs.main.simulationSpace == ParticleSystemSimulationSpace.Local)
  155. {
  156. var relativePos = root.InverseTransformPoint(currentPs.transform.position);
  157. matrix = Matrix4x4.Translate(relativePos) * matrix;
  158. }
  159. else
  160. {
  161. matrix = matrix * Matrix4x4.Translate(-root.position);
  162. }
  163. }
  164. else
  165. {
  166. matrix = GetScaledMatrix(currentPs);
  167. }
  168. matrix = scaleMatrix * matrix;
  169. Profiler.EndSample();
  170. // Extra world simulation.
  171. if (currentPs.main.simulationSpace == ParticleSystemSimulationSpace.World && 0 < diff.sqrMagnitude)
  172. {
  173. Profiler.BeginSample("[UIParticle] Bake Mesh > Extra world simulation");
  174. var count = currentPs.particleCount;
  175. if (s_Particles.Length < count)
  176. {
  177. var size = Mathf.NextPowerOfTwo(count);
  178. s_Particles = new ParticleSystem.Particle[size];
  179. }
  180. currentPs.GetParticles(s_Particles);
  181. for (var j = 0; j < count; j++)
  182. {
  183. var p = s_Particles[j];
  184. p.position += diff;
  185. s_Particles[j] = p;
  186. }
  187. currentPs.SetParticles(s_Particles, count);
  188. Profiler.EndSample();
  189. }
  190. #if UNITY_2018_3_OR_NEWER
  191. // #102: Do not bake particle system to mesh when the alpha is zero.
  192. if (Mathf.Approximately(particle.canvasRenderer.GetInheritedAlpha(), 0))
  193. continue;
  194. #endif
  195. // Bake main particles.
  196. if (CanBakeMesh(r))
  197. {
  198. Profiler.BeginSample("[UIParticle] Bake Mesh > Bake Main Particles");
  199. var hash = currentPs.GetMaterialHash(false);
  200. if (hash != 0)
  201. {
  202. var m = MeshHelper.GetTemporaryMesh();
  203. r.BakeMesh(m, camera, true);
  204. MeshHelper.Push(i * 2, hash, m, matrix);
  205. }
  206. Profiler.EndSample();
  207. }
  208. // Bake trails particles.
  209. if (currentPs.trails.enabled)
  210. {
  211. Profiler.BeginSample("[UIParticle] Bake Mesh > Bake Trails Particles");
  212. var hash = currentPs.GetMaterialHash(true);
  213. if (hash != 0)
  214. {
  215. matrix = currentPs.main.simulationSpace == ParticleSystemSimulationSpace.Local && currentPs.trails.worldSpace
  216. ? matrix * Matrix4x4.Translate(-currentPs.transform.position)
  217. : matrix;
  218. var m = MeshHelper.GetTemporaryMesh();
  219. try
  220. {
  221. r.BakeTrailsMesh(m, camera, true);
  222. MeshHelper.Push(i * 2 + 1, hash, m, matrix);
  223. }
  224. catch
  225. {
  226. MeshHelper.DiscardTemporaryMesh(m);
  227. }
  228. }
  229. Profiler.EndSample();
  230. }
  231. }
  232. // Set active indices.
  233. Profiler.BeginSample("[UIParticle] Bake Mesh > Set active indices");
  234. particle.activeMeshIndices = MeshHelper.activeMeshIndices;
  235. Profiler.EndSample();
  236. // Combine
  237. Profiler.BeginSample("[UIParticle] Bake Mesh > CombineMesh");
  238. MeshHelper.CombineMesh(particle.bakedMesh);
  239. MeshHelper.Clear();
  240. Profiler.EndSample();
  241. }
  242. private static bool CanBakeMesh(ParticleSystemRenderer renderer)
  243. {
  244. // #69: Editor crashes when mesh is set to null when `ParticleSystem.RenderMode = Mesh`
  245. if (renderer.renderMode == ParticleSystemRenderMode.Mesh && renderer.mesh == null) return false;
  246. // #61: When `ParticleSystem.RenderMode = None`, an error occurs
  247. if (renderer.renderMode == ParticleSystemRenderMode.None) return false;
  248. return true;
  249. }
  250. }
  251. }