BaseInstancedRenderer.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace Funly.SkyStudio
  5. {
  6. // Base abstract class for effeciently rendering GPU instanced meshes.
  7. public abstract class BaseSpriteInstancedRenderer : MonoBehaviour
  8. {
  9. // Absolute maximum of arrays, any higher and GPUs might not support it.
  10. public const int kArrayMaxSprites = 1000;
  11. // User configured maximum to stay below (must be less than kMaxSprites.
  12. public int maxSprites { get; protected set; }
  13. [Tooltip("Mesh used to render the instances onto. If empty, a quad will be used.")]
  14. public Mesh modelMesh;
  15. [Tooltip("Sky Studio sprite sheet animated shader material.")]
  16. public Material renderMaterial;
  17. protected Queue<BaseSpriteItemData> m_Available = new Queue<BaseSpriteItemData>();
  18. protected HashSet<BaseSpriteItemData> m_Active = new HashSet<BaseSpriteItemData>();
  19. MaterialPropertyBlock m_PropertyBlock;
  20. // Fields for the gpu instancing arguments.
  21. Matrix4x4[] m_ModelMatrices = new Matrix4x4[kArrayMaxSprites];
  22. float[] m_StartTimes = new float[kArrayMaxSprites];
  23. float[] m_EndTimes = new float[kArrayMaxSprites];
  24. // Sprite sheet info.
  25. protected SpriteSheetData m_SpriteSheetLayout = new SpriteSheetData();
  26. protected Texture m_SpriteTexture;
  27. protected Color m_TintColor = Color.white;
  28. protected Camera m_ViewerCamera { get; set; }
  29. protected Mesh m_DefaltModelMesh;
  30. void Start()
  31. {
  32. // Verify GPU instancing is supported.
  33. if (SystemInfo.supportsInstancing == false) {
  34. Debug.LogError("Can't render since GPU instancing isn't supported on this device");
  35. enabled = false;
  36. return;
  37. }
  38. m_ViewerCamera = Camera.main;
  39. }
  40. // Bounds so the instanced rendering isn't culled.
  41. protected abstract Bounds CalculateMeshBounds();
  42. // Allocate a new data item to track a sprite mesh rendering.
  43. protected abstract BaseSpriteItemData CreateSpriteItemData();
  44. // Hook to let client prepare and check if we should render before trying.
  45. protected abstract bool IsRenderingEnabled();
  46. // Ask the subclass how many new instances we should create this frame.
  47. protected abstract int GetNextSpawnCount();
  48. // Select where the next sprite will be rendered at.
  49. protected abstract void CalculateSpriteTRS(BaseSpriteItemData data, out Vector3 spritePosition, out Quaternion spriteRotation, out Vector3 spriteScale);
  50. // Configure a new sprite item data object properties, (could be new or recycled).
  51. protected abstract void ConfigureSpriteItemData(BaseSpriteItemData data);
  52. // Setup any per-instance data you need to pass.
  53. protected abstract void PrepareDataArraysForRendering(int instanceId, BaseSpriteItemData data);
  54. protected abstract void PopulatePropertyBlockForRendering(ref MaterialPropertyBlock propertyBlock);
  55. // Get or create a instance data field for a particle.
  56. BaseSpriteItemData DequeueNextSpriteItemData()
  57. {
  58. BaseSpriteItemData data = null;
  59. if (m_Available.Count == 0) {
  60. data = CreateSpriteItemData();
  61. } else {
  62. data = m_Available.Dequeue();
  63. }
  64. m_Active.Add(data);
  65. return data;
  66. }
  67. void ReturnSpriteItemData(BaseSpriteItemData splash)
  68. {
  69. splash.Reset();
  70. m_Active.Remove(splash);
  71. m_Available.Enqueue(splash);
  72. }
  73. protected virtual void LateUpdate()
  74. {
  75. m_ViewerCamera = Camera.main;
  76. if (IsRenderingEnabled() == false) {
  77. return;
  78. }
  79. GenerateNewSprites();
  80. AdvanceAllSprites();
  81. RenderAllSprites();
  82. }
  83. void GenerateNewSprites()
  84. {
  85. int spawnCount = GetNextSpawnCount();
  86. Vector3 spritePosition;
  87. Vector3 spriteScale;
  88. Quaternion spriteRotation;
  89. for (int i = 0; i < spawnCount; i++) {
  90. // Dequeue a sprite item data, and configure it.
  91. BaseSpriteItemData data = DequeueNextSpriteItemData();
  92. data.spriteSheetData = m_SpriteSheetLayout;
  93. ConfigureSpriteItemData(data);
  94. CalculateSpriteTRS(data, out spritePosition, out spriteRotation, out spriteScale);
  95. data.SetTRSMatrix(spritePosition, spriteRotation, spriteScale);
  96. data.Start();
  97. }
  98. }
  99. void AdvanceAllSprites()
  100. {
  101. // FIXME - Lets not make a copy, just make returning safe.
  102. HashSet<BaseSpriteItemData> dataCopy = new HashSet<BaseSpriteItemData>(m_Active);
  103. foreach (BaseSpriteItemData data in dataCopy) {
  104. data.Continue();
  105. if (data.state == BaseSpriteItemData.SpriteState.Complete) {
  106. ReturnSpriteItemData(data);
  107. }
  108. }
  109. }
  110. void RenderAllSprites()
  111. {
  112. if (m_Active.Count == 0) {
  113. return;
  114. }
  115. if (renderMaterial == null) {
  116. Debug.LogError("Can't render sprite without a material.");
  117. return;
  118. }
  119. if (m_PropertyBlock == null) {
  120. m_PropertyBlock = new MaterialPropertyBlock();
  121. }
  122. int renderCount = 0;
  123. foreach (BaseSpriteItemData data in m_Active) {
  124. if (renderCount >= kArrayMaxSprites) {
  125. Debug.LogError("Can't render any more sprites...");
  126. break;
  127. }
  128. if (data.state != BaseSpriteItemData.SpriteState.Animating) {
  129. continue;
  130. }
  131. if (data.startTime > Time.time) {
  132. continue;
  133. }
  134. m_ModelMatrices[renderCount] = data.modelMatrix;
  135. m_StartTimes[renderCount] = data.startTime;
  136. m_EndTimes[renderCount] = data.endTime;
  137. PrepareDataArraysForRendering(renderCount, data);
  138. renderCount++;
  139. }
  140. if (renderCount == 0) {
  141. return;
  142. }
  143. // Sprite sheet timing.
  144. m_PropertyBlock.SetFloatArray("_StartTime", m_StartTimes);
  145. m_PropertyBlock.SetFloatArray("_EndTime", m_EndTimes);
  146. // Sprite sheet layout.
  147. //m_PropertyBlock.SetTexture("_SpriteSheetTex", m_SpriteTexture);
  148. renderMaterial.SetTexture("_SpriteSheetTex", m_SpriteTexture);
  149. m_PropertyBlock.SetFloat("_SpriteColumnCount", m_SpriteSheetLayout.columns);
  150. m_PropertyBlock.SetFloat("_SpriteRowCount", m_SpriteSheetLayout.rows);
  151. m_PropertyBlock.SetFloat("_SpriteItemCount", m_SpriteSheetLayout.frameCount);
  152. m_PropertyBlock.SetFloat("_AnimationSpeed", m_SpriteSheetLayout.frameRate);
  153. m_PropertyBlock.SetVector("_TintColor", m_TintColor);
  154. PopulatePropertyBlockForRendering(ref m_PropertyBlock);
  155. Mesh mesh = GetMesh();
  156. mesh.bounds = CalculateMeshBounds();
  157. Graphics.DrawMeshInstanced(
  158. mesh, 0, renderMaterial, m_ModelMatrices, renderCount, m_PropertyBlock,
  159. UnityEngine.Rendering.ShadowCastingMode.Off, false, LayerMask.NameToLayer("TransparentFX"));
  160. }
  161. protected Mesh GetMesh() {
  162. if (modelMesh) {
  163. return modelMesh;
  164. }
  165. if (m_DefaltModelMesh) {
  166. return m_DefaltModelMesh;
  167. }
  168. m_DefaltModelMesh = GenerateMesh();
  169. return m_DefaltModelMesh;
  170. }
  171. protected virtual Mesh GenerateMesh()
  172. {
  173. Mesh m = new Mesh();
  174. Vector3[] verts = new Vector3[4];
  175. verts[0] = new Vector3(-1, -1, 0);
  176. verts[1] = new Vector3(-1, 1, 0);
  177. verts[2] = new Vector3(1, 1, 0);
  178. verts[3] = new Vector3(1, -1, 0);
  179. Vector2[] uvs = new Vector2[4];
  180. uvs[0] = new Vector2(0, 0);
  181. uvs[1] = new Vector2(0, 1);
  182. uvs[2] = new Vector2(1, 1);
  183. uvs[3] = new Vector2(1, 0);
  184. int[] triangles = new int[6];
  185. triangles[0] = 0;
  186. triangles[1] = 1;
  187. triangles[2] = 2;
  188. triangles[3] = 0;
  189. triangles[4] = 2;
  190. triangles[5] = 3;
  191. m.vertices = verts;
  192. m.uv = uvs;
  193. m.triangles = triangles;
  194. m.bounds = new Bounds(Vector3.zero, new Vector3(500.0f, 500.0f, 500.0f));
  195. return m;
  196. }
  197. }
  198. }