Unity – TextMeshPro With Atlas

Classé dans : Non classé | 0

This in one right click use your Atlas for TextMesh Pro in text image

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEditor;
using UnityEditor.U2D;
using UnityEngine.U2D;
using System;

namespace Plugins.SimpleTexturePacker.Editor
{

    /// <summary>
    /// Taken from: https://forum.unity.com/threads/text-mesh-pro-does-not-work-with-spriteatlas-assets.697088/#post-7581571
    /// All credits go to the original author
    /// Original name: SimpleSpriteAtlasConverter
    /// Atlas allowRotation and tightPacking must be set to false.
    /// Select a Sprite atlas and go to "Assets/SimpleSpriteAtlasConverter - Repack atlas to sprite" to convert it to a sprite.
    /// This is most useful to add a texture to TMPro without requiring managing a tpsheet project for TexturePacker
    /// </summary>
    public class SimpleTexturePacker
    {

        [MenuItem("Assets/SimpleSpriteAtlasConverter - Repack atlas to sprite", true)]
        private static bool RepackAtlasToSprite()
        {
            if(Selection.activeObject == null)
            {
                Debug.Log("No object selected");
                return false;
            }

            return Selection.activeObject.GetType() == typeof(SpriteAtlas);
        }

        [MenuItem("Assets/SimpleSpriteAtlasConverter - Repack atlas to sprite")]
        static void RepackAtlasToSprite(MenuCommand command)
        {

            SpriteAtlas atlas = (SpriteAtlas)Selection.activeObject;

            if(atlas.GetPackingSettings().enableRotation || atlas.GetPackingSettings().enableTightPacking)
            {
                Debug.LogError($"Problem! Atlas 'allowRotation' and 'tightPacking' must be set to false in '{Selection.activeObject.name}' to work correctly!", Selection.activeObject);
                return;
            }

            var padding = atlas.GetPackingSettings().padding;

            //Pack sprites
            Sprite[] sprites = new Sprite[atlas.spriteCount];
            atlas.GetSprites(sprites);
            var rects = new List<SpriteRect>();
            foreach(var spr in sprites)
            {
                rects.Add(new SpriteRect(spr, padding));
            }
            rects.Sort((a, b) => b.area.CompareTo(a.area));

            var packer = new RectanglePacker();

            foreach(var rect in rects)
            {
                if(!packer.Pack(rect.w, rect.h, out rect.x, out rect.y))
                    throw new Exception("Uh oh, we couldn't pack the rectangle :(");
            }

            //Calculate image size
            var maxSize = atlas.GetPlatformSettings("DefaultTexturePlatform").maxTextureSize;
            var pngSize = Math.Max(packer.Width, packer.Height);
            var powoftwo = 16;
            while(powoftwo < pngSize)
                powoftwo *= 2;
            pngSize = powoftwo;
            if(pngSize > maxSize)
                pngSize = maxSize;

            // LL added: seems like some folks need sRGB, so we add a choice for that
            var optionLinear = RenderTextureReadWrite.Linear;
            var optionSrgb = RenderTextureReadWrite.sRGB;
            var option = EditorUtility.DisplayDialogComplex(
                    "Choose texture format",
                    "This will be the target texture format. If in doubt, use Linear.",
                    optionLinear.ToString(),
                    optionSrgb.ToString(),
                    "Cancel"
                );
            RenderTextureReadWrite renderTextureReadWrite;
            if(option == 0)
                renderTextureReadWrite = optionLinear;
            else if(option == 1)
                renderTextureReadWrite = optionSrgb;
            else
            {
                Debug.Log("Cancelled packing texture");
                return;
            }

            Texture2D texture = new Texture2D(pngSize, pngSize, TextureFormat.RGBA32, false);

            //Make texture transparent
            Color fillColor = Color.clear;
            Color[] fillPixels = new Color[texture.width * texture.height];
            for(int i = 0; i < fillPixels.Length; i++)
                fillPixels[i] = fillColor;
            texture.SetPixels(fillPixels);

            var metas = new List<SpriteMetaData>();

            //Draw sprites
            foreach(var rect in rects)
            {
                var t = GetReadableTexture(rect.sprite.texture, renderTextureReadWrite);
                texture.SetPixels32(rect.x + padding, rect.y + padding, (int)rect.sprite.rect.width, (int)rect.sprite.rect.height, t.GetPixels32());
                metas.Add(new SpriteMetaData()
                {
                    alignment = 6, //BottomLeft
                    name = rect.sprite.name.Replace("(Clone)", ""),
                    rect = new Rect(rect.x + padding, rect.y + padding, rect.sw, rect.sh)
                });

            }

            //Save image
            var path = AssetDatabase.GetAssetPath(atlas);

            // LL: handling Sprite Atlas V2 case
            string pngPath;
            if(path.EndsWith(".spriteatlas"))
                pngPath = path.Replace(".spriteatlas", ".png");
            else if(path.EndsWith(".spriteatlasv2"))
                pngPath = path.Replace(".spriteatlasv2", ".png");
            else
                throw new InvalidOperationException($"Unexpected file extension: {path}");

            Debug.Log($"Create sprite from atlas: {atlas.name} path: {path}");

            byte[] bytes = texture.EncodeToPNG();
            var tempPngPath = pngPath; //To prevent bug in number of naming
            // Commented out : we do want to update existing assets
/*            int j = 1;
            while(File.Exists(tempPngPath))
            {
                tempPngPath = Path.Combine(Path.GetDirectoryName(pngPath), $"{Path.GetFileNameWithoutExtension(pngPath)} {j}{Path.GetExtension(pngPath)}");
                j++;
            }*/
            pngPath = tempPngPath.Replace(".png", "_Generated.png");
            File.WriteAllBytes(pngPath, bytes);

            //Update sprite settings
            AssetDatabase.Refresh();

            TextureImporter ti = AssetImporter.GetAtPath(pngPath) as TextureImporter;
            ti.textureType = TextureImporterType.Sprite;
            ti.spriteImportMode = SpriteImportMode.Multiple;
            ti.spritesheet = metas.ToArray();

            EditorUtility.SetDirty(ti);
            ti.SaveAndReimport();

            // Boardcast to generate texture
            Selection.activeObject = AssetDatabase.LoadAssetAtPath(ti.assetPath, typeof(UnityEngine.Object));
            TMPro.EditorUtilities.TMP_SpriteAssetMenu.CreateSpriteAsset();
        }

        private static Texture2D GetReadableTexture(Texture2D source, RenderTextureReadWrite readWriteMode)
        {

            RenderTexture tmp = RenderTexture.GetTemporary(
                    source.width,
                    source.height,
                    0,
                    RenderTextureFormat.ARGB32,
                    readWriteMode);

            Graphics.Blit(source, tmp);
            RenderTexture previous = RenderTexture.active;
            RenderTexture.active = tmp;
            Texture2D result = new Texture2D(source.width, source.height);
            result.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
            result.Apply();
            RenderTexture.active = previous;
            RenderTexture.ReleaseTemporary(tmp);
            return result;
        }
    }

    public class SpriteRect
    {
        public Sprite sprite;
        public int x, y, w, h;

        public SpriteRect(Sprite sprite, int padding)
        {
            this.sprite = sprite;
            this.x = 0;
            this.y = 0;
            this.w = (int)sw + padding * 2;
            this.h = (int)sh + padding * 2;
        }

        public int sw => (int)sprite.rect.width;
        public int sh => (int)sprite.rect.height;
        public int area => w * h;
    }

    // https://github.com/mikaturunen/RectanglePacker
    public class RectanglePacker
    {
        public int Width { get; private set; }
        public int Height { get; private set; }

        List<Node> nodes = new List<Node>();

        public RectanglePacker()
        {
            nodes.Add(new Node(0, 0, int.MaxValue, int.MaxValue));
        }

        public bool Pack(int w, int h, out int x, out int y)
        {
            for(int i = 0; i < nodes.Count; ++i)
            {
                if(w <= nodes[i].W && h <= nodes[i].H)
                {
                    var node = nodes[i];
                    nodes.RemoveAt(i);
                    x = node.X;
                    y = node.Y;
                    int r = x + w;
                    int b = y + h;
                    nodes.Add(new Node(r, y, node.Right - r, h));
                    nodes.Add(new Node(x, b, w, node.Bottom - b));
                    nodes.Add(new Node(r, b, node.Right - r, node.Bottom - b));
                    Width = Math.Max(Width, r);
                    Height = Math.Max(Height, b);
                    return true;
                }
            }
            x = 0;
            y = 0;
            return false;
        }

        public struct Node
        {
            public int X;
            public int Y;
            public int W;
            public int H;

            public Node(int x, int y, int w, int h)
            {
                X = x;
                Y = y;
                W = w;
                H = h;
            }

            public int Right
            {
                get { return X + W; }
            }

            public int Bottom
            {
                get { return Y + H; }
            }
        }
    }
}