SlotBlendModes.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. using System.Collections.Generic;
  30. using UnityEngine;
  31. using System;
  32. namespace Spine.Unity.Deprecated {
  33. /// <summary>
  34. /// Deprecated. The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. See the
  35. /// <see href="http://esotericsoftware.com/spine-unity-skeletondatamodifierassets#BlendModeMaterials">SkeletonDataModifierAssets BlendModeMaterials documentation page</see> and
  36. /// <see href="http://esotericsoftware.com/forum/Slot-blending-not-work-11281">this forum thread</see> for further information.
  37. /// This class will be removed in the spine-unity 3.9 runtime.
  38. /// </summary>
  39. [Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
  40. [DisallowMultipleComponent]
  41. public class SlotBlendModes : MonoBehaviour {
  42. #region Internal Material Dictionary
  43. public struct MaterialTexturePair {
  44. public Texture2D texture2D;
  45. public Material material;
  46. }
  47. internal class MaterialWithRefcount {
  48. public Material materialClone;
  49. public int refcount = 1;
  50. public MaterialWithRefcount(Material mat) {
  51. this.materialClone = mat;
  52. }
  53. }
  54. static Dictionary<MaterialTexturePair, MaterialWithRefcount> materialTable;
  55. internal static Dictionary<MaterialTexturePair, MaterialWithRefcount> MaterialTable {
  56. get {
  57. if (materialTable == null) materialTable = new Dictionary<MaterialTexturePair, MaterialWithRefcount>();
  58. return materialTable;
  59. }
  60. }
  61. internal struct SlotMaterialTextureTuple {
  62. public Slot slot;
  63. public Texture2D texture2D;
  64. public Material material;
  65. public SlotMaterialTextureTuple(Slot slot, Material material, Texture2D texture) {
  66. this.slot = slot;
  67. this.material = material;
  68. this.texture2D = texture;
  69. }
  70. }
  71. internal static Material GetOrAddMaterialFor(Material materialSource, Texture2D texture) {
  72. if (materialSource == null || texture == null) return null;
  73. var mt = SlotBlendModes.MaterialTable;
  74. MaterialWithRefcount matWithRefcount;
  75. var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
  76. if (!mt.TryGetValue(key, out matWithRefcount)) {
  77. matWithRefcount = new MaterialWithRefcount(new Material(materialSource));
  78. var m = matWithRefcount.materialClone;
  79. m.name = "(Clone)" + texture.name + "-" + materialSource.name;
  80. m.mainTexture = texture;
  81. mt[key] = matWithRefcount;
  82. }
  83. else {
  84. matWithRefcount.refcount++;
  85. }
  86. return matWithRefcount.materialClone;
  87. }
  88. internal static MaterialWithRefcount GetExistingMaterialFor(Material materialSource, Texture2D texture)
  89. {
  90. if (materialSource == null || texture == null) return null;
  91. var mt = SlotBlendModes.MaterialTable;
  92. MaterialWithRefcount matWithRefcount;
  93. var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
  94. if (!mt.TryGetValue(key, out matWithRefcount)) {
  95. return null;
  96. }
  97. return matWithRefcount;
  98. }
  99. internal static void RemoveMaterialFromTable(Material materialSource, Texture2D texture) {
  100. var mt = SlotBlendModes.MaterialTable;
  101. var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
  102. mt.Remove(key);
  103. }
  104. #endregion
  105. #region Inspector
  106. public Material multiplyMaterialSource;
  107. public Material screenMaterialSource;
  108. Texture2D texture;
  109. #endregion
  110. SlotMaterialTextureTuple[] slotsWithCustomMaterial = new SlotMaterialTextureTuple[0];
  111. public bool Applied { get; private set; }
  112. void Start() {
  113. if (!Applied) Apply();
  114. }
  115. void OnDestroy() {
  116. if (Applied) Remove();
  117. }
  118. public void Apply() {
  119. GetTexture();
  120. if (texture == null) return;
  121. var skeletonRenderer = GetComponent<SkeletonRenderer>();
  122. if (skeletonRenderer == null) return;
  123. var slotMaterials = skeletonRenderer.CustomSlotMaterials;
  124. int numSlotsWithCustomMaterial = 0;
  125. foreach (var s in skeletonRenderer.Skeleton.Slots) {
  126. switch (s.data.blendMode) {
  127. case BlendMode.Multiply:
  128. if (multiplyMaterialSource != null) {
  129. slotMaterials[s] = GetOrAddMaterialFor(multiplyMaterialSource, texture);
  130. ++numSlotsWithCustomMaterial;
  131. }
  132. break;
  133. case BlendMode.Screen:
  134. if (screenMaterialSource != null) {
  135. slotMaterials[s] = GetOrAddMaterialFor(screenMaterialSource, texture);
  136. ++numSlotsWithCustomMaterial;
  137. }
  138. break;
  139. }
  140. }
  141. slotsWithCustomMaterial = new SlotMaterialTextureTuple[numSlotsWithCustomMaterial];
  142. int storedSlotIndex = 0;
  143. foreach (var s in skeletonRenderer.Skeleton.Slots) {
  144. switch (s.data.blendMode) {
  145. case BlendMode.Multiply:
  146. if (multiplyMaterialSource != null) {
  147. slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, multiplyMaterialSource, texture);
  148. }
  149. break;
  150. case BlendMode.Screen:
  151. if (screenMaterialSource != null) {
  152. slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, screenMaterialSource, texture);
  153. }
  154. break;
  155. }
  156. }
  157. Applied = true;
  158. skeletonRenderer.LateUpdate();
  159. }
  160. public void Remove() {
  161. GetTexture();
  162. if (texture == null) return;
  163. var skeletonRenderer = GetComponent<SkeletonRenderer>();
  164. if (skeletonRenderer == null) return;
  165. var slotMaterials = skeletonRenderer.CustomSlotMaterials;
  166. foreach (var slotWithCustomMat in slotsWithCustomMaterial) {
  167. Slot s = slotWithCustomMat.slot;
  168. Material storedMaterialSource = slotWithCustomMat.material;
  169. Texture2D storedTexture = slotWithCustomMat.texture2D;
  170. var matWithRefcount = GetExistingMaterialFor(storedMaterialSource, storedTexture);
  171. if (--matWithRefcount.refcount == 0) {
  172. RemoveMaterialFromTable(storedMaterialSource, storedTexture);
  173. }
  174. // we don't want to remove slotMaterials[s] if it has been changed in the meantime.
  175. Material m;
  176. if (slotMaterials.TryGetValue(s, out m)) {
  177. var existingMat = matWithRefcount == null ? null : matWithRefcount.materialClone;
  178. if (Material.ReferenceEquals(m, existingMat)) {
  179. slotMaterials.Remove(s);
  180. }
  181. }
  182. }
  183. slotsWithCustomMaterial = null;
  184. Applied = false;
  185. if (skeletonRenderer.valid) skeletonRenderer.LateUpdate();
  186. }
  187. public void GetTexture() {
  188. if (texture == null) {
  189. var sr = GetComponent<SkeletonRenderer>(); if (sr == null) return;
  190. var sda = sr.skeletonDataAsset; if (sda == null) return;
  191. var aa = sda.atlasAssets[0]; if (aa == null) return;
  192. var am = aa.PrimaryMaterial; if (am == null) return;
  193. texture = am.mainTexture as Texture2D;
  194. }
  195. }
  196. }
  197. }