SkeletonRenderSeparator.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. #if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
  30. #define NEW_PREFAB_SYSTEM
  31. #endif
  32. #define SPINE_OPTIONAL_RENDEROVERRIDE
  33. using UnityEngine;
  34. using System.Collections.Generic;
  35. namespace Spine.Unity {
  36. #if NEW_PREFAB_SYSTEM
  37. [ExecuteAlways]
  38. #else
  39. [ExecuteInEditMode]
  40. #endif
  41. [HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
  42. public class SkeletonRenderSeparator : MonoBehaviour {
  43. public const int DefaultSortingOrderIncrement = 5;
  44. #region Inspector
  45. [SerializeField]
  46. protected SkeletonRenderer skeletonRenderer;
  47. public SkeletonRenderer SkeletonRenderer {
  48. get { return skeletonRenderer; }
  49. set {
  50. #if SPINE_OPTIONAL_RENDEROVERRIDE
  51. if (skeletonRenderer != null)
  52. skeletonRenderer.GenerateMeshOverride -= HandleRender;
  53. #endif
  54. skeletonRenderer = value;
  55. if (value == null)
  56. this.enabled = false;
  57. }
  58. }
  59. MeshRenderer mainMeshRenderer;
  60. public bool copyPropertyBlock = true;
  61. [Tooltip("Copies MeshRenderer flags into each parts renderer")]
  62. public bool copyMeshRendererFlags = true;
  63. public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
  64. #if UNITY_EDITOR
  65. void Reset () {
  66. if (skeletonRenderer == null)
  67. skeletonRenderer = GetComponent<SkeletonRenderer>();
  68. }
  69. #endif
  70. #endregion
  71. #region Callback Delegates
  72. /// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
  73. /// all materials have been updated.</summary>
  74. public event SkeletonRenderer.SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
  75. #endregion
  76. #region Runtime Instantiation
  77. /// <summary>Adds a SkeletonRenderSeparator and child SkeletonPartsRenderer GameObjects to a given SkeletonRenderer.</summary>
  78. /// <returns>The to skeleton renderer.</returns>
  79. /// <param name="skeletonRenderer">The target SkeletonRenderer or SkeletonAnimation.</param>
  80. /// <param name="sortingLayerID">Sorting layer to be used for the parts renderers.</param>
  81. /// <param name="extraPartsRenderers">Number of additional SkeletonPartsRenderers on top of the ones determined by counting the number of separator slots.</param>
  82. /// <param name="sortingOrderIncrement">The integer to increment the sorting order per SkeletonPartsRenderer to separate them.</param>
  83. /// <param name="baseSortingOrder">The sorting order value of the first SkeletonPartsRenderer.</param>
  84. /// <param name="addMinimumPartsRenderers">If set to <c>true</c>, a minimum number of SkeletonPartsRenderer GameObjects (determined by separatorSlots.Count + 1) will be added.</param>
  85. public static SkeletonRenderSeparator AddToSkeletonRenderer (SkeletonRenderer skeletonRenderer, int sortingLayerID = 0, int extraPartsRenderers = 0, int sortingOrderIncrement = DefaultSortingOrderIncrement, int baseSortingOrder = 0, bool addMinimumPartsRenderers = true) {
  86. if (skeletonRenderer == null) {
  87. Debug.Log("Tried to add SkeletonRenderSeparator to a null SkeletonRenderer reference.");
  88. return null;
  89. }
  90. var srs = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
  91. srs.skeletonRenderer = skeletonRenderer;
  92. skeletonRenderer.Initialize(false);
  93. int count = extraPartsRenderers;
  94. if (addMinimumPartsRenderers)
  95. count = extraPartsRenderers + skeletonRenderer.separatorSlots.Count + 1;
  96. var skeletonRendererTransform = skeletonRenderer.transform;
  97. var componentRenderers = srs.partsRenderers;
  98. for (int i = 0; i < count; i++) {
  99. var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRendererTransform, i.ToString());
  100. var mr = spr.MeshRenderer;
  101. mr.sortingLayerID = sortingLayerID;
  102. mr.sortingOrder = baseSortingOrder + (i * sortingOrderIncrement);
  103. componentRenderers.Add(spr);
  104. }
  105. srs.OnEnable();
  106. #if UNITY_EDITOR
  107. // Make sure editor updates properly in edit mode.
  108. if (!Application.isPlaying) {
  109. skeletonRenderer.enabled = false;
  110. skeletonRenderer.enabled = true;
  111. skeletonRenderer.LateUpdate();
  112. }
  113. #endif
  114. return srs;
  115. }
  116. /// <summary>Add a child SkeletonPartsRenderer GameObject to this SkeletonRenderSeparator.</summary>
  117. public SkeletonPartsRenderer AddPartsRenderer (int sortingOrderIncrement = DefaultSortingOrderIncrement, string name = null) {
  118. int sortingLayerID = 0;
  119. int sortingOrder = 0;
  120. if (partsRenderers.Count > 0) {
  121. var previous = partsRenderers[partsRenderers.Count - 1];
  122. var previousMeshRenderer = previous.MeshRenderer;
  123. sortingLayerID = previousMeshRenderer.sortingLayerID;
  124. sortingOrder = previousMeshRenderer.sortingOrder + sortingOrderIncrement;
  125. }
  126. if (string.IsNullOrEmpty(name))
  127. name = partsRenderers.Count.ToString();
  128. var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRenderer.transform, name);
  129. partsRenderers.Add(spr);
  130. var mr = spr.MeshRenderer;
  131. mr.sortingLayerID = sortingLayerID;
  132. mr.sortingOrder = sortingOrder;
  133. return spr;
  134. }
  135. #endregion
  136. public void OnEnable () {
  137. if (skeletonRenderer == null) return;
  138. if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock();
  139. mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
  140. #if SPINE_OPTIONAL_RENDEROVERRIDE
  141. skeletonRenderer.GenerateMeshOverride -= HandleRender;
  142. skeletonRenderer.GenerateMeshOverride += HandleRender;
  143. #endif
  144. if (copyMeshRendererFlags) {
  145. var lightProbeUsage = mainMeshRenderer.lightProbeUsage;
  146. bool receiveShadows = mainMeshRenderer.receiveShadows;
  147. var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage;
  148. var shadowCastingMode = mainMeshRenderer.shadowCastingMode;
  149. var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode;
  150. var probeAnchor = mainMeshRenderer.probeAnchor;
  151. for (int i = 0; i < partsRenderers.Count; i++) {
  152. var currentRenderer = partsRenderers[i];
  153. if (currentRenderer == null) continue; // skip null items.
  154. var mr = currentRenderer.MeshRenderer;
  155. mr.lightProbeUsage = lightProbeUsage;
  156. mr.receiveShadows = receiveShadows;
  157. mr.reflectionProbeUsage = reflectionProbeUsage;
  158. mr.shadowCastingMode = shadowCastingMode;
  159. mr.motionVectorGenerationMode = motionVectorGenerationMode;
  160. mr.probeAnchor = probeAnchor;
  161. }
  162. }
  163. }
  164. public void OnDisable () {
  165. if (skeletonRenderer == null) return;
  166. #if SPINE_OPTIONAL_RENDEROVERRIDE
  167. skeletonRenderer.GenerateMeshOverride -= HandleRender;
  168. #endif
  169. skeletonRenderer.LateUpdate();
  170. foreach (var partsRenderer in partsRenderers) {
  171. if (partsRenderer != null)
  172. partsRenderer.ClearMesh();
  173. }
  174. }
  175. MaterialPropertyBlock copiedBlock;
  176. void HandleRender (SkeletonRendererInstruction instruction) {
  177. int rendererCount = partsRenderers.Count;
  178. if (rendererCount <= 0) return;
  179. if (copyPropertyBlock)
  180. mainMeshRenderer.GetPropertyBlock(copiedBlock);
  181. var settings = new MeshGenerator.Settings {
  182. addNormals = skeletonRenderer.addNormals,
  183. calculateTangents = skeletonRenderer.calculateTangents,
  184. immutableTriangles = false, // parts cannot do immutable triangles.
  185. pmaVertexColors = skeletonRenderer.pmaVertexColors,
  186. tintBlack = skeletonRenderer.tintBlack,
  187. useClipping = true,
  188. zSpacing = skeletonRenderer.zSpacing
  189. };
  190. var submeshInstructions = instruction.submeshInstructions;
  191. var submeshInstructionsItems = submeshInstructions.Items;
  192. int lastSubmeshInstruction = submeshInstructions.Count - 1;
  193. int rendererIndex = 0;
  194. var currentRenderer = partsRenderers[rendererIndex];
  195. for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
  196. if (currentRenderer == null)
  197. continue;
  198. if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
  199. // Apply properties
  200. var meshGenerator = currentRenderer.MeshGenerator;
  201. meshGenerator.settings = settings;
  202. if (copyPropertyBlock)
  203. currentRenderer.SetPropertyBlock(copiedBlock);
  204. // Render
  205. currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
  206. start = si + 1;
  207. rendererIndex++;
  208. if (rendererIndex < rendererCount) {
  209. currentRenderer = partsRenderers[rendererIndex];
  210. } else {
  211. // Not enough renderers. Skip the rest of the instructions.
  212. break;
  213. }
  214. }
  215. }
  216. if (OnMeshAndMaterialsUpdated != null)
  217. OnMeshAndMaterialsUpdated(this.skeletonRenderer);
  218. // Clear extra renderers if they exist.
  219. for (; rendererIndex < rendererCount; rendererIndex++) {
  220. currentRenderer = partsRenderers[rendererIndex];
  221. if (currentRenderer != null)
  222. partsRenderers[rendererIndex].ClearMesh();
  223. }
  224. }
  225. }
  226. }