/****************************************************************************** * 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 UnityEngine; using System.Collections.Generic; using System.Collections; namespace Spine.Unity.AttachmentTools { public static class SkinUtilities { #region Skeleton Skin Extensions /// <summary> /// Convenience method for duplicating a skeleton's current active skin so changes to it will not affect other skeleton instances. .</summary> public static Skin UnshareSkin (this Skeleton skeleton, bool includeDefaultSkin, bool unshareAttachments, AnimationState state = null) { // 1. Copy the current skin and set the skeleton's skin to the new one. var newSkin = skeleton.GetClonedSkin("cloned skin", includeDefaultSkin, unshareAttachments, true); skeleton.SetSkin(newSkin); // 2. Apply correct attachments: skeleton.SetToSetupPose + animationState.Apply if (state != null) { skeleton.SetToSetupPose(); state.Apply(skeleton); } // 3. Return unshared skin. return newSkin; } public static Skin GetClonedSkin (this Skeleton skeleton, string newSkinName, bool includeDefaultSkin = false, bool cloneAttachments = false, bool cloneMeshesAsLinked = true) { var newSkin = new Skin(newSkinName); // may have null name. Harmless. var defaultSkin = skeleton.data.DefaultSkin; var activeSkin = skeleton.skin; if (includeDefaultSkin) defaultSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked); if (activeSkin != null) activeSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked); return newSkin; } #endregion /// <summary> /// Gets a shallow copy of the skin. The cloned skin's attachments are shared with the original skin.</summary> public static Skin GetClone (this Skin original) { var newSkin = new Skin(original.name + " clone"); var newSkinAttachments = newSkin.Attachments; var newSkinBones = newSkin.Bones; var newSkinConstraints = newSkin.Constraints; foreach (var a in original.Attachments) newSkinAttachments[a.Key] = a.Value; newSkinBones.AddRange(original.bones); newSkinConstraints.AddRange(original.constraints); return newSkin; } /// <summary>Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced.</summary> public static void SetAttachment (this Skin skin, string slotName, string keyName, Attachment attachment, Skeleton skeleton) { int slotIndex = skeleton.FindSlotIndex(slotName); if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null."); if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); skin.SetAttachment(slotIndex, keyName, attachment); } /// <summary>Adds skin items from another skin. For items that already exist, the previous values are replaced.</summary> public static void AddAttachments (this Skin skin, Skin otherSkin) { if (otherSkin == null) return; otherSkin.CopyTo(skin, true, false); } /// <summary>Gets an attachment from the skin for the specified slot index and name.</summary> public static Attachment GetAttachment (this Skin skin, string slotName, string keyName, Skeleton skeleton) { int slotIndex = skeleton.FindSlotIndex(slotName); if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null."); if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); return skin.GetAttachment(slotIndex, keyName); } /// <summary>Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced.</summary> public static void SetAttachment (this Skin skin, int slotIndex, string keyName, Attachment attachment) { skin.SetAttachment(slotIndex, keyName, attachment); } public static void RemoveAttachment (this Skin skin, string slotName, string keyName, SkeletonData skeletonData) { int slotIndex = skeletonData.FindSlotIndex(slotName); if (skeletonData == null) throw new System.ArgumentNullException("skeletonData", "skeletonData cannot be null."); if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); skin.RemoveAttachment(slotIndex, keyName); } public static void Clear (this Skin skin) { skin.Attachments.Clear(); } //[System.Obsolete] public static void Append (this Skin destination, Skin source) { source.CopyTo(destination, true, false); } public static void CopyTo (this Skin source, Skin destination, bool overwrite, bool cloneAttachments, bool cloneMeshesAsLinked = true) { var sourceAttachments = source.Attachments; var destinationAttachments = destination.Attachments; var destinationBones = destination.Bones; var destinationConstraints = destination.Constraints; if (cloneAttachments) { if (overwrite) { foreach (var e in sourceAttachments) { Attachment clonedAttachment = e.Value.GetCopy(cloneMeshesAsLinked); destinationAttachments[new Skin.SkinEntry(e.Key.SlotIndex, e.Key.Name, clonedAttachment)] = clonedAttachment; } } else { foreach (var e in sourceAttachments) { if (destinationAttachments.ContainsKey(e.Key)) continue; Attachment clonedAttachment = e.Value.GetCopy(cloneMeshesAsLinked); destinationAttachments.Add(new Skin.SkinEntry(e.Key.SlotIndex, e.Key.Name, clonedAttachment), clonedAttachment); } } } else { if (overwrite) { foreach (var e in sourceAttachments) destinationAttachments[e.Key] = e.Value; } else { foreach (var e in sourceAttachments) { if (destinationAttachments.ContainsKey(e.Key)) continue; destinationAttachments.Add(e.Key, e.Value); } } } foreach (BoneData data in source.bones) if (!destinationBones.Contains(data)) destinationBones.Add(data); foreach (ConstraintData data in source.constraints) if (!destinationConstraints.Contains(data)) destinationConstraints.Add(data); } } }