diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b4f057b3..6232a830a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -183,6 +183,7 @@
* Moved `Modules/AnimationMatchModifier` directory to `Spine Examples/Scripts/MecanimAnimationMatchModifier`.
* Moved `SkeletonRagdoll` and `SkeletonRagdoll2D` components from `Modules/Ragdoll` directory to `Spine Examples/Scripts/Sample Components/SkeletonUtility Modules`.
* Moved `AttachmentTools.cs` to `Utility` directory.
+ * Split the file `AttachmentTools` into 4 new files for each contained class. No namespace or other API changes performed.
* Moved `SkeletonExtensions.cs` to `Utility` directory.
* Moved `Modules/YieldInstructions` directory to `Utility/YieldInstructions`.
* Moved corresponding editor scripts of the above components to restructured directories as well.
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs
similarity index 57%
rename from spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs
rename to spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs
index cb046318e..31f96179e 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs
@@ -32,179 +32,7 @@ using System.Collections.Generic;
using System.Collections;
namespace Spine.Unity.AttachmentTools {
- public static class AttachmentRegionExtensions {
- #region GetRegion
- ///
- /// Tries to get the region (image) of a renderable attachment. If the attachment is not renderable, it returns null.
- public static AtlasRegion GetRegion (this Attachment attachment) {
- var renderableAttachment = attachment as IHasRendererObject;
- if (renderableAttachment != null)
- return renderableAttachment.RendererObject as AtlasRegion;
-
- return null;
- }
-
- /// Gets the region (image) of a RegionAttachment
- public static AtlasRegion GetRegion (this RegionAttachment regionAttachment) {
- return regionAttachment.RendererObject as AtlasRegion;
- }
-
- /// Gets the region (image) of a MeshAttachment
- public static AtlasRegion GetRegion (this MeshAttachment meshAttachment) {
- return meshAttachment.RendererObject as AtlasRegion;
- }
- #endregion
- #region SetRegion
- ///
- /// Tries to set the region (image) of a renderable attachment. If the attachment is not renderable, nothing is applied.
- public static void SetRegion (this Attachment attachment, AtlasRegion region, bool updateOffset = true) {
- var regionAttachment = attachment as RegionAttachment;
- if (regionAttachment != null)
- regionAttachment.SetRegion(region, updateOffset);
-
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null)
- meshAttachment.SetRegion(region, updateOffset);
- }
-
- /// Sets the region (image) of a RegionAttachment
- public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) {
- if (region == null) throw new System.ArgumentNullException("region");
-
- // (AtlasAttachmentLoader.cs)
- attachment.RendererObject = region;
- attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
- attachment.regionOffsetX = region.offsetX;
- attachment.regionOffsetY = region.offsetY;
- attachment.regionWidth = region.width;
- attachment.regionHeight = region.height;
- attachment.regionOriginalWidth = region.originalWidth;
- attachment.regionOriginalHeight = region.originalHeight;
-
- if (updateOffset) attachment.UpdateOffset();
- }
-
- /// Sets the region (image) of a MeshAttachment
- public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) {
- if (region == null) throw new System.ArgumentNullException("region");
-
- // (AtlasAttachmentLoader.cs)
- attachment.RendererObject = region;
- attachment.RegionU = region.u;
- attachment.RegionV = region.v;
- attachment.RegionU2 = region.u2;
- attachment.RegionV2 = region.v2;
- attachment.RegionRotate = region.rotate;
- attachment.regionOffsetX = region.offsetX;
- attachment.regionOffsetY = region.offsetY;
- attachment.regionWidth = region.width;
- attachment.regionHeight = region.height;
- attachment.regionOriginalWidth = region.originalWidth;
- attachment.regionOriginalHeight = region.originalHeight;
-
- if (updateUVs) attachment.UpdateUVs();
- }
- #endregion
-
- #region Runtime RegionAttachments
- ///
- /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses a new AtlasPage with the Material provided.///
- public static RegionAttachment ToRegionAttachment (this Sprite sprite, Material material, float rotation = 0f) {
- return sprite.ToRegionAttachment(material.ToSpineAtlasPage(), rotation);
- }
-
- ///
- /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses the AtlasPage provided.///
- public static RegionAttachment ToRegionAttachment (this Sprite sprite, AtlasPage page, float rotation = 0f) {
- if (sprite == null) throw new System.ArgumentNullException("sprite");
- if (page == null) throw new System.ArgumentNullException("page");
- var region = sprite.ToAtlasRegion(page);
- var unitsPerPixel = 1f / sprite.pixelsPerUnit;
- return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
- }
-
- ///
- /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate texture of the Sprite's texture data. Returns a RegionAttachment that uses it. Use this if you plan to use a premultiply alpha shader such as "Spine/Skeleton"
- public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null, float rotation = 0f) {
- if (sprite == null) throw new System.ArgumentNullException("sprite");
- if (shader == null) throw new System.ArgumentNullException("shader");
- var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource);
- var unitsPerPixel = 1f / sprite.pixelsPerUnit;
- return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
- }
-
- public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, float rotation = 0f) {
- return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource, rotation);
- }
-
- ///
- /// Creates a new RegionAttachment from a given AtlasRegion.
- public static RegionAttachment ToRegionAttachment (this AtlasRegion region, string attachmentName, float scale = 0.01f, float rotation = 0f) {
- if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName");
- if (region == null) throw new System.ArgumentNullException("region");
-
- // (AtlasAttachmentLoader.cs)
- var attachment = new RegionAttachment(attachmentName);
-
- attachment.RendererObject = region;
- attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
- attachment.regionOffsetX = region.offsetX;
- attachment.regionOffsetY = region.offsetY;
- attachment.regionWidth = region.width;
- attachment.regionHeight = region.height;
- attachment.regionOriginalWidth = region.originalWidth;
- attachment.regionOriginalHeight = region.originalHeight;
-
- attachment.Path = region.name;
- attachment.scaleX = 1;
- attachment.scaleY = 1;
- attachment.rotation = rotation;
-
- attachment.r = 1;
- attachment.g = 1;
- attachment.b = 1;
- attachment.a = 1;
-
- // pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation.
- attachment.width = attachment.regionOriginalWidth * scale;
- attachment.height = attachment.regionOriginalHeight * scale;
-
- attachment.SetColor(Color.white);
- attachment.UpdateOffset();
- return attachment;
- }
-
- /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change.
- public static void SetScale (this RegionAttachment regionAttachment, Vector2 scale) {
- regionAttachment.scaleX = scale.x;
- regionAttachment.scaleY = scale.y;
- }
-
- /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change.
- public static void SetScale (this RegionAttachment regionAttachment, float x, float y) {
- regionAttachment.scaleX = x;
- regionAttachment.scaleY = y;
- }
-
- /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.
- public static void SetPositionOffset (this RegionAttachment regionAttachment, Vector2 offset) {
- regionAttachment.x = offset.x;
- regionAttachment.y = offset.y;
- }
-
- /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.
- public static void SetPositionOffset (this RegionAttachment regionAttachment, float x, float y) {
- regionAttachment.x = x;
- regionAttachment.y = y;
- }
-
- /// Sets the rotation. Call regionAttachment.UpdateOffset to apply the change.
- public static void SetRotation (this RegionAttachment regionAttachment, float rotation) {
- regionAttachment.rotation = rotation;
- }
- #endregion
- }
-
+
public static class AtlasUtilities {
internal const TextureFormat SpineTextureFormat = TextureFormat.RGBA32;
internal const float DefaultMipmapBias = -0.5f;
@@ -753,212 +581,4 @@ namespace Spine.Unity.AttachmentTools {
return (value - a) / (b - a);
}
}
-
- 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;
-
- foreach (DictionaryEntry a in original.Attachments)
- newSkinAttachments[a.Key] = a.Value;
-
- 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;
-
- if (cloneAttachments) {
- if (overwrite) {
- foreach (DictionaryEntry e in sourceAttachments)
- destinationAttachments[e.Key] = ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked);
- } else {
- foreach (DictionaryEntry e in sourceAttachments) {
- if (destinationAttachments.Contains(e.Key)) continue;
- destinationAttachments.Add(e.Key, ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked));
- }
- }
- } else {
- if (overwrite) {
- foreach (DictionaryEntry e in sourceAttachments)
- destinationAttachments[e.Key] = e.Value;
- } else {
- foreach (DictionaryEntry e in sourceAttachments) {
- if (destinationAttachments.Contains(e.Key)) continue;
- destinationAttachments.Add(e.Key, e.Value);
- }
- }
- }
- }
-
-
- }
-
- public static class AttachmentCloneExtensions {
- ///
- /// Clones the attachment.
- public static Attachment GetCopy (this Attachment o, bool cloneMeshesAsLinked) {
- var meshAttachment = o as MeshAttachment;
- if (meshAttachment != null && cloneMeshesAsLinked)
- return meshAttachment.NewLinkedMesh();
- return o.Copy();
- }
-
- #region Runtime Linked MeshAttachments
- ///
- /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to the AtlasRegion provided.
- public static MeshAttachment GetLinkedMesh (this MeshAttachment o, string newLinkedMeshName, AtlasRegion region) {
- if (region == null) throw new System.ArgumentNullException("region");
- MeshAttachment mesh = o.NewLinkedMesh();
- mesh.SetRegion(region, false);
- return mesh;
- }
-
- ///
- /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
- /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)
- public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Shader shader, Material materialPropertySource = null) {
- var m = new Material(shader);
- if (materialPropertySource != null) {
- m.CopyPropertiesFromMaterial(materialPropertySource);
- m.shaderKeywords = materialPropertySource.shaderKeywords;
- }
- return o.GetLinkedMesh(sprite.name, sprite.ToAtlasRegion());
- }
-
- ///
- /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
- /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)
- public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Material materialPropertySource) {
- return o.GetLinkedMesh(sprite, materialPropertySource.shader, materialPropertySource);
- }
- #endregion
-
- #region RemappedClone Convenience Methods
- ///
- /// Gets a clone of the attachment remapped with a sprite image.
- /// The remapped clone.
- /// The original attachment.
- /// The sprite whose texture to use.
- /// The source material used to copy the shader and material properties from.
- /// If true, a premultiply alpha clone of the original texture will be created.
- /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.
- /// If true the size of the original attachment will be followed, instead of using the Sprite size.
- public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial, bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false) {
- var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture } );
- return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, 1f/sprite.pixelsPerUnit);
- }
-
- ///
- /// Gets a clone of the attachment remapped with an atlasRegion image.
- /// The remapped clone.
- /// The original attachment.
- /// Atlas region.
- /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.
- /// If true the size of the original attachment will be followed, instead of using the Sprite size.
- /// Unity units per pixel scale used to scale the atlas region size when not using the original region size.
- public static Attachment GetRemappedClone (this Attachment o, AtlasRegion atlasRegion, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, float scale = 0.01f) {
- var regionAttachment = o as RegionAttachment;
- if (regionAttachment != null) {
- RegionAttachment newAttachment = (RegionAttachment)regionAttachment.Copy();
- newAttachment.SetRegion(atlasRegion, false);
- if (!useOriginalRegionSize) {
- newAttachment.width = atlasRegion.width * scale;
- newAttachment.height = atlasRegion.height * scale;
- }
- newAttachment.UpdateOffset();
- return newAttachment;
- } else {
- var meshAttachment = o as MeshAttachment;
- if (meshAttachment != null) {
- MeshAttachment newAttachment = cloneMeshAsLinked ? meshAttachment.NewLinkedMesh() : (MeshAttachment)meshAttachment.Copy();
- newAttachment.SetRegion(atlasRegion);
- return newAttachment;
- }
- }
-
- return o.GetCopy(true); // Non-renderable Attachments will return as normal cloned attachments.
- }
- #endregion
- }
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs.meta
similarity index 76%
rename from spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs.meta
rename to spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs.meta
index 68ad280ef..32d291e25 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentTools.cs.meta
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 8dd46dbf979fcb7459246cd37aad09ef
-timeCreated: 1478437807
+guid: 25ceef568a3dad448bf8a14fcc326964
+timeCreated: 1563321428
licenseType: Free
MonoImporter:
serializedVersion: 2
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs
new file mode 100644
index 000000000..16bd1c5a2
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs
@@ -0,0 +1,123 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, 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.
+ *
+ * THIS SOFTWARE IS 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 THIS SOFTWARE,
+ * 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 AttachmentCloneExtensions {
+ ///
+ /// Clones the attachment.
+ public static Attachment GetCopy (this Attachment o, bool cloneMeshesAsLinked) {
+ var meshAttachment = o as MeshAttachment;
+ if (meshAttachment != null && cloneMeshesAsLinked)
+ return meshAttachment.NewLinkedMesh();
+ return o.Copy();
+ }
+
+ #region Runtime Linked MeshAttachments
+ ///
+ /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to the AtlasRegion provided.
+ public static MeshAttachment GetLinkedMesh (this MeshAttachment o, string newLinkedMeshName, AtlasRegion region) {
+ if (region == null) throw new System.ArgumentNullException("region");
+ MeshAttachment mesh = o.NewLinkedMesh();
+ mesh.SetRegion(region, false);
+ return mesh;
+ }
+
+ ///
+ /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
+ /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)
+ public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Shader shader, Material materialPropertySource = null) {
+ var m = new Material(shader);
+ if (materialPropertySource != null) {
+ m.CopyPropertiesFromMaterial(materialPropertySource);
+ m.shaderKeywords = materialPropertySource.shaderKeywords;
+ }
+ return o.GetLinkedMesh(sprite.name, sprite.ToAtlasRegion());
+ }
+
+ ///
+ /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader.
+ /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool)
+ public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Material materialPropertySource) {
+ return o.GetLinkedMesh(sprite, materialPropertySource.shader, materialPropertySource);
+ }
+ #endregion
+
+ #region RemappedClone Convenience Methods
+ ///
+ /// Gets a clone of the attachment remapped with a sprite image.
+ /// The remapped clone.
+ /// The original attachment.
+ /// The sprite whose texture to use.
+ /// The source material used to copy the shader and material properties from.
+ /// If true, a premultiply alpha clone of the original texture will be created.
+ /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.
+ /// If true the size of the original attachment will be followed, instead of using the Sprite size.
+ public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial, bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false) {
+ var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture } );
+ return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, 1f/sprite.pixelsPerUnit);
+ }
+
+ ///
+ /// Gets a clone of the attachment remapped with an atlasRegion image.
+ /// The remapped clone.
+ /// The original attachment.
+ /// Atlas region.
+ /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.
+ /// If true the size of the original attachment will be followed, instead of using the Sprite size.
+ /// Unity units per pixel scale used to scale the atlas region size when not using the original region size.
+ public static Attachment GetRemappedClone (this Attachment o, AtlasRegion atlasRegion, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, float scale = 0.01f) {
+ var regionAttachment = o as RegionAttachment;
+ if (regionAttachment != null) {
+ RegionAttachment newAttachment = (RegionAttachment)regionAttachment.Copy();
+ newAttachment.SetRegion(atlasRegion, false);
+ if (!useOriginalRegionSize) {
+ newAttachment.width = atlasRegion.width * scale;
+ newAttachment.height = atlasRegion.height * scale;
+ }
+ newAttachment.UpdateOffset();
+ return newAttachment;
+ } else {
+ var meshAttachment = o as MeshAttachment;
+ if (meshAttachment != null) {
+ MeshAttachment newAttachment = cloneMeshAsLinked ? meshAttachment.NewLinkedMesh() : (MeshAttachment)meshAttachment.Copy();
+ newAttachment.SetRegion(atlasRegion);
+ return newAttachment;
+ }
+ }
+
+ return o.GetCopy(true); // Non-renderable Attachments will return as normal cloned attachments.
+ }
+ #endregion
+ }
+}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs.meta
new file mode 100644
index 000000000..4d6580e62
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 3431ed563b2c62f4c8c974a99365ba52
+timeCreated: 1563321428
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs
new file mode 100644
index 000000000..32d219655
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs
@@ -0,0 +1,207 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, 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.
+ *
+ * THIS SOFTWARE IS 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 THIS SOFTWARE,
+ * 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 AttachmentRegionExtensions {
+ #region GetRegion
+ ///
+ /// Tries to get the region (image) of a renderable attachment. If the attachment is not renderable, it returns null.
+ public static AtlasRegion GetRegion (this Attachment attachment) {
+ var renderableAttachment = attachment as IHasRendererObject;
+ if (renderableAttachment != null)
+ return renderableAttachment.RendererObject as AtlasRegion;
+
+ return null;
+ }
+
+ /// Gets the region (image) of a RegionAttachment
+ public static AtlasRegion GetRegion (this RegionAttachment regionAttachment) {
+ return regionAttachment.RendererObject as AtlasRegion;
+ }
+
+ /// Gets the region (image) of a MeshAttachment
+ public static AtlasRegion GetRegion (this MeshAttachment meshAttachment) {
+ return meshAttachment.RendererObject as AtlasRegion;
+ }
+ #endregion
+ #region SetRegion
+ ///
+ /// Tries to set the region (image) of a renderable attachment. If the attachment is not renderable, nothing is applied.
+ public static void SetRegion (this Attachment attachment, AtlasRegion region, bool updateOffset = true) {
+ var regionAttachment = attachment as RegionAttachment;
+ if (regionAttachment != null)
+ regionAttachment.SetRegion(region, updateOffset);
+
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null)
+ meshAttachment.SetRegion(region, updateOffset);
+ }
+
+ /// Sets the region (image) of a RegionAttachment
+ public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) {
+ if (region == null) throw new System.ArgumentNullException("region");
+
+ // (AtlasAttachmentLoader.cs)
+ attachment.RendererObject = region;
+ attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
+ attachment.regionOffsetX = region.offsetX;
+ attachment.regionOffsetY = region.offsetY;
+ attachment.regionWidth = region.width;
+ attachment.regionHeight = region.height;
+ attachment.regionOriginalWidth = region.originalWidth;
+ attachment.regionOriginalHeight = region.originalHeight;
+
+ if (updateOffset) attachment.UpdateOffset();
+ }
+
+ /// Sets the region (image) of a MeshAttachment
+ public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) {
+ if (region == null) throw new System.ArgumentNullException("region");
+
+ // (AtlasAttachmentLoader.cs)
+ attachment.RendererObject = region;
+ attachment.RegionU = region.u;
+ attachment.RegionV = region.v;
+ attachment.RegionU2 = region.u2;
+ attachment.RegionV2 = region.v2;
+ attachment.RegionRotate = region.rotate;
+ attachment.regionOffsetX = region.offsetX;
+ attachment.regionOffsetY = region.offsetY;
+ attachment.regionWidth = region.width;
+ attachment.regionHeight = region.height;
+ attachment.regionOriginalWidth = region.originalWidth;
+ attachment.regionOriginalHeight = region.originalHeight;
+
+ if (updateUVs) attachment.UpdateUVs();
+ }
+ #endregion
+
+ #region Runtime RegionAttachments
+ ///
+ /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses a new AtlasPage with the Material provided.///
+ public static RegionAttachment ToRegionAttachment (this Sprite sprite, Material material, float rotation = 0f) {
+ return sprite.ToRegionAttachment(material.ToSpineAtlasPage(), rotation);
+ }
+
+ ///
+ /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses the AtlasPage provided.///
+ public static RegionAttachment ToRegionAttachment (this Sprite sprite, AtlasPage page, float rotation = 0f) {
+ if (sprite == null) throw new System.ArgumentNullException("sprite");
+ if (page == null) throw new System.ArgumentNullException("page");
+ var region = sprite.ToAtlasRegion(page);
+ var unitsPerPixel = 1f / sprite.pixelsPerUnit;
+ return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
+ }
+
+ ///
+ /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate texture of the Sprite's texture data. Returns a RegionAttachment that uses it. Use this if you plan to use a premultiply alpha shader such as "Spine/Skeleton"
+ public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null, float rotation = 0f) {
+ if (sprite == null) throw new System.ArgumentNullException("sprite");
+ if (shader == null) throw new System.ArgumentNullException("shader");
+ var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource);
+ var unitsPerPixel = 1f / sprite.pixelsPerUnit;
+ return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
+ }
+
+ public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, float rotation = 0f) {
+ return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource, rotation);
+ }
+
+ ///
+ /// Creates a new RegionAttachment from a given AtlasRegion.
+ public static RegionAttachment ToRegionAttachment (this AtlasRegion region, string attachmentName, float scale = 0.01f, float rotation = 0f) {
+ if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName");
+ if (region == null) throw new System.ArgumentNullException("region");
+
+ // (AtlasAttachmentLoader.cs)
+ var attachment = new RegionAttachment(attachmentName);
+
+ attachment.RendererObject = region;
+ attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
+ attachment.regionOffsetX = region.offsetX;
+ attachment.regionOffsetY = region.offsetY;
+ attachment.regionWidth = region.width;
+ attachment.regionHeight = region.height;
+ attachment.regionOriginalWidth = region.originalWidth;
+ attachment.regionOriginalHeight = region.originalHeight;
+
+ attachment.Path = region.name;
+ attachment.scaleX = 1;
+ attachment.scaleY = 1;
+ attachment.rotation = rotation;
+
+ attachment.r = 1;
+ attachment.g = 1;
+ attachment.b = 1;
+ attachment.a = 1;
+
+ // pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation.
+ attachment.width = attachment.regionOriginalWidth * scale;
+ attachment.height = attachment.regionOriginalHeight * scale;
+
+ attachment.SetColor(Color.white);
+ attachment.UpdateOffset();
+ return attachment;
+ }
+
+ /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change.
+ public static void SetScale (this RegionAttachment regionAttachment, Vector2 scale) {
+ regionAttachment.scaleX = scale.x;
+ regionAttachment.scaleY = scale.y;
+ }
+
+ /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change.
+ public static void SetScale (this RegionAttachment regionAttachment, float x, float y) {
+ regionAttachment.scaleX = x;
+ regionAttachment.scaleY = y;
+ }
+
+ /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.
+ public static void SetPositionOffset (this RegionAttachment regionAttachment, Vector2 offset) {
+ regionAttachment.x = offset.x;
+ regionAttachment.y = offset.y;
+ }
+
+ /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.
+ public static void SetPositionOffset (this RegionAttachment regionAttachment, float x, float y) {
+ regionAttachment.x = x;
+ regionAttachment.y = y;
+ }
+
+ /// Sets the rotation. Call regionAttachment.UpdateOffset to apply the change.
+ public static void SetRotation (this RegionAttachment regionAttachment, float rotation) {
+ regionAttachment.rotation = rotation;
+ }
+ #endregion
+ }
+}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs.meta
new file mode 100644
index 000000000..58262113b
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7e7eac783deea004e9bc403eca68a7dc
+timeCreated: 1563321428
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs
new file mode 100644
index 000000000..c8b4ce92d
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs
@@ -0,0 +1,156 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, 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.
+ *
+ * THIS SOFTWARE IS 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 THIS SOFTWARE,
+ * 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;
+
+ foreach (DictionaryEntry a in original.Attachments)
+ newSkinAttachments[a.Key] = a.Value;
+
+ 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;
+
+ if (cloneAttachments) {
+ if (overwrite) {
+ foreach (DictionaryEntry e in sourceAttachments)
+ destinationAttachments[e.Key] = ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked);
+ } else {
+ foreach (DictionaryEntry e in sourceAttachments) {
+ if (destinationAttachments.Contains(e.Key)) continue;
+ destinationAttachments.Add(e.Key, ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked));
+ }
+ }
+ } else {
+ if (overwrite) {
+ foreach (DictionaryEntry e in sourceAttachments)
+ destinationAttachments[e.Key] = e.Value;
+ } else {
+ foreach (DictionaryEntry e in sourceAttachments) {
+ if (destinationAttachments.Contains(e.Key)) continue;
+ destinationAttachments.Add(e.Key, e.Value);
+ }
+ }
+ }
+ }
+
+
+ }
+
+}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs.meta
new file mode 100644
index 000000000..5ac1f297b
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: f4692b9527684d048862210ba3f9834e
+timeCreated: 1563321428
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: