/****************************************************************************** * Spine Runtimes License Agreement * Last updated January 1, 2020. Replaces all prior versions. * * Copyright (c) 2013-2020, Esoteric Software LLC * * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: * http://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software * or otherwise create derivative works of the Spine Runtimes (collectively, * "Products"), provided that each user of the Products must obtain their own * Spine Editor license and redistribution of the Products in any form must * include this license and copyright notice. * * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ using System.Collections.Generic; using UnityEngine; using System; namespace Spine.Unity.Deprecated { /// /// Deprecated. The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. See the /// SkeletonDataModifierAssets BlendModeMaterials documentation page and /// this forum thread for further information. /// This class will be removed in the spine-unity 3.9 runtime. /// [Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)] [DisallowMultipleComponent] public class SlotBlendModes : MonoBehaviour { #region Internal Material Dictionary public struct MaterialTexturePair { public Texture2D texture2D; public Material material; } internal class MaterialWithRefcount { public Material materialClone; public int refcount = 1; public MaterialWithRefcount(Material mat) { this.materialClone = mat; } } static Dictionary materialTable; internal static Dictionary MaterialTable { get { if (materialTable == null) materialTable = new Dictionary(); return materialTable; } } internal struct SlotMaterialTextureTuple { public Slot slot; public Texture2D texture2D; public Material material; public SlotMaterialTextureTuple(Slot slot, Material material, Texture2D texture) { this.slot = slot; this.material = material; this.texture2D = texture; } } internal static Material GetOrAddMaterialFor(Material materialSource, Texture2D texture) { if (materialSource == null || texture == null) return null; var mt = SlotBlendModes.MaterialTable; MaterialWithRefcount matWithRefcount; var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; if (!mt.TryGetValue(key, out matWithRefcount)) { matWithRefcount = new MaterialWithRefcount(new Material(materialSource)); var m = matWithRefcount.materialClone; m.name = "(Clone)" + texture.name + "-" + materialSource.name; m.mainTexture = texture; mt[key] = matWithRefcount; } else { matWithRefcount.refcount++; } return matWithRefcount.materialClone; } internal static MaterialWithRefcount GetExistingMaterialFor(Material materialSource, Texture2D texture) { if (materialSource == null || texture == null) return null; var mt = SlotBlendModes.MaterialTable; MaterialWithRefcount matWithRefcount; var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; if (!mt.TryGetValue(key, out matWithRefcount)) { return null; } return matWithRefcount; } internal static void RemoveMaterialFromTable(Material materialSource, Texture2D texture) { var mt = SlotBlendModes.MaterialTable; var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; mt.Remove(key); } #endregion #region Inspector public Material multiplyMaterialSource; public Material screenMaterialSource; Texture2D texture; #endregion SlotMaterialTextureTuple[] slotsWithCustomMaterial = new SlotMaterialTextureTuple[0]; public bool Applied { get; private set; } void Start() { if (!Applied) Apply(); } void OnDestroy() { if (Applied) Remove(); } public void Apply() { GetTexture(); if (texture == null) return; var skeletonRenderer = GetComponent(); if (skeletonRenderer == null) return; var slotMaterials = skeletonRenderer.CustomSlotMaterials; int numSlotsWithCustomMaterial = 0; foreach (var s in skeletonRenderer.Skeleton.Slots) { switch (s.data.blendMode) { case BlendMode.Multiply: if (multiplyMaterialSource != null) { slotMaterials[s] = GetOrAddMaterialFor(multiplyMaterialSource, texture); ++numSlotsWithCustomMaterial; } break; case BlendMode.Screen: if (screenMaterialSource != null) { slotMaterials[s] = GetOrAddMaterialFor(screenMaterialSource, texture); ++numSlotsWithCustomMaterial; } break; } } slotsWithCustomMaterial = new SlotMaterialTextureTuple[numSlotsWithCustomMaterial]; int storedSlotIndex = 0; foreach (var s in skeletonRenderer.Skeleton.Slots) { switch (s.data.blendMode) { case BlendMode.Multiply: if (multiplyMaterialSource != null) { slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, multiplyMaterialSource, texture); } break; case BlendMode.Screen: if (screenMaterialSource != null) { slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, screenMaterialSource, texture); } break; } } Applied = true; skeletonRenderer.LateUpdate(); } public void Remove() { GetTexture(); if (texture == null) return; var skeletonRenderer = GetComponent(); if (skeletonRenderer == null) return; var slotMaterials = skeletonRenderer.CustomSlotMaterials; foreach (var slotWithCustomMat in slotsWithCustomMaterial) { Slot s = slotWithCustomMat.slot; Material storedMaterialSource = slotWithCustomMat.material; Texture2D storedTexture = slotWithCustomMat.texture2D; var matWithRefcount = GetExistingMaterialFor(storedMaterialSource, storedTexture); if (--matWithRefcount.refcount == 0) { RemoveMaterialFromTable(storedMaterialSource, storedTexture); } // we don't want to remove slotMaterials[s] if it has been changed in the meantime. Material m; if (slotMaterials.TryGetValue(s, out m)) { var existingMat = matWithRefcount == null ? null : matWithRefcount.materialClone; if (Material.ReferenceEquals(m, existingMat)) { slotMaterials.Remove(s); } } } slotsWithCustomMaterial = null; Applied = false; if (skeletonRenderer.valid) skeletonRenderer.LateUpdate(); } public void GetTexture() { if (texture == null) { var sr = GetComponent(); if (sr == null) return; var sda = sr.skeletonDataAsset; if (sda == null) return; var aa = sda.atlasAssets[0]; if (aa == null) return; var am = aa.PrimaryMaterial; if (am == null) return; texture = am.mainTexture as Texture2D; } } } }