namespace MagicaVoxelToolbox { using System.Collections.Generic; using System.Collections; using UnityEngine; public class PackingData { public int Width; public int Height; public Color[] TextureColors; public PackingData (int sizeU, int sizeV, Color[] colors, bool outline = true) { Width = sizeU; Height = sizeV; if (outline) { Width += 2; Height += 2; } TextureColors = new Color[Width * Height]; if (outline) { for (int v = 1; v < Height - 1; v++) { TextureColors[v * Width] = colors[(v - 1) * sizeU]; for (int u = 1; u < Width - 1; u++) { TextureColors[v * Width + u] = colors[(v - 1) * sizeU + (u - 1)]; } TextureColors[v * Width + Width - 1] = colors[(v - 1) * sizeU + (Width - 3)]; } for (int u = 0; u < Width; u++) { TextureColors[u] = TextureColors[Width + u]; TextureColors[(Height - 1) * Width + u] = TextureColors[(Height - 2) * Width + u]; } } else { for (int v = 0; v < Height; v++) { for (int u = 0; u < Width; u++) { TextureColors[v * Width + u] = colors[v * sizeU + u]; } } } } public bool SameWith (PackingData other) { if (Width != other.Width || Height != other.Height) { return false; } int end = TextureColors.Length - Width; for (int i = Width; i < end; i++) { if (TextureColors[i] != other.TextureColors[i]) { return false; } } return true; } } public struct RectPacking { private class ItemSorter : IComparer { bool SortWithIndex; public ItemSorter (bool sortWithIndex) { SortWithIndex = sortWithIndex; } public int Compare (Item x, Item y) { return SortWithIndex ? x.Index.CompareTo(y.Index) : y.Height.CompareTo(x.Height); } } private struct Item { public int Index; public int X, Y; public int Width, Height; public Color[] TextureColors; } private class Shelf { public int Y; public int Width; public int Height; public int[] RoomHeight; public bool AddItem (ref Item item, ref int width, ref int height) { int currentFitWidth = 0; int maxRoomY = 0; for (int i = 0; i < RoomHeight.Length; i++) { if (RoomHeight[i] >= item.Height) { // fit currentFitWidth++; maxRoomY = Mathf.Max(maxRoomY, Height - RoomHeight[i]); if (currentFitWidth >= item.Width) { item.Y = Y + maxRoomY; item.X = i - currentFitWidth + 1; // set width height width = Mathf.Max(width, item.X + item.Width); height = Mathf.Max(height, item.Y + item.Height); // Set room height for (int j = item.X; j < item.X + item.Width; j++) { RoomHeight[j] = Height - maxRoomY - item.Height; } return true; } } else { // not fit currentFitWidth = 0; maxRoomY = 0; } } return false; } } public static Rect[] PackTextures (out Texture2D texture, List packingList, bool hasOutline, bool defaultClear) { // Check if (packingList.Count == 0) { texture = Texture2D.whiteTexture; return new Rect[1] { new Rect(0, 0, 1, 1) }; } // Init int aimSize = 16; int minWidth = 16; int allArea = 0; List items = new List(); for (int i = 0; i < packingList.Count; i++) { int w = packingList[i].Width; int h = packingList[i].Height; items.Add(new Item() { Index = i, Width = w, Height = h, TextureColors = packingList[i].TextureColors }); allArea += items[i].Width * items[i].Height; minWidth = Mathf.Max(minWidth, items[i].Width); } while (aimSize < minWidth || aimSize * aimSize < allArea * 1.1f) { aimSize *= 2; } //Sort With Height items.Sort(new ItemSorter(false)); // Pack int width = 0; int height = 0; List shelfs = new List(); for (int i = 0; i < items.Count; i++) { // Try Add bool success = false; Item item = items[i]; for (int j = 0; j < shelfs.Count; j++) { success = shelfs[j].AddItem( ref item, ref width, ref height ); if (success) { items[i] = item; break; } } // Fail to Add if (!success) { // New shelf Shelf s = new Shelf() { Y = shelfs.Count == 0 ? 0 : shelfs[shelfs.Count - 1].Y + shelfs[shelfs.Count - 1].Height, Width = aimSize, Height = items[i].Height, RoomHeight = new int[aimSize], }; for (int j = 0; j < aimSize; j++) { s.RoomHeight[j] = s.Height; } shelfs.Add(s); // Add Again success = shelfs[shelfs.Count - 1].AddItem( ref item, ref width, ref height ); items[i] = item; // Error, this shouldn't be happen... if (!success) { throw new System.Exception("Fail to pack textures."); } } } // Set Texture width = aimSize; height = Mathf.Max(height, aimSize); texture = new Texture2D(width, height, TextureFormat.ARGB32, false, false) { filterMode = FilterMode.Point }; // Default Color if (defaultClear) { var defualtColors = new Color[width * height]; for (int i = 0; i < defualtColors.Length; i++) { defualtColors[i] = Color.clear; } texture.SetPixels(defualtColors); } for (int i = 0; i < items.Count; i++) { texture.SetPixels( items[i].X, items[i].Y, items[i].Width, items[i].Height, items[i].TextureColors ); } texture.Apply(); // Sort with Index items.Sort(new ItemSorter(true)); Rect[] uvs = new Rect[items.Count]; for (int i = 0; i < items.Count; i++) { if (hasOutline) { uvs[i] = new Rect( (float)(items[i].X + 1) / width, (float)(items[i].Y + 1) / height, (float)(items[i].Width - 2) / width, (float)(items[i].Height - 2) / height ); } else { uvs[i] = new Rect( (float)(items[i].X) / width, (float)(items[i].Y) / height, (float)(items[i].Width) / width, (float)(items[i].Height) / height ); } } return uvs; } } }