From ba02df87bda315f4706faff42e2e65485da8d7f0 Mon Sep 17 00:00:00 2001 From: pharan Date: Mon, 16 Oct 2017 21:42:39 +0800 Subject: [PATCH] [unity] GetRepackedAttachments --- .../AttachmentTools/AttachmentTools.cs | 112 +++++++++++++++--- 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index ab5d73d0b..cc92143e1 100644 --- a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -129,7 +129,7 @@ namespace Spine.Unity.Modules.AttachmentTools { /// /// 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 = SpriteAtlasRegionExtensions.SpineTextureFormat, bool mipmaps = SpriteAtlasRegionExtensions.UseMipMaps, Material materialPropertySource = null) { + public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null) { if (sprite == null) throw new System.ArgumentNullException("sprite"); if (shader == null) throw new System.ArgumentNullException("shader"); var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource); @@ -137,7 +137,7 @@ namespace Spine.Unity.Modules.AttachmentTools { return region.ToRegionAttachment(sprite.name, unitsPerPixel); } - public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = SpriteAtlasRegionExtensions.SpineTextureFormat, bool mipmaps = SpriteAtlasRegionExtensions.UseMipMaps) { + public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps) { return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource); } @@ -210,7 +210,7 @@ namespace Spine.Unity.Modules.AttachmentTools { #endregion } - public static class SpriteAtlasRegionExtensions { + public static class AtlasUtilities { internal const TextureFormat SpineTextureFormat = TextureFormat.RGBA32; internal const float DefaultMipmapBias = -0.5f; internal const bool UseMipMaps = false; @@ -395,16 +395,98 @@ namespace Spine.Unity.Modules.AttachmentTools { #region Runtime Repacking /// - /// 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. - /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. - public static Skin GetRepackedSkin (this Skin o, string newName, Material materialPropertySource, out Material m, out Texture2D t, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { - return GetRepackedSkin(o, newName, materialPropertySource.shader, out m, out t, maxAtlasSize, padding, textureFormat, mipmaps, materialPropertySource); + /// 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") { + 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]; + var newAttachment = originalAttachment.GetClone(true); + if (IsRenderable(newAttachment)) { + + var region = newAttachment.GetAtlasRegion(); + 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; + } + } + + // Fill a new texture with the collected attachment textures. + var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); + newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; + newTexture.anisoLevel = texturesToPack[0].anisoLevel; + newTexture.name = newAssetName; + 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.name, page, oldRegion.offsetX, oldRegion.offsetY, oldRegion.rotate); + 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]; + a.SetRegion(repackedRegions[regionIndexes[i]]); + } + + // Clean up. + foreach (var ttp in texturesToPack) + UnityEngine.Object.Destroy(ttp); + + 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. /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. - public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material m, out Texture2D t, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null) { + public static Skin GetRepackedSkin (this Skin o, string newName, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + return GetRepackedSkin(o, newName, materialPropertySource.shader, out outputMaterial, out outputTexture, maxAtlasSize, padding, textureFormat, mipmaps, materialPropertySource); + } + + /// + /// 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. + /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. + public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null) { var skinAttachments = o.Attachments; var newSkin = new Skin(newName); @@ -441,7 +523,7 @@ namespace Spine.Unity.Modules.AttachmentTools { // Fill a new texture with the collected attachment textures. var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); - newTexture.mipMapBias = SpriteAtlasRegionExtensions.DefaultMipmapBias; + newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; newTexture.anisoLevel = texturesToPack[0].anisoLevel; newTexture.name = newName; var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); @@ -471,12 +553,12 @@ namespace Spine.Unity.Modules.AttachmentTools { a.SetRegion(repackedRegions[regionIndexes[i]]); } -// // Clean up -// foreach (var ttp in texturesToPack) -// UnityEngine.Object.Destroy(ttp); + // Clean up. + foreach (var ttp in texturesToPack) + UnityEngine.Object.Destroy(ttp); - t = newTexture; - m = newMaterial; + outputTexture = newTexture; + outputMaterial = newMaterial; return newSkin; } @@ -679,7 +761,7 @@ namespace Spine.Unity.Modules.AttachmentTools { } } - public static class SkinExtensions { + public static class SkinUtilities { #region Skeleton Skin Extensions ///