/****************************************************************************** * 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 /// /// Convenience method for duplicating a skeleton's current active skin so changes to it will not affect other skeleton instances. . 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 /// /// Gets a shallow copy of the skin. The cloned skin's attachments are shared with the original skin. 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; } /// 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. 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); } /// Adds skin items from another skin. For items that already exist, the previous values are replaced. public static void AddAttachments (this Skin skin, Skin otherSkin) { if (otherSkin == null) return; otherSkin.CopyTo(skin, true, false); } /// Gets an attachment from the skin for the specified slot index and name. 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); } /// 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. 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); } } }