From bcffbcb725a8f6796524933da04f7a358e0e15f7 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 31 Mar 2017 02:06:26 +0800 Subject: [PATCH] [unity] Features and fixes to AttachmentTools. --- .../AttachmentTools/AttachmentTools.cs | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index 9ce7a0d11..54b316bdc 100644 --- a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -1,4 +1,4 @@ -/****************************************************************************** +/****************************************************************************** * Spine Runtimes Software License v2.5 * * Copyright (c) 2013-2016, Esoteric Software @@ -129,10 +129,10 @@ 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) { + public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = SpriteAtlasRegionExtensions.SpineTextureFormat, bool mipmaps = SpriteAtlasRegionExtensions.UseMipMaps) { if (sprite == null) throw new System.ArgumentNullException("sprite"); if (shader == null) throw new System.ArgumentNullException("shader"); - var region = sprite.ToAtlasRegionPMAClone(shader); + var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps); var unitsPerPixel = 1f / sprite.pixelsPerUnit; return region.ToRegionAttachment(sprite.name, unitsPerPixel); } @@ -201,6 +201,9 @@ namespace Spine.Unity.Modules.AttachmentTools { } public static class SpriteAtlasRegionExtensions { + internal const TextureFormat SpineTextureFormat = TextureFormat.RGBA32; + internal const bool UseMipMaps = false; + /// /// Creates a new Spine.AtlasPage from a UnityEngine.Material. If the material has a preassigned texture, the page width and height will be set. public static AtlasPage ToSpineAtlasPage (this Material m) { @@ -237,9 +240,9 @@ namespace Spine.Unity.Modules.AttachmentTools { /// /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate of the Sprite's texture data. - public static AtlasRegion ToAtlasRegionPMAClone (this Sprite s, Shader shader) { + public static AtlasRegion ToAtlasRegionPMAClone (this Sprite s, Shader shader, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { var material = new Material(shader); - var tex = s.ToTexture(false); + var tex = s.ToTexture(false, textureFormat, mipmaps); tex.ApplyPMA(true); tex.name = s.name + "-pma-"; @@ -298,40 +301,71 @@ namespace Spine.Unity.Modules.AttachmentTools { /// /// 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) { + 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) { var skinAttachments = o.Attachments; var newSkin = new Skin(newName); + // Use these to detect and use shared regions. + var existingRegions = new Dictionary(); + var regionIndexes = new List(); + + // Collect all textures from the attachments of the original skin. var repackedAttachments = new List(); var texturesToPack = new List(); + var originalRegions = new List(); + int newRegionIndex = 0; foreach (var kvp in skinAttachments) { var newAttachment = kvp.Value.GetClone(true); if (IsRenderable(newAttachment)) { - texturesToPack.Add(newAttachment.GetAtlasRegion().ToTexture()); + + 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++; + } + repackedAttachments.Add(newAttachment); } var key = kvp.Key; newSkin.AddAttachment(key.slotIndex, key.name, newAttachment); } - var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize); + // Fill a new texture with the collected attachment textures. + var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); + newTexture.anisoLevel = texturesToPack[0].anisoLevel; newTexture.name = newName; var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); + // Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments var newMaterial = new Material(shader); newMaterial.name = newName; newMaterial.mainTexture = newTexture; var page = newMaterial.ToSpineAtlasPage(); page.name = newName; + 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 = repackedAttachments.Count; i < n; i++) { var a = repackedAttachments[i]; - var r = rects[i]; - var oldRegion = a.GetAtlasRegion(); - var newRegion = UVRectToAtlasRegion(r, oldRegion.name, page, oldRegion.offsetX, oldRegion.offsetY, oldRegion.rotate); - a.SetRegion(newRegion); + a.SetRegion(repackedRegions[regionIndexes[i]]); } + // Clean up + foreach (var ttp in texturesToPack) + UnityEngine.Object.Destroy(ttp); + t = newTexture; m = newMaterial; return newSkin; @@ -343,12 +377,14 @@ namespace Spine.Unity.Modules.AttachmentTools { /// Creates a new Texture2D object based on an AtlasRegion. /// If applyImmediately is true, Texture2D.Apply is called immediately after the Texture2D is filled with data. - public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true) { + public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { Texture2D sourceTexture = ar.GetMainTexture(); Rect r = ar.GetUnityRect(sourceTexture.height); - Texture2D output = new Texture2D((int)r.width, (int)r.height); + int width = (int)r.width; + int height = (int)r.height; + Texture2D output = new Texture2D(width, height, textureFormat, mipmaps); output.name = ar.name; - Color[] pixelBuffer = sourceTexture.GetPixels((int)r.x, (int)r.y, (int)r.width, (int)r.height); + Color[] pixelBuffer = sourceTexture.GetPixels((int)r.x, (int)r.y, width, height); output.SetPixels(pixelBuffer); if (applyImmediately) @@ -357,11 +393,11 @@ namespace Spine.Unity.Modules.AttachmentTools { return output; } - static Texture2D ToTexture (this Sprite s, bool applyImmediately = true) { + static Texture2D ToTexture (this Sprite s, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { var spriteTexture = s.texture; var r = s.textureRect; var spritePixels = spriteTexture.GetPixels((int)r.x, (int)r.y, (int)r.width, (int)r.height); - var newTexture = new Texture2D((int)r.width, (int)r.height); + var newTexture = new Texture2D((int)r.width, (int)r.height, textureFormat, mipmaps); newTexture.SetPixels(spritePixels); if (applyImmediately) @@ -428,7 +464,17 @@ namespace Spine.Unity.Modules.AttachmentTools { static AtlasRegion UVRectToAtlasRegion (Rect uvRect, string name, AtlasPage page, float offsetX, float offsetY, bool rotate) { var tr = UVRectToTextureRect(uvRect, page.width, page.height); var rr = tr.SpineUnityFlipRect(page.height); - int w = (int)rr.width, h = (int)rr.height, x = (int)rr.x, y = (int)rr.y; + + int x = (int)rr.x, y = (int)rr.y; + int w, h; + if (rotate) { + w = (int)rr.height; + h = (int)rr.width; + } else { + w = (int)rr.width; + h = (int)rr.height; + } + return new AtlasRegion { page = page, name = name, @@ -659,9 +705,9 @@ namespace Spine.Unity.Modules.AttachmentTools { }; // Linked mesh - if (o.parentMesh != null) { + if (o.ParentMesh != null) { // bones, vertices, worldVerticesLength, regionUVs, triangles, HullLength, Edges, Width, Height - ma.ParentMesh = o.parentMesh; + ma.ParentMesh = o.ParentMesh; } else { CloneVertexAttachment(o, ma); // bones, vertices, worldVerticesLength ma.regionUVs = o.regionUVs.Clone() as float[]; @@ -669,7 +715,7 @@ namespace Spine.Unity.Modules.AttachmentTools { ma.hulllength = o.hulllength; // Nonessential. - ma.Edges = o.Edges.Clone() as int[]; + ma.Edges = (o.Edges == null) ? null : o.Edges.Clone() as int[]; // Allow absence of Edges array when nonessential data is not exported. ma.Width = o.Width; ma.Height = o.Height; } @@ -706,8 +752,8 @@ namespace Spine.Unity.Modules.AttachmentTools { if (region == null) throw new System.ArgumentNullException("region"); // If parentMesh is a linked mesh, create a link to its parent. Preserves Deform animations. - if (o.parentMesh != null) - o = o.parentMesh; + if (o.ParentMesh != null) + o = o.ParentMesh; // 1. NewMeshAttachment (AtlasAttachmentLoader.cs) var mesh = new MeshAttachment(newLinkedMeshName);