/****************************************************************************** * Spine Runtimes License Agreement * Last updated July 28, 2023. Replaces all prior versions. * * Copyright (c) 2013-2023, 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. *****************************************************************************/ // Original Contribution by: Mitch Thompson using Spine.Unity.AttachmentTools; using System.Collections.Generic; using UnityEngine; namespace Spine.Unity.Examples { public class SpriteAttacher : MonoBehaviour { public const string DefaultPMAShader = "Spine/Skeleton"; public const string DefaultStraightAlphaShader = "Sprites/Default"; #region Inspector public bool attachOnStart = true; public bool overrideAnimation = true; public Sprite sprite; [SpineSlot] public string slot; #endregion #if UNITY_EDITOR void OnValidate () { ISkeletonComponent skeletonComponent = GetComponent(); SkeletonRenderer skeletonRenderer = skeletonComponent as SkeletonRenderer; bool applyPMA; if (skeletonRenderer != null) { applyPMA = skeletonRenderer.pmaVertexColors; } else { SkeletonGraphic skeletonGraphic = skeletonComponent as SkeletonGraphic; applyPMA = skeletonGraphic != null && skeletonGraphic.MeshGenerator.settings.pmaVertexColors; } if (applyPMA) { try { if (sprite == null) return; sprite.texture.GetPixel(0, 0); } catch (UnityException e) { Debug.LogFormat("Texture of {0} ({1}) is not read/write enabled. SpriteAttacher requires this in order to work with a SkeletonRenderer that renders premultiplied alpha. Please check the texture settings.", sprite.name, sprite.texture.name); UnityEditor.EditorGUIUtility.PingObject(sprite.texture); throw e; } } } #endif RegionAttachment attachment; Slot spineSlot; bool applyPMA; static Dictionary atlasPageCache; static AtlasPage GetPageFor (Texture texture, Shader shader) { if (atlasPageCache == null) atlasPageCache = new Dictionary(); AtlasPage atlasPage; atlasPageCache.TryGetValue(texture, out atlasPage); if (atlasPage == null) { Material newMaterial = new Material(shader); atlasPage = newMaterial.ToSpineAtlasPage(); atlasPageCache[texture] = atlasPage; } return atlasPage; } void Start () { // Initialize slot and attachment references. Initialize(false); if (attachOnStart) Attach(); } void AnimationOverrideSpriteAttach (ISkeletonAnimation animated) { if (overrideAnimation && isActiveAndEnabled) Attach(); } public void Initialize (bool overwrite = true) { if (overwrite || attachment == null) { // Get the applyPMA value. ISkeletonComponent skeletonComponent = GetComponent(); SkeletonRenderer skeletonRenderer = skeletonComponent as SkeletonRenderer; if (skeletonRenderer != null) this.applyPMA = skeletonRenderer.pmaVertexColors; else { SkeletonGraphic skeletonGraphic = skeletonComponent as SkeletonGraphic; if (skeletonGraphic != null) this.applyPMA = skeletonGraphic.MeshGenerator.settings.pmaVertexColors; } // Subscribe to UpdateComplete to override animation keys. if (overrideAnimation) { ISkeletonAnimation animatedSkeleton = skeletonComponent as ISkeletonAnimation; if (animatedSkeleton != null) { animatedSkeleton.UpdateComplete -= AnimationOverrideSpriteAttach; animatedSkeleton.UpdateComplete += AnimationOverrideSpriteAttach; } } spineSlot = spineSlot ?? skeletonComponent.Skeleton.FindSlot(slot); Shader attachmentShader = applyPMA ? Shader.Find(DefaultPMAShader) : Shader.Find(DefaultStraightAlphaShader); if (sprite == null) attachment = null; else attachment = applyPMA ? sprite.ToRegionAttachmentPMAClone(attachmentShader) : sprite.ToRegionAttachment(SpriteAttacher.GetPageFor(sprite.texture, attachmentShader)); } } void OnDestroy () { ISkeletonAnimation animatedSkeleton = GetComponent(); if (animatedSkeleton != null) animatedSkeleton.UpdateComplete -= AnimationOverrideSpriteAttach; } /// Update the slot's attachment to the Attachment generated from the sprite. public void Attach () { if (spineSlot != null) spineSlot.Attachment = attachment; } } public static class SpriteAttachmentExtensions { [System.Obsolete] public static RegionAttachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, string shaderName = SpriteAttacher.DefaultPMAShader, bool applyPMA = true, float rotation = 0f) { return skeleton.AttachUnitySprite(slotName, sprite, Shader.Find(shaderName), applyPMA, rotation: rotation); } [System.Obsolete] public static RegionAttachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName = "", string shaderName = SpriteAttacher.DefaultPMAShader, bool applyPMA = true, float rotation = 0f) { return skeletonData.AddUnitySprite(slotName, sprite, skinName, Shader.Find(shaderName), applyPMA, rotation: rotation); } [System.Obsolete] public static RegionAttachment AttachUnitySprite (this Skeleton skeleton, string slotName, Sprite sprite, Shader shader, bool applyPMA, float rotation = 0f) { RegionAttachment att = applyPMA ? sprite.ToRegionAttachmentPMAClone(shader, rotation: rotation) : sprite.ToRegionAttachment(new Material(shader), rotation: rotation); skeleton.FindSlot(slotName).Attachment = att; return att; } [System.Obsolete] public static RegionAttachment AddUnitySprite (this SkeletonData skeletonData, string slotName, Sprite sprite, string skinName, Shader shader, bool applyPMA, float rotation = 0f) { RegionAttachment att = applyPMA ? sprite.ToRegionAttachmentPMAClone(shader, rotation: rotation) : sprite.ToRegionAttachment(new Material(shader), rotation); int slotIndex = skeletonData.FindSlot(slotName).Index; Skin skin = skeletonData.DefaultSkin; if (skinName != "") skin = skeletonData.FindSkin(skinName); if (skin != null) skin.SetAttachment(slotIndex, att.Name, att); return att; } } }