【Unity3D】【NGUI】Atlas的动态创建

1、参见SZUIAtlasMakerRuntimeTest设置相应的值以上值需要提前设置好
2、没有检查是否atlas能够正确创建,自己可以改,加入返回值
3、代码都是在NGUI里面拷贝出来的,只是进行改动,没有新代码
4、适用与那种从网上下图片,之后还不想用UITexture的人,但是还是建议用UITexture如果drawcall不是问题的话
5、自己以后更新按我的方式改改就可以
6、动态创建速度较慢,建议在游戏启动的时候运行
7、游戏时可以将创建的atlas保存到可写目录,避免每次都新创建

SZUIAtlasMakerRuntimeTest.cs

[csharp]  view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class SZUIAtlasMakerRuntimeTest : MonoBehaviour {  
  5.   
  6.     public Texture2D[] texs;  
  7.     public UISprite sprite;  
  8.     private UIAtlas atlas;  
  9.   
  10.     void Start () {  
  11.   
  12.         SZUIAtlasMakerRuntime.atlasTrimming = true;  
  13.         SZUIAtlasMakerRuntime.atlasPMA = atlas != null ? atlas.premultipliedAlpha : false;  
  14.         SZUIAtlasMakerRuntime.unityPacking = false;  
  15.         SZUIAtlasMakerRuntime.atlasPadding = 1;  
  16.         SZUIAtlasMakerRuntime.allow4096 = true;  
  17.         SZUIAtlasMakerRuntime.UITexturePacker.forceSquareAtlas = true;  
  18.   
  19.         if (atlas == null)  
  20.         {  
  21.             atlas = this.gameObject.AddComponent<UIAtlas>();  
  22.         }  
  23.         string lastName = string.Empty;  
  24.         foreach (var tex in texs)  
  25.         {  
  26.             SZUIAtlasMakerRuntime.AddOrUpdate(atlas, tex);  
  27.             lastName = tex.name;  
  28.         }  
  29.         sprite.atlas = atlas;  
  30.         sprite.spriteName = lastName;  
  31.     }  
  32.   
  33. }  


SZUIAtlasMakerRuntime.cs

