diff --git a/CHANGELOG.md b/CHANGELOG.md index db4d14796..b3ff2af59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,8 +44,8 @@ * **Breaking changes** * Removed all `Spine.Unity.AttachmentTools.SkinUtilities` Skin extension methods. These have become obsoleted and error-prone since the introduction of the new Skin API in 3.8. To fix any compile errors, replace any usage of `Skin` extension methods with their counterparts, e.g. replace occurrances of `skin.AddAttachments()` with `skin.AddSkin()`. - * Removed redundant `Spine.Unity.AttachmentTools` extension method `Skin.GetRepackedAttachments()`. Please use `Skin.GetRepackedSkin()` instead. * Removed redundant `Spine.Unity.AttachmentTools.AttachmentCloneExtensions` extension methods `Attachment.GetCopy()` and `Attachment.GetLinkedMesh()`. To fix any compile errors, replace any occurrances with `Attachment.Copy()` and `Attachment.NewLinkedMesh()`. + * Removed rarely used `Spine.Unity.AttachmentTools.AttachmentRegionExtensions` extension methods `Attachment.GetRegion()`. Use `Attachment.RendererObject as AtlasRegion` instead. * Removed redundant `Spine.SkeletonExtensions` extension methods `Skeleton.Set*ToSetupPose()`. Also removed less commonly used extension methods `TrackEntry.AllowImmediateQueue()` and `Attachment.IsRenderable()`. * `Skin.Attachments` now replaces `Skin.GetAttachments()`, returning an `ICollection`. This makes access more consistent and intuitive. To fix any compile errors, replace any occurrances of `skin.GetAttachments()` by `skin.Attachments`. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs index 3bd102dc1..a78f48450 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs @@ -219,6 +219,99 @@ namespace Spine.Unity.AttachmentTools { } #region Runtime Repacking + /// + /// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments, but mapped to a new single texture using the same material. + /// The list of attachments to be repacked. + /// The List(Attachment) to populate with the newly created Attachment objects. + /// + /// May be null. If no Material property source is provided, no special + public static void GetRepackedAttachments (List sourceAttachments, List outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false, bool useOriginalNonrenderables = true) { + if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments"); + if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments"); + + // Use these to detect and use shared regions. + var existingRegions = new Dictionary(); + var regionIndexes = new List(); + var texturesToPack = new List(); + var originalRegions = new List(); + + outputAttachments.Clear(); + outputAttachments.AddRange(sourceAttachments); + + int newRegionIndex = 0; + for (int i = 0, n = sourceAttachments.Count; i < n; i++) { + var originalAttachment = sourceAttachments[i]; + + if (originalAttachment is IHasRendererObject) { + var originalMeshAttachment = originalAttachment as MeshAttachment; + Attachment newAttachment = (originalMeshAttachment != null) ? originalMeshAttachment.NewLinkedMesh() : originalAttachment.Copy(); + var region = ((IHasRendererObject)newAttachment).RendererObject as AtlasRegion; + int existingIndex; + if (existingRegions.TryGetValue(region, out existingIndex)) { + regionIndexes.Add(existingIndex); // Store the region index for the eventual new attachment. + } + else { + originalRegions.Add(region); + texturesToPack.Add(region.ToTexture()); // Add the texture to the PackTextures argument + existingRegions.Add(region, newRegionIndex); // Add the region to the dictionary of known regions + regionIndexes.Add(newRegionIndex); // Store the region index for the eventual new attachment. + newRegionIndex++; + } + + outputAttachments[i] = newAttachment; + } + else { + outputAttachments[i] = useOriginalNonrenderables ? originalAttachment : originalAttachment.Copy(); + regionIndexes.Add(NonrenderingRegion); // Output attachments pairs with regionIndexes list 1:1. Pad with a sentinel if the attachment doesn't have a region. + } + } + + // Fill a new texture with the collected attachment textures. + var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); + newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; + newTexture.name = newAssetName; + // Copy settings + if (texturesToPack.Count > 0) { + var sourceTexture = texturesToPack[0]; + newTexture.CopyTextureAttributesFrom(sourceTexture); + } + var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); + + // Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments + Shader shader = materialPropertySource == null ? Shader.Find("Spine/Skeleton") : materialPropertySource.shader; + var newMaterial = new Material(shader); + if (materialPropertySource != null) { + newMaterial.CopyPropertiesFromMaterial(materialPropertySource); + newMaterial.shaderKeywords = materialPropertySource.shaderKeywords; + } + + newMaterial.name = newAssetName; + newMaterial.mainTexture = newTexture; + var page = newMaterial.ToSpineAtlasPage(); + page.name = newAssetName; + + var repackedRegions = new List(); + for (int i = 0, n = originalRegions.Count; i < n; i++) { + var oldRegion = originalRegions[i]; + var newRegion = UVRectToAtlasRegion(rects[i], oldRegion, page); + repackedRegions.Add(newRegion); + } + + // Map the cloned attachments to the repacked atlas. + for (int i = 0, n = outputAttachments.Count; i < n; i++) { + var a = outputAttachments[i]; + if (a is IHasRendererObject) + a.SetRegion(repackedRegions[regionIndexes[i]]); + } + + // Clean up. + if (clearCache) + AtlasUtilities.ClearCache(); + + outputTexture = newTexture; + outputMaterial = newMaterial; + } + /// /// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas /// comprised of all the regions from the original skin. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs index eac9104a2..c1325c3d4 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentRegionExtensions.cs @@ -33,27 +33,6 @@ 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.