[csharp]  view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using System.Collections.Generic;  
  4.   
  5. public class SZUIAtlasMakerRuntime {  
  6.   
  7.     public static bool atlasTrimming = true;  
  8.     public static bool atlasPMA = false;  
  9.     public static bool unityPacking = false;  
  10.     public static int atlasPadding = 1;  
  11.     public static bool allow4096 = true;  
  12.   
  13.     public class SpriteEntry : UISpriteData  
  14.     {  
  15.         // Sprite texture -- original texture or a temporary texture  
  16.         public Texture2D tex;  
  17.           
  18.         // Whether the texture is temporary and should be deleted  
  19.         public bool temporaryTexture = false;  
  20.     }  
  21.   
  22.     /// <summary>  
  23.     /// Used to sort the sprites by pixels used  
  24.     /// </summary>  
  25.       
  26.     static int Compare (SpriteEntry a, SpriteEntry b)  
  27.     {  
  28.         // A is null b is not b is greater so put it at the front of the list  
  29.         if (a == null && b != nullreturn 1;  
  30.           
  31.         // A is not null b is null a is greater so put it at the front of the list  
  32.         if (a != null && b == nullreturn -1;  
  33.           
  34.         // Get the total pixels used for each sprite  
  35.         int aPixels = a.width * a.height;  
  36.         int bPixels = b.width * b.height;  
  37.           
  38.         if (aPixels > bPixels) return -1;  
  39.         else if (aPixels < bPixels) return 1;  
  40.         return 0;  
  41.     }  
  42.   
  43.     /// <summary>  
  44.     /// Pack all of the specified sprites into a single texture, updating the outer and inner rects of the sprites as needed.  
  45.     /// </summary>  
  46.       
  47.     static bool PackTextures (Texture2D tex, List<SpriteEntry> sprites)  
  48.     {  
  49.         Texture2D[] textures = new Texture2D[sprites.Count];  
  50.         Rect[] rects;  
  51.          
  52.         #if UNITY_3_5 || UNITY_4_0  
  53.         int maxSize = 4096;  
  54.         #else  
  55.         int maxSize = SystemInfo.maxTextureSize;  
  56.         #endif  
  57.          
  58.         #if UNITY_ANDROID || UNITY_IPHONE  
  59.         maxSize = Mathf.Min(maxSize, allow4096 ? 4096 : 2048);  
  60.         #endif  
  61.         if (unityPacking)  
  62.         {  
  63.             for (int i = 0; i < sprites.Count; ++i) textures[i] = sprites[i].tex;  
  64.             rects = tex.PackTextures(textures, atlasPadding, maxSize);  
  65.         }  
  66.         else  
  67.         {  
  68.             sprites.Sort(Compare);  
  69.             for (int i = 0; i < sprites.Count; ++i) textures[i] = sprites[i].tex;  
  70.             rects = UITexturePacker.PackTextures(tex, textures, 4, 4, atlasPadding, maxSize);  
  71.         }  
  72.           
  73.         for (int i = 0; i < sprites.Count; ++i)  
  74.         {  
  75.             Rect rect = NGUIMath.ConvertToPixels(rects[i], tex.width, tex.height, true);  
  76.               
  77.             // Make sure that we don't shrink the textures  
  78.             if (Mathf.RoundToInt(rect.width) != textures[i].width) return false;  
  79.               
  80.             SpriteEntry se = sprites[i];  
  81.             se.x = Mathf.RoundToInt(rect.x);  
  82.             se.y = Mathf.RoundToInt(rect.y);  
  83.             se.width = Mathf.RoundToInt(rect.width);  
  84.             se.height = Mathf.RoundToInt(rect.height);  
  85.         }  
  86.         return true;  
  87.     }  
  88.   
  89.     static public void AddOrUpdate (UIAtlas atlas, Texture2D tex)  
  90.     {  
  91.         if (atlas != null && tex != null)  
  92.         {  
  93.             List<Texture> textures = new List<Texture>();  
  94.             textures.Add(tex);  
  95.             List<SpriteEntry> sprites = CreateSprites(textures);  
  96.             ExtractSprites(atlas, sprites);  
  97.             UpdateAtlas(atlas, sprites);  
  98.         }  
  99.     }  
  100.       
  101.     /// <summary>  
  102.     /// Update the sprite atlas, keeping only the sprites that are on the specified list.  
  103.     /// </summary>  
  104.       
  105.     static public void UpdateAtlas (UIAtlas atlas, List<SpriteEntry> sprites)  
  106.     {  
  107.         if (sprites.Count > 0)  
  108.         {  
  109.             // Combine all sprites into a single texture and save it  
  110.             if (UpdateTexture(atlas, sprites))  
  111.             {  
  112.                 // Replace the sprites within the atlas  
  113.                 ReplaceSprites(atlas, sprites);  
  114.             }  
  115.               
  116.             // Release the temporary textures  
  117.             ReleaseSprites(sprites);  
  118.             return;  
  119.         }  
  120.         else  
  121.         {  
  122.             atlas.spriteList.Clear();  
  123.             NGUITools.Destroy(atlas.spriteMaterial.mainTexture);  
  124.             atlas.spriteMaterial.mainTexture = null;  
  125.         }  
  126.           
  127.         atlas.MarkAsChanged();  
  128.     }  
  129.   
  130.     /// <summary>  
  131.     /// Add a new sprite to the atlas, given the texture it's coming from and the packed rect within the atlas.  
  132.     /// </summary>  
  133.       
  134.     static public UISpriteData AddSprite (List<UISpriteData> sprites, SpriteEntry se)  
  135.     {  
  136.         // See if this sprite already exists  
  137.         foreach (UISpriteData sp in sprites)  
  138.         {  
  139.             if (sp.name == se.name)  
  140.             {  
  141.                 sp.CopyFrom(se);  
  142.                 return sp;  
  143.             }  
  144.         }  
  145.           
  146.         UISpriteData sprite = new UISpriteData();  
  147.         sprite.CopyFrom(se);  
  148.         sprites.Add(sprite);  
  149.         return sprite;  
  150.     }  
  151.   
  152.     /// <summary>  
  153.     /// Create a list of sprites using the specified list of textures.  
  154.     /// </summary>  
  155.     ///   
  156.     static public List<SpriteEntry> CreateSprites (List<Texture> textures)  
  157.     {  
  158.         List<SpriteEntry> list = new List<SpriteEntry>();  
  159.           
  160.         foreach (Texture tex in textures)  
  161.         {  
  162.             Texture2D oldTex = tex as Texture2D;  
  163.               
  164.             // If we aren't doing trimming, just use the texture as-is  
  165.             if (!atlasTrimming && !atlasPMA)  
  166.             {  
  167.                 SpriteEntry sprite = new SpriteEntry();  
  168.                 sprite.SetRect(0, 0, oldTex.width, oldTex.height);  
  169.                 sprite.tex = oldTex;  
  170.                 sprite.name = oldTex.name;  
  171.                 sprite.temporaryTexture = false;  
  172.                 list.Add(sprite);  
  173.                 continue;  
  174.             }  
  175.               
  176.             // If we want to trim transparent pixels, there is more work to be done  
  177.             Color32[] pixels = oldTex.GetPixels32();  
  178.               
  179.             int xmin = oldTex.width;  
  180.             int xmax = 0;  
  181.             int ymin = oldTex.height;  
  182.             int ymax = 0;  
  183.             int oldWidth = oldTex.width;  
  184.             int oldHeight = oldTex.height;  
  185.               
  186.             // Find solid pixels  
  187.             if (atlasTrimming)  
  188.             {  
  189.                 for (int y = 0, yw = oldHeight; y < yw; ++y)  
  190.                 {  
  191.                     for (int x = 0, xw = oldWidth; x < xw; ++x)  
  192.                     {  
  193.                         Color32 c = pixels[y * xw + x];  
  194.                           
  195.                         if (c.a != 0)  
  196.                         {  
  197.                             if (y < ymin) ymin = y;  
  198.                             if (y > ymax) ymax = y;  
  199.                             if (x < xmin) xmin = x;  
  200.                             if (x > xmax) xmax = x;  
  201.                         }  
  202.                     }  
  203.                 }  
  204.             }  
  205.             else  
  206.             {  
  207.                 xmin = 0;  
  208.                 xmax = oldWidth - 1;  
  209.                 ymin = 0;  
  210.                 ymax = oldHeight - 1;  
  211.             }  
  212.               
  213.             int newWidth  = (xmax - xmin) + 1;  
  214.             int newHeight = (ymax - ymin) + 1;  
  215.               
  216.             if (newWidth > 0 && newHeight > 0)  
  217.             {  
  218.                 SpriteEntry sprite = new SpriteEntry();  
  219.                 sprite.x = 0;  
  220.                 sprite.y = 0;  
  221.                 sprite.width = oldTex.width;  
  222.                 sprite.height = oldTex.height;  
  223.                   
  224.                 // If the dimensions match, then nothing was actually trimmed  
  225.                 if (!atlasPMA && (newWidth == oldWidth && newHeight == oldHeight))  
  226.                 {  
  227.                     sprite.tex = oldTex;  
  228.                     sprite.name = oldTex.name;  
  229.                     sprite.temporaryTexture = false;  
  230.                 }  
  231.                 else  
  232.                 {  
  233.                     // Copy the non-trimmed texture data into a temporary buffer  
  234.                     Color32[] newPixels = new Color32[newWidth * newHeight];  
  235.                       
  236.                     for (int y = 0; y < newHeight; ++y)  
  237.                     {  
  238.                         for (int x = 0; x < newWidth; ++x)  
  239.                         {  
  240.                             int newIndex = y * newWidth + x;  
  241.                             int oldIndex = (ymin + y) * oldWidth + (xmin + x);  
  242.                             if (atlasPMA) newPixels[newIndex] = NGUITools.ApplyPMA(pixels[oldIndex]);  
  243.                             else newPixels[newIndex] = pixels[oldIndex];  
  244.                         }  
  245.                     }  
  246.                       
  247.                     // Create a new texture  
  248.                     sprite.temporaryTexture = true;  
  249.                     sprite.name = oldTex.name;  
  250.                     sprite.tex = new Texture2D(newWidth, newHeight);  
  251.                     sprite.tex.SetPixels32(newPixels);  
  252.                     sprite.tex.Apply();  
  253.                       
  254.                     // Remember the padding offset  
  255.                     sprite.SetPadding(xmin, ymin, oldWidth - newWidth - xmin, oldHeight - newHeight - ymin);  
  256.                 }  
  257.                 list.Add(sprite);  
  258.             }  
  259.         }  
  260.         return list;  
  261.     }  
  262.   
  263.     /// <summary>  
  264.     /// Release all temporary textures created for the sprites.  
  265.     /// </summary>  
  266.       
  267.     static public void ReleaseSprites (List<SpriteEntry> sprites)  
  268.     {  
  269.         foreach (SpriteEntry se in sprites)  
  270.         {  
  271.             if (se.temporaryTexture)  
  272.             {  
  273.                 NGUITools.Destroy(se.tex);  
  274.                 se.tex = null;  
  275.             }  
  276.         }  
  277.         Resources.UnloadUnusedAssets();  
  278.     }  
  279.       
  280.     /// <summary>  
  281.     /// Replace the sprites within the atlas.  
  282.     /// </summary>  
  283.       
  284.     static public void ReplaceSprites (UIAtlas atlas, List<SpriteEntry> sprites)  
  285.     {  
  286.         // Get the list of sprites we'll be updating  
  287.         List<UISpriteData> spriteList = atlas.spriteList;  
  288.         List<UISpriteData> kept = new List<UISpriteData>();  
  289.           
  290.         // Run through all the textures we added and add them as sprites to the atlas  
  291.         for (int i = 0; i < sprites.Count; ++i)  
  292.         {  
  293.             SpriteEntry se = sprites[i];  
  294.             UISpriteData sprite = AddSprite(spriteList, se);  
  295.             kept.Add(sprite);  
  296.         }  
  297.           
  298.         // Remove unused sprites  
  299.         for (int i = spriteList.Count; i > 0; )  
  300.         {  
  301.             UISpriteData sp = spriteList[--i];  
  302.             if (!kept.Contains(sp)) spriteList.RemoveAt(i);  
  303.         }  
  304.           
  305.         // Sort the sprites so that they are alphabetical within the atlas  
  306.         atlas.SortAlphabetically();  
  307.         atlas.MarkAsChanged();  
  308.     }  
  309.   
  310.     /// <summary>  
  311.     /// Extract the specified sprite from the atlas.  
  312.     /// </summary>  
  313.     ///   
  314.     static public SpriteEntry ExtractSprite (UIAtlas atlas, string spriteName)  
  315.     {  
  316.         if (atlas.texture == nullreturn null;  
  317.         UISpriteData sd = atlas.GetSprite(spriteName);  
  318.         if (sd == nullreturn null;  
  319.           
  320.         Texture2D tex = atlas.texture as Texture2D;  
  321.         SpriteEntry se = ExtractSprite(sd, tex);  
  322.         return se;  
  323.     }  
  324.       
  325.     /// <summary>  
  326.     /// Extract the specified sprite from the atlas texture.  
  327.     /// </summary>  
  328.       
  329.     static SpriteEntry ExtractSprite (UISpriteData es, Texture2D tex)  
  330.     {  
  331.         return (tex != null) ? ExtractSprite(es, tex.GetPixels32(), tex.width, tex.height) : null;  
  332.     }  
  333.       
  334.     /// <summary>  
  335.     /// Extract the specified sprite from the atlas texture.  
  336.     /// </summary>  
  337.       
  338.     static SpriteEntry ExtractSprite (UISpriteData es, Color32[] oldPixels, int oldWidth, int oldHeight)  
  339.     {  
  340.         int xmin = Mathf.Clamp(es.x, 0, oldWidth);  
  341.         int ymin = Mathf.Clamp(es.y, 0, oldHeight);  
  342.         int xmax = Mathf.Min(xmin + es.width, oldWidth - 1);  
  343.         int ymax = Mathf.Min(ymin + es.height, oldHeight - 1);  
  344.         int newWidth = Mathf.Clamp(es.width, 0, oldWidth);  
  345.         int newHeight = Mathf.Clamp(es.height, 0, oldHeight);  
  346.           
  347.         if (newWidth == 0 || newHeight == 0) return null;  
  348.           
  349.         Color32[] newPixels = new Color32[newWidth * newHeight];  
  350.           
  351.         for (int y = 0; y < newHeight; ++y)  
  352.         {  
  353.             int cy = ymin + y;  
  354.             if (cy > ymax) cy = ymax;  
  355.               
  356.             for (int x = 0; x < newWidth; ++x)  
  357.             {  
  358.                 int cx = xmin + x;  
  359.                 if (cx > xmax) cx = xmax;  
  360.                   
  361.                 int newIndex = (newHeight - 1 - y) * newWidth + x;  
  362.                 int oldIndex = (oldHeight - 1 - cy) * oldWidth + cx;  
  363.                   
  364.                 newPixels[newIndex] = oldPixels[oldIndex];  
  365.             }  
  366.         }  
  367.           
  368.         // Create a new sprite  
  369.         SpriteEntry sprite = new SpriteEntry();  
  370.         sprite.CopyFrom(es);  
  371.         sprite.SetRect(0, 0, newWidth, newHeight);  
  372.         sprite.temporaryTexture = true;  
  373.         sprite.tex = new Texture2D(newWidth, newHeight);  
  374.         sprite.tex.SetPixels32(newPixels);  
  375.         sprite.tex.Apply();  
  376.         return sprite;  
  377.     }  
  378.       
  379.     /// <summary>  
  380.     /// Extract sprites from the atlas, adding them to the list.  
  381.     /// </summary>  
  382.       
  383.     static public void ExtractSprites (UIAtlas atlas, List<SpriteEntry> finalSprites)  
  384.     {  
  385.         Texture2D tex = atlas.texture as Texture2D;  
  386.           
  387.         if (tex != null)  
  388.         {  
  389.             Color32[] pixels = null;  
  390.             int width = tex.width;  
  391.             int height = tex.height;  
  392.             List<UISpriteData> sprites = atlas.spriteList;  
  393.             float count = sprites.Count;  
  394.             int index = 0;  
  395.               
  396.             foreach (UISpriteData es in sprites)  
  397.             {                 
  398.                 bool found = false;  
  399.                   
  400.                 foreach (SpriteEntry fs in finalSprites)  
  401.                 {  
  402.                     if (es.name == fs.name)  
  403.                     {  
  404.                         fs.CopyBorderFrom(es);  
  405.                         found = true;  
  406.                         break;  
  407.                     }  
  408.                 }  
  409.                   
  410.                 if (!found)  
  411.                 {  
  412.                     if (pixels == null) pixels = tex.GetPixels32();  
  413.                     SpriteEntry sprite = ExtractSprite(es, pixels, width, height);  
  414.                     if (sprite != null) finalSprites.Add(sprite);  
  415.                 }  
  416.             }  
  417.         }  
  418.     }  
  419.   
  420.     static public bool UpdateTexture (UIAtlas atlas, List<SpriteEntry> sprites)  
  421.     {  
  422.         // Get the texture for the atlas  
  423.         Texture2D tex = atlas.texture as Texture2D;  
  424.       
  425.         bool newTexture = tex == null;  
  426.           
  427.         if (newTexture)  
  428.         {  
  429.             // Create a new texture for the atlas  
  430.             tex = new Texture2D(1, 1, TextureFormat.ARGB32, false);  
  431.         }  
  432.           
  433.         // Pack the sprites into this texture  
  434.         if (PackTextures(tex, sprites) && tex != null)  
  435.         {  
  436.             // Update the atlas texture  
  437.             if (newTexture)  
  438.             {  
  439.                 if (atlas.spriteMaterial == null)  
  440.                 {  
  441.                     Shader shader = Shader.Find(atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored");  
  442.                     atlas.spriteMaterial = new Material(shader);  
  443.                 }  
  444.                 atlas.spriteMaterial.mainTexture = tex;  
  445.                 ReleaseSprites(sprites);  
  446.             }  
  447.             return true;  
  448.         }  
  449.         else  
  450.         {  
  451.             return false;  
  452.         }  
  453.     }  
  454.   
  455.     // save as ngui's  
  456.     public class UITexturePacker  
  457.     {  
  458.         // sz modify  
  459.         public static bool forceSquareAtlas = true;  
  460.         public int binWidth = 0;  
  461.         public int binHeight = 0;  
  462.         public bool allowRotations;  
  463.           
  464.         public List<Rect> usedRectangles = new List<Rect>();  
  465.         public List<Rect> freeRectangles = new List<Rect>();  
  466.           
  467.         public enum FreeRectChoiceHeuristic  
  468.         {  
  469.             RectBestShortSideFit, //< -BSSF: Positions the rectangle against the short side of a free rectangle into which it fits the best.  
  470.             RectBestLongSideFit, //< -BLSF: Positions the rectangle against the long side of a free rectangle into which it fits the best.  
  471.             RectBestAreaFit, //< -BAF: Positions the rectangle into the smallest free rect into which it fits.  
  472.             RectBottomLeftRule, //< -BL: Does the Tetris placement.  
  473.             RectContactPointRule //< -CP: Choosest the placement where the rectangle touches other rects as much as possible.  
  474.         };  
  475.           
  476.         public UITexturePacker (int width, int height, bool rotations)  
  477.         {  
  478.             Init(width, height, rotations);  
  479.         }  
  480.           
  481.         public void Init (int width, int height, bool rotations)  
  482.         {  
  483.             binWidth = width;  
  484.             binHeight = height;  
  485.             allowRotations = rotations;  
  486.               
  487.             Rect n = new Rect();  
  488.             n.x = 0;  
  489.             n.y = 0;  
  490.             n.width = width;  
  491.             n.height = height;  
  492.               
  493.             usedRectangles.Clear();  
  494.               
  495.             freeRectangles.Clear();  
  496.             freeRectangles.Add(n);  
  497.         }  
  498.           
  499.         private struct Storage  
  500.         {  
  501.             public Rect rect;  
  502.             public bool paddingX;  
  503.             public bool paddingY;  
  504.         }  
  505.           
  506.         public static Rect[] PackTextures (Texture2D texture, Texture2D[] textures, int width, int height, int padding, int maxSize)  
  507.         {  
  508.             if (width > maxSize && height > maxSize) return null;  
  509.             if (width > maxSize || height > maxSize) { int temp = width; width = height; height = temp; }  
  510.               
  511.             // Force square by sizing up  
  512.             // sz modify  
  513.             //if (NGUISettings.forceSquareAtlas)  
  514.             if (forceSquareAtlas)  
  515.             {  
  516.                 if (width > height)  
  517.                     height = width;  
  518.                 else if (height > width)  
  519.                     width = height;  
  520.             }  
  521.             UITexturePacker bp = new UITexturePacker(width, height, false);  
  522.             Storage[] storage = new Storage[textures.Length];  
  523.               
  524.             for (int i = 0; i < textures.Length; i++)  
  525.             {  
  526.                 Texture2D tex = textures[i];  
  527.                 if (!tex) continue;  
  528.                   
  529.                 Rect rect = new Rect();  
  530.                   
  531.                 int xPadding = 1;  
  532.                 int yPadding = 1;  
  533.                   
  534.                 for (xPadding = 1; xPadding >= 0; --xPadding)  
  535.                 {  
  536.                     for (yPadding = 1; yPadding >= 0; --yPadding)  
  537.                     {  
  538.                         rect = bp.Insert(tex.width + (xPadding * padding), tex.height + (yPadding * padding),  
  539.                                          UITexturePacker.FreeRectChoiceHeuristic.RectBestAreaFit);  
  540.                         if (rect.width != 0 && rect.height != 0) break;  
  541.                           
  542.                         // After having no padding if it still doesn't fit -- increase texture size.  
  543.                         else if (xPadding == 0 && yPadding == 0)  
  544.                         {  
  545.                             return PackTextures(texture, textures, width * (width <= height ? 2 : 1),  
  546.                                                 height * (height < width ? 2 : 1), padding, maxSize);  
  547.                         }  
  548.                     }  
  549.                     if (rect.width != 0 && rect.height != 0) break;  
  550.                 }  
  551.                   
  552.                 storage[i] = new Storage();  
  553.                 storage[i].rect = rect;  
  554.                 storage[i].paddingX = (xPadding != 0);  
  555.                 storage[i].paddingY = (yPadding != 0);  
  556.             }  
  557.               
  558.             texture.Resize(width, height);  
  559.             texture.SetPixels(new Color[width * height]);  
  560.               
  561.             // The returned rects  
  562.             Rect[] rects = new Rect[textures.Length];  
  563.               
  564.             for (int i = 0; i < textures.Length; i++)  
  565.             {  
  566.                 Texture2D tex = textures[i];  
  567.                 if (!tex) continue;  
  568.                   
  569.                 Rect rect = storage[i].rect;  
  570.                 int xPadding = (storage[i].paddingX ? padding : 0);  
  571.                 int yPadding = (storage[i].paddingY ? padding : 0);  
  572.                 Color[] colors = tex.GetPixels();  
  573.                   
  574.                 // Would be used to rotate the texture if need be.  
  575.                 if (rect.width != tex.width + xPadding)  
  576.                 {  
  577.                     Color[] newColors = tex.GetPixels();  
  578.                       
  579.                     for (int x = 0; x < rect.width; x++)  
  580.                     {  
  581.                         for (int y = 0; y < rect.height; y++)  
  582.                         {  
  583.                             int prevIndex = ((int)rect.height - (y + 1)) + x * (int)tex.width;  
  584.                             newColors[x + y * (int)rect.width] = colors[prevIndex];  
  585.                         }  
  586.                     }  
  587.                       
  588.                     colors = newColors;  
  589.                 }  
  590.                   
  591.                 texture.SetPixels((int)rect.x, (int)rect.y, (int)rect.width - xPadding, (int)rect.height - yPadding, colors);  
  592.                 rect.x /= width;  
  593.                 rect.y /= height;  
  594.                 rect.width = (rect.width - xPadding) / width;  
  595.                 rect.height = (rect.height - yPadding) / height;  
  596.                 rects[i] = rect;  
  597.             }  
  598.             texture.Apply();  
  599.             return rects;  
  600.         }  
  601.           
  602.         public Rect Insert (int width, int height, FreeRectChoiceHeuristic method)  
  603.         {  
  604.             Rect newNode = new Rect();  
  605.             int score1 = 0; // Unused in this function. We don't need to know the score after finding the position.  
  606.             int score2 = 0;  
  607.             switch (method)  
  608.             {  
  609.             case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;  
  610.             case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;  
  611.             case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1); break;  
  612.             case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;  
  613.             case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;  
  614.             }  
  615.               
  616.             if (newNode.height == 0)  
  617.                 return newNode;  
  618.               
  619.             int numRectanglesToProcess = freeRectangles.Count;  
  620.             for (int i = 0; i < numRectanglesToProcess; ++i)  
  621.             {  
  622.                 if (SplitFreeNode(freeRectangles[i], ref newNode))  
  623.                 {  
  624.                     freeRectangles.RemoveAt(i);  
  625.                     --i;  
  626.                     --numRectanglesToProcess;  
  627.                 }  
  628.             }  
  629.               
  630.             PruneFreeList();  
  631.               
  632.             usedRectangles.Add(newNode);  
  633.             return newNode;  
  634.         }  
  635.           
  636.         public void Insert (List<Rect> rects, List<Rect> dst, FreeRectChoiceHeuristic method)  
  637.         {  
  638.             dst.Clear();  
  639.               
  640.             while (rects.Count > 0)  
  641.             {  
  642.                 int bestScore1 = int.MaxValue;  
  643.                 int bestScore2 = int.MaxValue;  
  644.                 int bestRectIndex = -1;  
  645.                 Rect bestNode = new Rect();  
  646.                   
  647.                 for (int i = 0; i < rects.Count; ++i)  
  648.                 {  
  649.                     int score1 = 0;  
  650.                     int score2 = 0;  
  651.                     Rect newNode = ScoreRect((int)rects[i].width, (int)rects[i].height, method, ref score1, ref score2);  
  652.                       
  653.                     if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))  
  654.                     {  
  655.                         bestScore1 = score1;  
  656.                         bestScore2 = score2;  
  657.                         bestNode = newNode;  
  658.                         bestRectIndex = i;  
  659.                     }  
  660.                 }  
  661.                   
  662.                 if (bestRectIndex == -1)  
  663.                     return;  
  664.                   
  665.                 PlaceRect(bestNode);  
  666.                 rects.RemoveAt(bestRectIndex);  
  667.             }  
  668.         }  
  669.           
  670.         void PlaceRect (Rect node)  
  671.         {  
  672.             int numRectanglesToProcess = freeRectangles.Count;  
  673.             for (int i = 0; i < numRectanglesToProcess; ++i)  
  674.             {  
  675.                 if (SplitFreeNode(freeRectangles[i], ref node))  
  676.                 {  
  677.                     freeRectangles.RemoveAt(i);  
  678.                     --i;  
  679.                     --numRectanglesToProcess;  
  680.                 }  
  681.             }  
  682.               
  683.             PruneFreeList();  
  684.               
  685.             usedRectangles.Add(node);  
  686.         }  
  687.           
  688.         Rect ScoreRect (int width, int height, FreeRectChoiceHeuristic method, ref int score1, ref int score2)  
  689.         {  
  690.             Rect newNode = new Rect();  
  691.             score1 = int.MaxValue;  
  692.             score2 = int.MaxValue;  
  693.             switch (method)  
  694.             {  
  695.             case FreeRectChoiceHeuristic.RectBestShortSideFit: newNode = FindPositionForNewNodeBestShortSideFit(width, height, ref score1, ref score2); break;  
  696.             case FreeRectChoiceHeuristic.RectBottomLeftRule: newNode = FindPositionForNewNodeBottomLeft(width, height, ref score1, ref score2); break;  
  697.             case FreeRectChoiceHeuristic.RectContactPointRule: newNode = FindPositionForNewNodeContactPoint(width, height, ref score1);  
  698.                 score1 = -score1; // Reverse since we are minimizing, but for contact point score bigger is better.  
  699.                 break;  
  700.             case FreeRectChoiceHeuristic.RectBestLongSideFit: newNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, ref score1); break;  
  701.             case FreeRectChoiceHeuristic.RectBestAreaFit: newNode = FindPositionForNewNodeBestAreaFit(width, height, ref score1, ref score2); break;  
  702.             }  
  703.               
  704.             // Cannot fit the current rectangle.  
  705.             if (newNode.height == 0)  
  706.             {  
  707.                 score1 = int.MaxValue;  
  708.                 score2 = int.MaxValue;  
  709.             }  
  710.               
  711.             return newNode;  
  712.         }  
  713.           
  714.         /// Computes the ratio of used surface area.  
  715.         public float Occupancy ()  
  716.         {  
  717.             ulong usedSurfaceArea = 0;  
  718.             for (int i = 0; i < usedRectangles.Count; ++i)  
  719.                 usedSurfaceArea += (uint)usedRectangles[i].width * (uint)usedRectangles[i].height;  
  720.               
  721.             return (float)usedSurfaceArea / (binWidth * binHeight);  
  722.         }  
  723.           
  724.         Rect FindPositionForNewNodeBottomLeft (int width, int height, ref int bestY, ref int bestX)  
  725.         {  
  726.             Rect bestNode = new Rect();  
  727.             //memset(bestNode, 0, sizeof(Rect));  
  728.               
  729.             bestY = int.MaxValue;  
  730.               
  731.             for (int i = 0; i < freeRectangles.Count; ++i)  
  732.             {  
  733.                 // Try to place the rectangle in upright (non-flipped) orientation.  
  734.                 if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)  
  735.                 {  
  736.                     int topSideY = (int)freeRectangles[i].y + height;  
  737.                     if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))  
  738.                     {  
  739.                         bestNode.x = freeRectangles[i].x;  
  740.                         bestNode.y = freeRectangles[i].y;  
  741.                         bestNode.width = width;  
  742.                         bestNode.height = height;  
  743.                         bestY = topSideY;  
  744.                         bestX = (int)freeRectangles[i].x;  
  745.                     }  
  746.                 }  
  747.                 if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)  
  748.                 {  
  749.                     int topSideY = (int)freeRectangles[i].y + width;  
  750.                     if (topSideY < bestY || (topSideY == bestY && freeRectangles[i].x < bestX))  
  751.                     {  
  752.                         bestNode.x = freeRectangles[i].x;  
  753.                         bestNode.y = freeRectangles[i].y;  
  754.                         bestNode.width = height;  
  755.                         bestNode.height = width;  
  756.                         bestY = topSideY;  
  757.                         bestX = (int)freeRectangles[i].x;  
  758.                     }  
  759.                 }  
  760.             }  
  761.             return bestNode;  
  762.         }  
  763.           
  764.         Rect FindPositionForNewNodeBestShortSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)  
  765.         {  
  766.             Rect bestNode = new Rect();  
  767.             //memset(&bestNode, 0, sizeof(Rect));  
  768.               
  769.             bestShortSideFit = int.MaxValue;  
  770.               
  771.             for (int i = 0; i < freeRectangles.Count; ++i)  
  772.             {  
  773.                 // Try to place the rectangle in upright (non-flipped) orientation.  
  774.                 if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)  
  775.                 {  
  776.                     int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);  
  777.                     int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);  
  778.                     int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);  
  779.                     int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);  
  780.                       
  781.                     if (shortSideFit < bestShortSideFit || (shortSideFit == bestShortSideFit && longSideFit < bestLongSideFit))  
  782.                     {  
  783.                         bestNode.x = freeRectangles[i].x;  
  784.                         bestNode.y = freeRectangles[i].y;  
  785.                         bestNode.width = width;  
  786.                         bestNode.height = height;  
  787.                         bestShortSideFit = shortSideFit;  
  788.                         bestLongSideFit = longSideFit;  
  789.                     }  
  790.                 }  
  791.                   
  792.                 if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)  
  793.                 {  
  794.                     int flippedLeftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);  
  795.                     int flippedLeftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);  
  796.                     int flippedShortSideFit = Mathf.Min(flippedLeftoverHoriz, flippedLeftoverVert);  
  797.                     int flippedLongSideFit = Mathf.Max(flippedLeftoverHoriz, flippedLeftoverVert);  
  798.                       
  799.                     if (flippedShortSideFit < bestShortSideFit || (flippedShortSideFit == bestShortSideFit && flippedLongSideFit < bestLongSideFit))  
  800.                     {  
  801.                         bestNode.x = freeRectangles[i].x;  
  802.                         bestNode.y = freeRectangles[i].y;  
  803.                         bestNode.width = height;  
  804.                         bestNode.height = width;  
  805.                         bestShortSideFit = flippedShortSideFit;  
  806.                         bestLongSideFit = flippedLongSideFit;  
  807.                     }  
  808.                 }  
  809.             }  
  810.             return bestNode;  
  811.         }  
  812.           
  813.         Rect FindPositionForNewNodeBestLongSideFit (int width, int height, ref int bestShortSideFit, ref int bestLongSideFit)  
  814.         {  
  815.             Rect bestNode = new Rect();  
  816.             //memset(&bestNode, 0, sizeof(Rect));  
  817.               
  818.             bestLongSideFit = int.MaxValue;  
  819.               
  820.             for (int i = 0; i < freeRectangles.Count; ++i)  
  821.             {  
  822.                 // Try to place the rectangle in upright (non-flipped) orientation.  
  823.                 if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)  
  824.                 {  
  825.                     int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);  
  826.                     int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);  
  827.                     int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);  
  828.                     int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);  
  829.                       
  830.                     if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))  
  831.                     {  
  832.                         bestNode.x = freeRectangles[i].x;  
  833.                         bestNode.y = freeRectangles[i].y;  
  834.                         bestNode.width = width;  
  835.                         bestNode.height = height;  
  836.                         bestShortSideFit = shortSideFit;  
  837.                         bestLongSideFit = longSideFit;  
  838.                     }  
  839.                 }  
  840.                   
  841.                 if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)  
  842.                 {  
  843.                     int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);  
  844.                     int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);  
  845.                     int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);  
  846.                     int longSideFit = Mathf.Max(leftoverHoriz, leftoverVert);  
  847.                       
  848.                     if (longSideFit < bestLongSideFit || (longSideFit == bestLongSideFit && shortSideFit < bestShortSideFit))  
  849.                     {  
  850.                         bestNode.x = freeRectangles[i].x;  
  851.                         bestNode.y = freeRectangles[i].y;  
  852.                         bestNode.width = height;  
  853.                         bestNode.height = width;  
  854.                         bestShortSideFit = shortSideFit;  
  855.                         bestLongSideFit = longSideFit;  
  856.                     }  
  857.                 }  
  858.             }  
  859.             return bestNode;  
  860.         }  
  861.           
  862.         Rect FindPositionForNewNodeBestAreaFit (int width, int height, ref int bestAreaFit, ref int bestShortSideFit)  
  863.         {  
  864.             Rect bestNode = new Rect();  
  865.             //memset(&bestNode, 0, sizeof(Rect));  
  866.               
  867.             bestAreaFit = int.MaxValue;  
  868.               
  869.             for (int i = 0; i < freeRectangles.Count; ++i)  
  870.             {  
  871.                 int areaFit = (int)freeRectangles[i].width * (int)freeRectangles[i].height - width * height;  
  872.                   
  873.                 // Try to place the rectangle in upright (non-flipped) orientation.  
  874.                 if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)  
  875.                 {  
  876.                     int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - width);  
  877.                     int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - height);  
  878.                     int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);  
  879.                       
  880.                     if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))  
  881.                     {  
  882.                         bestNode.x = freeRectangles[i].x;  
  883.                         bestNode.y = freeRectangles[i].y;  
  884.                         bestNode.width = width;  
  885.                         bestNode.height = height;  
  886.                         bestShortSideFit = shortSideFit;  
  887.                         bestAreaFit = areaFit;  
  888.                     }  
  889.                 }  
  890.                   
  891.                 if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)  
  892.                 {  
  893.                     int leftoverHoriz = Mathf.Abs((int)freeRectangles[i].width - height);  
  894.                     int leftoverVert = Mathf.Abs((int)freeRectangles[i].height - width);  
  895.                     int shortSideFit = Mathf.Min(leftoverHoriz, leftoverVert);  
  896.                       
  897.                     if (areaFit < bestAreaFit || (areaFit == bestAreaFit && shortSideFit < bestShortSideFit))  
  898.                     {  
  899.                         bestNode.x = freeRectangles[i].x;  
  900.                         bestNode.y = freeRectangles[i].y;  
  901.                         bestNode.width = height;  
  902.                         bestNode.height = width;  
  903.                         bestShortSideFit = shortSideFit;  
  904.                         bestAreaFit = areaFit;  
  905.                     }  
  906.                 }  
  907.             }  
  908.             return bestNode;  
  909.         }  
  910.           
  911.         /// Returns 0 if the two intervals i1 and i2 are disjoint, or the length of their overlap otherwise.  
  912.         int CommonIntervalLength (int i1start, int i1end, int i2start, int i2end)  
  913.         {  
  914.             if (i1end < i2start || i2end < i1start)  
  915.                 return 0;  
  916.             return Mathf.Min(i1end, i2end) - Mathf.Max(i1start, i2start);  
  917.         }  
  918.           
  919.         int ContactPointScoreNode (int x, int y, int width, int height)  
  920.         {  
  921.             int score = 0;  
  922.               
  923.             if (x == 0 || x + width == binWidth)  
  924.                 score += height;  
  925.             if (y == 0 || y + height == binHeight)  
  926.                 score += width;  
  927.               
  928.             for (int i = 0; i < usedRectangles.Count; ++i)  
  929.             {  
  930.                 if (usedRectangles[i].x == x + width || usedRectangles[i].x + usedRectangles[i].width == x)  
  931.                     score += CommonIntervalLength((int)usedRectangles[i].y, (int)usedRectangles[i].y + (int)usedRectangles[i].height, y, y + height);  
  932.                 if (usedRectangles[i].y == y + height || usedRectangles[i].y + usedRectangles[i].height == y)  
  933.                     score += CommonIntervalLength((int)usedRectangles[i].x, (int)usedRectangles[i].x + (int)usedRectangles[i].width, x, x + width);  
  934.             }  
  935.             return score;  
  936.         }  
  937.           
  938.         Rect FindPositionForNewNodeContactPoint (int width, int height, ref int bestContactScore)  
  939.         {  
  940.             Rect bestNode = new Rect();  
  941.             //memset(&bestNode, 0, sizeof(Rect));  
  942.               
  943.             bestContactScore = -1;  
  944.               
  945.             for (int i = 0; i < freeRectangles.Count; ++i)  
  946.             {  
  947.                 // Try to place the rectangle in upright (non-flipped) orientation.  
  948.                 if (freeRectangles[i].width >= width && freeRectangles[i].height >= height)  
  949.                 {  
  950.                     int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, width, height);  
  951.                     if (score > bestContactScore)  
  952.                     {  
  953.                         bestNode.x = (int)freeRectangles[i].x;  
  954.                         bestNode.y = (int)freeRectangles[i].y;  
  955.                         bestNode.width = width;  
  956.                         bestNode.height = height;  
  957.                         bestContactScore = score;  
  958.                     }  
  959.                 }  
  960.                 if (allowRotations && freeRectangles[i].width >= height && freeRectangles[i].height >= width)  
  961.                 {  
  962.                     int score = ContactPointScoreNode((int)freeRectangles[i].x, (int)freeRectangles[i].y, height, width);  
  963.                     if (score > bestContactScore)  
  964.                     {  
  965.                         bestNode.x = (int)freeRectangles[i].x;  
  966.                         bestNode.y = (int)freeRectangles[i].y;  
  967.                         bestNode.width = height;  
  968.                         bestNode.height = width;  
  969.                         bestContactScore = score;  
  970.                     }  
  971.                 }  
  972.             }  
  973.             return bestNode;  
  974.         }  
  975.           
  976.         bool SplitFreeNode (Rect freeNode, ref Rect usedNode)  
  977.         {  
  978.             // Test with SAT if the rectangles even intersect.  
  979.             if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||  
  980.                 usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)  
  981.                 return false;  
  982.               
  983.             if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x)  
  984.             {  
  985.                 // New node at the top side of the used node.  
  986.                 if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height)  
  987.                 {  
  988.                     Rect newNode = freeNode;  
  989.                     newNode.height = usedNode.y - newNode.y;  
  990.                     freeRectangles.Add(newNode);  
  991.                 }  
  992.                   
  993.                 // New node at the bottom side of the used node.  
  994.                 if (usedNode.y + usedNode.height < freeNode.y + freeNode.height)  
  995.                 {  
  996.                     Rect newNode = freeNode;  
  997.                     newNode.y = usedNode.y + usedNode.height;  
  998.                     newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);  
  999.                     freeRectangles.Add(newNode);  
  1000.                 }  
  1001.             }  
  1002.               
  1003.             if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y)  
  1004.             {  
  1005.                 // New node at the left side of the used node.  
  1006.                 if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width)  
  1007.                 {  
  1008.                     Rect newNode = freeNode;  
  1009.                     newNode.width = usedNode.x - newNode.x;  
  1010.                     freeRectangles.Add(newNode);  
  1011.                 }  
  1012.                   
  1013.                 // New node at the right side of the used node.  
  1014.                 if (usedNode.x + usedNode.width < freeNode.x + freeNode.width)  
  1015.                 {  
  1016.                     Rect newNode = freeNode;  
  1017.                     newNode.x = usedNode.x + usedNode.width;  
  1018.                     newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);  
  1019.                     freeRectangles.Add(newNode);  
  1020.                 }  
  1021.             }  
  1022.               
  1023.             return true;  
  1024.         }  
  1025.           
  1026.         void PruneFreeList ()  
  1027.         {  
  1028.             for (int i = 0; i < freeRectangles.Count; ++i)  
  1029.                 for (int j = i + 1; j < freeRectangles.Count; ++j)  
  1030.             {  
  1031.                 if (IsContainedIn(freeRectangles[i], freeRectangles[j]))  
  1032.                 {  
  1033.                     freeRectangles.RemoveAt(i);  
  1034.                     --i;  
  1035.                     break;  
  1036.                 }  
  1037.                 if (IsContainedIn(freeRectangles[j], freeRectangles[i]))  
  1038.                 {  
  1039.                     freeRectangles.RemoveAt(j);  
  1040.                     --j;  
  1041.                 }  
  1042.             }  
  1043.         }  
  1044.           
  1045.         bool IsContainedIn (Rect a, Rect b)  
  1046.         {  
  1047.             return a.x >= b.x && a.y >= b.y  
  1048.                 && a.x + a.width <= b.x + b.width  
  1049.                     && a.y + a.height <= b.y + b.height;  
  1050.         }  
  1051.     }  
  1052.   
  1053. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值