/// <summary> /// Add a new sprite to the atlas, given the texture it's coming from and the packed rect within the atlas. /// </summary> static UIAtlas.Sprite AddSprite(List<UIAtlas.Sprite> sprites, SpriteEntry se) { UIAtlas.Sprite sprite = null; // See if this sprite already exists foreach (UIAtlas.Sprite sp in sprites) { Debug.Log(se.tex.name + " " + sp.name); if (sp.name == se.tex.name) { sprite = sp; break; } } if (sprite != null) { float x0 = sprite.inner.xMin - sprite.outer.xMin; float y0 = sprite.inner.yMin - sprite.outer.yMin; float x1 = sprite.outer.xMax - sprite.inner.xMax; float y1 = sprite.outer.yMax - sprite.inner.yMax; sprite.outer = se.rect; sprite.inner = se.rect; sprite.inner.xMin = Mathf.Max(sprite.inner.xMin + x0, sprite.outer.xMin); sprite.inner.yMin = Mathf.Max(sprite.inner.yMin + y0, sprite.outer.yMin); sprite.inner.xMax = Mathf.Min(sprite.inner.xMax - x1, sprite.outer.xMax); sprite.inner.yMax = Mathf.Min(sprite.inner.yMax - y1, sprite.outer.yMax); } else { sprite = new UIAtlas.Sprite(); sprite.name = se.tex.name; sprite.outer = se.rect; sprite.inner = se.rect; sprites.Add(sprite); } float width = Mathf.Max(1f, sprite.outer.width); float height = Mathf.Max(1f, sprite.outer.height); // Sprite's padding values are relative to width and height sprite.paddingLeft = se.minX / width; sprite.paddingRight = se.maxX / width; sprite.paddingTop = se.maxY / height; sprite.paddingBottom = se.minY / height; return sprite; }
/// <summary> /// Used to sort the sprites by pixels used /// </summary> static int Compare(SpriteEntry a, SpriteEntry b) { // A is null b is not b is greater so put it at the front of the list if (a == null && b != null) return 1; // A is not null b is null a is greater so put it at the front of the list if (a == null && b != null) return -1; // Get the total pixels used for each sprite int aPixels = a.width * a.height; int bPixels = b.width * b.height; if (aPixels > bPixels) return -1; else if (aPixels < bPixels) return 1; return 0; }
/// <summary> /// Add a new sprite to the atlas, given the texture it's coming from and the packed rect within the atlas. /// </summary> static UISpriteData AddSprite(List<UISpriteData> sprites, SpriteEntry se) { // See if this sprite already exists foreach (UISpriteData sp in sprites) { if (sp.name == se.name) { sp.CopyFrom(se); return sp; } } UISpriteData sprite = new UISpriteData(); sprite.CopyFrom(se); sprites.Add(sprite); return sprite; }
/// <summary> /// Create a list of sprites using the specified list of textures. /// </summary> static public List <SpriteEntry> CreateSprites(List <Texture> textures) { List <SpriteEntry> list = new List <SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = NGUIEditorTools.ImportTexture(tex, true, false, true); if (oldTex == null) { oldTex = tex as Texture2D; } if (oldTex == null) { continue; } // If we aren't doing trimming, just use the texture as-is if (!NGUISettings.atlasTrimming && !NGUISettings.atlasPMA) { SpriteEntry sprite = new SpriteEntry(); sprite.SetRect(0, 0, oldTex.width, oldTex.height); sprite.tex = oldTex; sprite.name = oldTex.name; sprite.temporaryTexture = false; list.Add(sprite); continue; } // If we want to trim transparent pixels, there is more work to be done Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; // Find solid pixels if (NGUISettings.atlasTrimming) { for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels[y * xw + x]; if (c.a != 0) { if (y < ymin) { ymin = y; } if (y > ymax) { ymax = y; } if (x < xmin) { xmin = x; } if (x > xmax) { xmax = x; } } } } } else { xmin = 0; xmax = oldWidth - 1; ymin = 0; ymax = oldHeight - 1; } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.x = 0; sprite.y = 0; sprite.width = oldTex.width; sprite.height = oldTex.height; // If the dimensions match, then nothing was actually trimmed if (!NGUISettings.atlasPMA && (newWidth == oldWidth && newHeight == oldHeight)) { sprite.tex = oldTex; sprite.name = oldTex.name; sprite.temporaryTexture = false; } else { // Copy the non-trimmed texture data into a temporary buffer Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); if (NGUISettings.atlasPMA) { newPixels[newIndex] = NGUITools.ApplyPMA(pixels[oldIndex]); } else { newPixels[newIndex] = pixels[oldIndex]; } } } // Create a new texture sprite.name = oldTex.name; sprite.SetTexture(newPixels, newWidth, newHeight); // Remember the padding offset sprite.SetPadding(xmin, ymin, oldWidth - newWidth - xmin, oldHeight - newHeight - ymin); } list.Add(sprite); } } return(list); }
/// <summary> /// Draw the UI for this tool. /// </summary> void OnGUI() { bool create = false; bool update = false; bool replace = false; string prefabPath = ""; string matPath = ""; // If we have an atlas to work with, see if we can figure out the path for it and its material if (NGUISettings.atlas != null && NGUISettings.atlas.name == NGUISettings.atlasName) { prefabPath = AssetDatabase.GetAssetPath(NGUISettings.atlas.gameObject.GetInstanceID()); if (NGUISettings.atlas.spriteMaterial != null) { matPath = AssetDatabase.GetAssetPath(NGUISettings.atlas.spriteMaterial.GetInstanceID()); } } // Assume default values if needed if (string.IsNullOrEmpty(NGUISettings.atlasName)) { NGUISettings.atlasName = "New Atlas"; } if (string.IsNullOrEmpty(prefabPath)) { prefabPath = NGUIEditorTools.GetSelectionFolder() + NGUISettings.atlasName + ".prefab"; } if (string.IsNullOrEmpty(matPath)) { matPath = NGUIEditorTools.GetSelectionFolder() + NGUISettings.atlasName + ".mat"; } // Try to load the prefab GameObject go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; if (NGUISettings.atlas == null && go != null) { NGUISettings.atlas = go.GetComponent <UIAtlas>(); } NGUIEditorTools.SetLabelWidth(80f); GUILayout.Space(6f); GUILayout.BeginHorizontal(); if (go == null) { GUI.backgroundColor = Color.green; create = GUILayout.Button("Create", GUILayout.Width(76f)); } else { GUI.backgroundColor = Color.red; create = GUILayout.Button("Replace", GUILayout.Width(76f)); } GUI.backgroundColor = Color.white; NGUISettings.atlasName = GUILayout.TextField(NGUISettings.atlasName); GUILayout.EndHorizontal(); if (create) { // If the prefab already exists, confirm that we want to overwrite it if (go == null || EditorUtility.DisplayDialog("Are you sure?", "Are you sure you want to replace the contents of the " + NGUISettings.atlasName + " atlas with the textures currently selected in the Project View? All other sprites will be deleted.", "Yes", "No")) { replace = true; // Try to load the material Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find(NGUISettings.atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } if (NGUISettings.atlas == null || NGUISettings.atlas.name != NGUISettings.atlasName) { // Create a new prefab for the atlas Object prefab = (go != null) ? go : PrefabUtility.CreateEmptyPrefab(prefabPath); // Create a new game object for the atlas go = new GameObject(NGUISettings.atlasName); go.AddComponent <UIAtlas>().spriteMaterial = mat; // Update the prefab PrefabUtility.ReplacePrefab(go, prefab); DestroyImmediate(go); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Select the atlas go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; NGUISettings.atlas = go.GetComponent <UIAtlas>(); } } } ComponentSelector.Draw <UIAtlas>("Select", NGUISettings.atlas, OnSelectAtlas, true); List <Texture> textures = GetSelectedTextures(); if (NGUISettings.atlas != null && NGUISettings.atlas.name == NGUISettings.atlasName) { Material mat = NGUISettings.atlas.spriteMaterial; Texture tex = NGUISettings.atlas.texture; // Material information GUILayout.BeginHorizontal(); { if (mat != null) { if (GUILayout.Button("Material", GUILayout.Width(76f))) { Selection.activeObject = mat; } GUILayout.Label(" " + mat.name); } else { GUI.color = Color.grey; GUILayout.Button("Material", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); // Texture atlas information GUILayout.BeginHorizontal(); { if (tex != null) { if (GUILayout.Button("Texture", GUILayout.Width(76f))) { Selection.activeObject = tex; } GUILayout.Label(" " + tex.width + "x" + tex.height); } else { GUI.color = Color.grey; GUILayout.Button("Texture", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(); NGUISettings.atlasPadding = Mathf.Clamp(EditorGUILayout.IntField("Padding", NGUISettings.atlasPadding, GUILayout.Width(100f)), 0, 8); GUILayout.Label((NGUISettings.atlasPadding == 1 ? "pixel" : "pixels") + " in-between of sprites"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasTrimming = EditorGUILayout.Toggle("Trim Alpha", NGUISettings.atlasTrimming, GUILayout.Width(100f)); GUILayout.Label("Remove empty space"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasPMA = EditorGUILayout.Toggle("PMA Shader", NGUISettings.atlasPMA, GUILayout.Width(100f)); GUILayout.Label("Pre-multiply color by alpha"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.unityPacking = EditorGUILayout.Toggle("Unity Packer", NGUISettings.unityPacking, GUILayout.Width(100f)); GUILayout.Label("if off, use a custom packer"); GUILayout.EndHorizontal(); if (!NGUISettings.unityPacking) { GUILayout.BeginHorizontal(); NGUISettings.forceSquareAtlas = EditorGUILayout.Toggle("Force Square", NGUISettings.forceSquareAtlas, GUILayout.Width(100f)); GUILayout.Label("if on, forces a square atlas texture"); GUILayout.EndHorizontal(); } #if UNITY_IPHONE || UNITY_ANDROID GUILayout.BeginHorizontal(); NGUISettings.allow4096 = EditorGUILayout.Toggle("4096x4096", NGUISettings.allow4096, GUILayout.Width(100f)); GUILayout.Label("if off, limit atlases to 2048x2048"); GUILayout.EndHorizontal(); #endif if (NGUISettings.atlas != null && NGUISettings.atlas.name == NGUISettings.atlasName) { if (textures.Count > 0) { GUI.backgroundColor = Color.green; update = GUILayout.Button("Add/Update All"); GUI.backgroundColor = Color.white; } else { EditorGUILayout.HelpBox("You can reveal more options by selecting one or more textures in the Project View window.", MessageType.Info); } } else { EditorGUILayout.HelpBox("You can create a new atlas by selecting one or more textures in the Project View window, then clicking \"Create\".", MessageType.Info); } string selection = null; Dictionary <string, int> spriteList = GetSpriteList(textures); if (spriteList.Count > 0) { NGUIEditorTools.DrawHeader("Sprites", true); { GUILayout.BeginHorizontal(); GUILayout.Space(3f); GUILayout.BeginVertical(); mScroll = GUILayout.BeginScrollView(mScroll); bool delete = false; int index = 0; foreach (KeyValuePair <string, int> iter in spriteList) { ++index; GUILayout.Space(-1f); bool highlight = (UIAtlasInspector.instance != null) && (NGUISettings.selectedSprite == iter.Key); GUI.backgroundColor = highlight ? Color.white : new Color(0.8f, 0.8f, 0.8f); GUILayout.BeginHorizontal("AS TextArea", GUILayout.MinHeight(20f)); GUI.backgroundColor = Color.white; GUILayout.Label(index.ToString(), GUILayout.Width(24f)); if (GUILayout.Button(iter.Key, "OL TextField", GUILayout.Height(20f))) { selection = iter.Key; } if (iter.Value == 2) { GUI.color = Color.green; GUILayout.Label("Add", GUILayout.Width(27f)); GUI.color = Color.white; } else if (iter.Value == 1) { GUI.color = Color.cyan; GUILayout.Label("Update", GUILayout.Width(45f)); GUI.color = Color.white; } else { if (mDelNames.Contains(iter.Key)) { GUI.backgroundColor = Color.red; if (GUILayout.Button("Delete", GUILayout.Width(60f))) { delete = true; } GUI.backgroundColor = Color.green; if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Remove(iter.Key); delete = false; } GUI.backgroundColor = Color.white; } else { // If we have not yet selected a sprite for deletion, show a small "X" button if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Add(iter.Key); } } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(3f); GUILayout.EndHorizontal(); // If this sprite was marked for deletion, remove it from the atlas if (delete) { List <SpriteEntry> sprites = new List <SpriteEntry>(); ExtractSprites(NGUISettings.atlas, sprites); for (int i = sprites.Count; i > 0;) { SpriteEntry ent = sprites[--i]; if (mDelNames.Contains(ent.name)) { sprites.RemoveAt(i); } } UpdateAtlas(NGUISettings.atlas, sprites); mDelNames.Clear(); } else if (update) { UpdateAtlas(textures, true); } else if (replace) { UpdateAtlas(textures, false); } if (NGUISettings.atlas != null && !string.IsNullOrEmpty(selection)) { NGUISettings.selectedSprite = selection; Selection.activeGameObject = NGUISettings.atlas.gameObject; if (UIAtlasInspector.instance != null) { UIAtlasInspector.instance.Repaint(); } } else if (update || replace) { NGUIEditorTools.UpgradeTexturesToSprites(NGUISettings.atlas); } } } // Uncomment this line if you want to be able to force-sort the atlas //if (NGUISettings.atlas != null && GUILayout.Button("Sort Alphabetically")) NGUISettings.atlas.SortAlphabetically(); }
/// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static void ExtractSprites(UIAtlas atlas, List <SpriteEntry> finalSprites) { // Make the atlas texture readable Texture2D atlasTex = NGUIEditorTools.ImportTexture(atlas.texture, true, false, !atlas.premultipliedAlpha); if (atlasTex != null) { Color32[] oldPixels = null; int oldWidth = atlasTex.width; int oldHeight = atlasTex.height; List <UISpriteData> existingSprites = atlas.spriteList; foreach (UISpriteData es in existingSprites) { bool found = false; foreach (SpriteEntry fs in finalSprites) { if (es.name == fs.name) { fs.CopyBorderFrom(es); found = true; break; } } if (!found) { // Read the atlas if (oldPixels == null) { oldPixels = atlasTex.GetPixels32(); } int xmin = Mathf.Clamp(es.x, 0, oldWidth); int ymin = Mathf.Clamp(es.y, 0, oldHeight); int newWidth = Mathf.Clamp(es.width, 0, oldWidth); int newHeight = Mathf.Clamp(es.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) { continue; } Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - (ymin + y)) * oldWidth + (xmin + x); newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(es); sprite.SetRect(0, 0, newWidth, newHeight); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); finalSprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); }
/// <summary> /// Create a list of sprites using the specified list of textures. /// </summary> static List<SpriteEntry> CreateSprites (List<Texture> textures) { List<SpriteEntry> list = new List<SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = NGUIEditorTools.ImportTexture(tex, true, false); if (oldTex == null) continue; // If we aren't doing trimming, just use the texture as-is if (!NGUISettings.atlasTrimming) { SpriteEntry sprite = new SpriteEntry(); sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height); sprite.tex = oldTex; sprite.temporaryTexture = false; list.Add(sprite); continue; } // If we want to trim transparent pixels, there is more work to be done Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; // Find solid pixels for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels[y * xw + x]; if (c.a != 0) { if (y < ymin) ymin = y; if (y > ymax) ymax = y; if (x < xmin) xmin = x; if (x > xmax) xmax = x; } } } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; // If the sprite is empty, don't do anything with it if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height); // If the dimensions match, then nothing was actually trimmed if (newWidth == oldWidth && newHeight == oldHeight) { sprite.tex = oldTex; sprite.temporaryTexture = false; } else { // Copy the non-trimmed texture data into a temporary buffer Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = pixels[oldIndex]; } } // Create a new texture sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = oldTex.name; sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Remember the padding offset sprite.minX = xmin; sprite.maxX = oldWidth - newWidth - xmin; sprite.minY = ymin; sprite.maxY = oldHeight - newHeight - ymin; } list.Add(sprite); } } return list; }
/// <summary> /// Extract the specified sprite from the atlas texture. /// </summary> static SpriteEntry ExtractSprite (UISpriteData es, Color32[] oldPixels, int oldWidth, int oldHeight) { int xmin = Mathf.Clamp(es.x, 0, oldWidth); int ymin = Mathf.Clamp(es.y, 0, oldHeight); int xmax = Mathf.Min(xmin + es.width, oldWidth - 1); int ymax = Mathf.Min(ymin + es.height, oldHeight - 1); int newWidth = Mathf.Clamp(es.width, 0, oldWidth); int newHeight = Mathf.Clamp(es.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) return null; Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { int cy = ymin + y; if (cy > ymax) cy = ymax; for (int x = 0; x < newWidth; ++x) { int cx = xmin + x; if (cx > xmax) cx = xmax; int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - cy) * oldWidth + cx; newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(es); sprite.SetRect(0, 0, newWidth, newHeight); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); return sprite; }
/// <summary> /// Create a list of sprites using the specified list of textures. /// </summary> static List <SpriteEntry> CreateSprites(List <Texture> textures) { List <SpriteEntry> list = new List <SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = NGUIEditorTools.ImportTexture(tex, true, false); if (oldTex == null) { continue; } Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels[y * xw + x]; if (c.a != 0) { if (y < ymin) { ymin = y; } if (y > ymax) { ymax = y; } if (x < xmin) { xmin = x; } if (x > xmax) { xmax = x; } } } } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height); if (newWidth == oldWidth && newHeight == oldHeight) { sprite.tex = oldTex; sprite.temporaryTexture = false; } else { Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = pixels[oldIndex]; } } // Create a new texture sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = oldTex.name; sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Remember the padding offset sprite.minX = xmin; sprite.maxX = oldWidth - newWidth - xmin; sprite.minY = ymin; sprite.maxY = oldHeight - newHeight - ymin; } list.Add(sprite); } } return(list); }
static List <SpriteEntry> CreateSprites(List <Texture> textures) { List <SpriteEntry> list = new List <SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = tex as Texture2D; if (oldTex == null) { continue; } // If we want to trim transparent pixels, there is more work to be done Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; // Find solid pixels for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels[y * xw + x]; if (c.a != 0) { if (y < ymin) { ymin = y; } if (y > ymax) { ymax = y; } if (x < xmin) { xmin = x; } if (x > xmax) { xmax = x; } } } } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; // If the sprite is empty, don't do anything with it if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height); // If the dimensions match, then nothing was actually trimmed if (newWidth == oldWidth && newHeight == oldHeight) { sprite.tex = oldTex; sprite.temporaryTexture = false; } else { // Copy the non-trimmed texture data into a temporary buffer Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = pixels[oldIndex]; } } // Create a new texture sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = oldTex.name; sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Remember the padding offset sprite.minX = xmin; sprite.maxX = oldWidth - newWidth - xmin; sprite.minY = ymin; sprite.maxY = oldHeight - newHeight - ymin; } list.Add(sprite); } } return(list); }
/// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static void ExtractSprites(UIAtlas atlas, List<SpriteEntry> sprites) { // Make the atlas texture readable Texture2D atlasTex = NGUIEditorTools.ImportTexture(atlas.texture, true, false, !atlas.premultipliedAlpha); if (atlasTex != null) { Color32[] oldPixels = null; int oldWidth = atlasTex.width; int oldHeight = atlasTex.height; List<UISpriteData> list = atlas.spriteList; foreach (UISpriteData asp in list) { bool found = false; foreach (SpriteEntry se in sprites) { if (asp.name == se.name) { found = true; break; } } if (!found) { // Read the atlas if (oldPixels == null) oldPixels = atlasTex.GetPixels32(); int xmin = Mathf.Clamp(asp.x, 0, oldWidth); int ymin = Mathf.Clamp(asp.y, 0, oldHeight); int newWidth = Mathf.Clamp(asp.width, 0, oldWidth); int newHeight = Mathf.Clamp(asp.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) continue; Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - (ymin + y)) * oldWidth + (xmin + x); newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(asp); sprite.SetRect(0, 0, newWidth, newHeight); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); sprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); }
private static void WriteSprite(BBData data, SpriteInfo si, StringsChunkBuilder strings, int[] texPagOffsets) { var se = new SpriteEntry { Name = strings.GetOffset(si.Name), Size = si.Size, Bounding = si.Bounding, BBoxMode = si.BBoxMode, Origin = si.Origin, SeparateColMasks = si.SeparateColMasks ? DwordBool.True : DwordBool.False }; var tmp = new BinBuffer(); tmp.Write(se); data.Buffer.Write(tmp, 0, tmp.Size - 8, 0); if (si.Version >= 2) { var se2 = new SpriteEntry2(); unsafe { se2._pad2[0] = -1; se2._pad2[1] = 1; se2._pad2[2] = 0; } se2.funk = si.UnknownFloat; tmp = new BinBuffer(); tmp.Write(se2); data.Buffer.Write(tmp, 0, 0x14, 0x38); } if (si.TextureIndices == null) { data.Buffer.Write(0xFFFFFFFF); } else { data.Buffer.Write(si.TextureIndices.Length); foreach (var ti in si.TextureIndices) { data.Buffer.Write(texPagOffsets[ti]); } if (si.CollisionMasks == null) { data.Buffer.Write(0xFFFFFFFF); } else { data.Buffer.Write((uint)si.CollisionMasks.Length); foreach (var mask in si.CollisionMasks) { int w = mask.GetLength(0); for (int y = 0; y < mask.GetLength(1); y++) { for (int x = 0; x < w; x += 8) { byte b = 0; if (x < w && mask[x, y]) { b |= 0b00000001; } if (x + 1 < w && mask[x + 1, y]) { b |= 0b00000010; } if (x + 2 < w && mask[x + 2, y]) { b |= 0b00000100; } if (x + 3 < w && mask[x + 3, y]) { b |= 0b00001000; } if (x + 4 < w && mask[x + 4, y]) { b |= 0b00010000; } if (x + 5 < w && mask[x + 5, y]) { b |= 0b00100000; } if (x + 6 < w && mask[x + 6, y]) { b |= 0b01000000; } if (x + 7 < w && mask[x + 7, y]) { b |= 0b10000000; } data.Buffer.Write(b); } } } } } Pad(data, 4, 0); }
/// <summary> /// Pack all of the specified sprites into a single texture, updating the outer and inner rects of the sprites as needed. /// </summary> static bool PackTextures(Texture2D tex, List <SpriteEntry> sprites) { Texture2D[] textures = new Texture2D[sprites.Count]; Rect[] rects; #if UNITY_3_5 || UNITY_4_0 int maxSize = 4096; #else int maxSize = SystemInfo.maxTextureSize; #endif #if UNITY_ANDROID || UNITY_IPHONE maxSize = Mathf.Min(maxSize, NGUISettings.allow4096 ? 4096 : 2048); #endif if (NGUISettings.unityPacking) { for (int i = 0; i < sprites.Count; ++i) { textures[i] = sprites[i].tex; } rects = tex.PackTextures(textures, Mathf.Max(NGUISettings.atlasPadding, (NGUISettings.atlasExtendPadding << 1)), maxSize); } else { sprites.Sort(Compare); for (int i = 0; i < sprites.Count; ++i) { textures[i] = sprites[i].tex; } rects = UITexturePacker.PackTextures(tex, textures, 4, 4, Mathf.Max(NGUISettings.atlasPadding, (NGUISettings.atlasExtendPadding << 1)), maxSize); } for (int i = 0; i < sprites.Count; ++i) { Rect rect = NGUIMath.ConvertToPixels(rects[i], tex.width, tex.height, true); // Apparently Unity can take the liberty of destroying temporary textures without any warning if (textures[i] == null) { return(false); } // Make sure that we don't shrink the textures if (Mathf.RoundToInt(rect.width) != textures[i].width) { return(false); } SpriteEntry se = sprites[i]; se.x = Mathf.RoundToInt(rect.x); se.y = Mathf.RoundToInt(rect.y); se.width = Mathf.RoundToInt(rect.width); se.height = Mathf.RoundToInt(rect.height); } if (NGUISettings.atlasExtendPadding > 0) { Color32[] colors = tex.GetPixels32(); int texWidth = tex.width; int texHeight = tex.height; for (int i = 0; i < sprites.Count; ++i) { SpriteEntry se = sprites[i]; int xMin = se.x; int xMax = se.x + se.width - 1; // Y-axis is inverted int yMin = texHeight - se.y - se.height; int yMax = texHeight - se.y - 1; for (int x = xMin - NGUISettings.atlasExtendPadding; x <= xMax + NGUISettings.atlasExtendPadding; ++x) { for (int y = yMin - NGUISettings.atlasExtendPadding; y <= yMax + NGUISettings.atlasExtendPadding; ++y) { if (x < 0 || x >= texWidth || y < 0 || y >= texHeight) { // Out of tex bounds continue; } if (x >= xMin && x <= xMax && y >= yMin && y <= yMax) { // In sprite bounds continue; } int baseX = Mathf.Clamp(x, xMin, xMax); int baseY = Mathf.Clamp(y, yMin, yMax); int index = y * texWidth + x; int baseIndex = baseY * texWidth + baseX; colors[index] = colors[baseIndex]; } } } tex.SetPixels32(colors); } return(true); }
static UIAtlas.Sprite AddSprite(ICollection<UIAtlas.Sprite> sprites, SpriteEntry se) { UIAtlas.Sprite sprite = sprites.FirstOrDefault(sp => sp.name == se.Tex.name); // See if this sprite already exists if (sprite != null) { float x0 = sprite.inner.xMin - sprite.outer.xMin; float y0 = sprite.inner.yMin - sprite.outer.yMin; float x1 = sprite.outer.xMax - sprite.inner.xMax; float y1 = sprite.outer.yMax - sprite.inner.yMax; sprite.outer = se.Rect; sprite.inner = se.Rect; sprite.inner.xMin = Mathf.Max(sprite.inner.xMin + x0, sprite.outer.xMin); sprite.inner.yMin = Mathf.Max(sprite.inner.yMin + y0, sprite.outer.yMin); sprite.inner.xMax = Mathf.Min(sprite.inner.xMax - x1, sprite.outer.xMax); sprite.inner.yMax = Mathf.Min(sprite.inner.yMax - y1, sprite.outer.yMax); } else { sprite = new UIAtlas.Sprite {name = se.Tex.name, outer = se.Rect, inner = se.Rect}; sprites.Add(sprite); } float width = Mathf.Max(1f, sprite.outer.width); float height = Mathf.Max(1f, sprite.outer.height); // Sprite's padding values are relative to width and height sprite.paddingLeft = se.MinX / width; sprite.paddingRight = se.MaxX / width; sprite.paddingTop = se.MaxY / height; sprite.paddingBottom = se.MinY / height; return sprite; }
/// <summary> /// Draw the UI for this tool. /// </summary> void OnGUI() { if (mLastAtlas != NGUISettings.atlas) { mLastAtlas = NGUISettings.atlas; } bool update = false; bool replace = false; NGUIEditorTools.SetLabelWidth(84f); GUILayout.Space(3f); NGUIEditorTools.DrawHeader("Input", true); NGUIEditorTools.BeginContents(false); GUILayout.BeginHorizontal(); { ComponentSelector.Draw <UIAtlas>("Atlas", NGUISettings.atlas, OnSelectAtlas, true, GUILayout.MinWidth(80f)); EditorGUI.BeginDisabledGroup(NGUISettings.atlas == null); if (GUILayout.Button("New", GUILayout.Width(40f))) { NGUISettings.atlas = null; } EditorGUI.EndDisabledGroup(); } GUILayout.EndHorizontal(); List <Texture> textures = GetSelectedTextures(); if (NGUISettings.atlas != null) { Material mat = NGUISettings.atlas.spriteMaterial; Texture tex = NGUISettings.atlas.texture; // Material information GUILayout.BeginHorizontal(); { if (mat != null) { if (GUILayout.Button("Material", GUILayout.Width(76f))) { Selection.activeObject = mat; } GUILayout.Label(" " + mat.name); } else { GUI.color = Color.grey; GUILayout.Button("Material", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); // Texture atlas information GUILayout.BeginHorizontal(); { if (tex != null) { if (GUILayout.Button("Texture", GUILayout.Width(76f))) { Selection.activeObject = tex; } GUILayout.Label(" " + tex.width + "x" + tex.height); } else { GUI.color = Color.grey; GUILayout.Button("Texture", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(); NGUISettings.atlasPadding = Mathf.Clamp(EditorGUILayout.IntField("Padding", NGUISettings.atlasPadding, GUILayout.Width(100f)), 0, 8); GUILayout.Label((NGUISettings.atlasPadding == 1 ? "pixel" : "pixels") + " between sprites"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasTrimming = EditorGUILayout.Toggle("Trim Alpha", NGUISettings.atlasTrimming, GUILayout.Width(100f)); GUILayout.Label("Remove empty space"); GUILayout.EndHorizontal(); bool fixedShader = false; if (NGUISettings.atlas != null) { Material mat = NGUISettings.atlas.spriteMaterial; if (mat != null) { Shader shader = mat.shader; if (shader != null) { if (shader.name == "Unlit/Transparent Colored") { NGUISettings.atlasPMA = false; fixedShader = true; } else if (shader.name == "Unlit/Premultiplied Colored") { NGUISettings.atlasPMA = true; fixedShader = true; } } } } if (!fixedShader) { GUILayout.BeginHorizontal(); NGUISettings.atlasPMA = EditorGUILayout.Toggle("PMA Shader", NGUISettings.atlasPMA, GUILayout.Width(100f)); GUILayout.Label("Pre-multiplied alpha", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); } //GUILayout.BeginHorizontal(); //NGUISettings.keepPadding = EditorGUILayout.Toggle("Keep Padding", NGUISettings.keepPadding, GUILayout.Width(100f)); //GUILayout.Label("or replace with trimmed pixels", GUILayout.MinWidth(70f)); //GUILayout.EndHorizontal(); #if !UNITY_5_6 GUILayout.BeginHorizontal(); NGUISettings.unityPacking = EditorGUILayout.Toggle("Unity Packer", NGUISettings.unityPacking, GUILayout.Width(100f)); GUILayout.Label("or custom packer", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); #endif GUILayout.BeginHorizontal(); NGUISettings.trueColorAtlas = EditorGUILayout.Toggle("Truecolor", NGUISettings.trueColorAtlas, GUILayout.Width(100f)); GUILayout.Label("force ARGB32 textures", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.autoUpgradeSprites = EditorGUILayout.Toggle("Auto-upgrade", NGUISettings.autoUpgradeSprites, GUILayout.Width(100f)); GUILayout.Label("replace textures with sprites", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); #if !UNITY_5_6 if (!NGUISettings.unityPacking) { GUILayout.BeginHorizontal(); NGUISettings.forceSquareAtlas = EditorGUILayout.Toggle("Force Square", NGUISettings.forceSquareAtlas, GUILayout.Width(100f)); GUILayout.Label("if on, forces a square atlas texture", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); } #endif #if UNITY_IPHONE || UNITY_ANDROID GUILayout.BeginHorizontal(); NGUISettings.allow4096 = EditorGUILayout.Toggle("4096x4096", NGUISettings.allow4096, GUILayout.Width(100f)); GUILayout.Label("if off, limit atlases to 2048x2048"); GUILayout.EndHorizontal(); #endif NGUIEditorTools.EndContents(); if (NGUISettings.atlas != null) { GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (textures.Count > 0) { update = GUILayout.Button("Add/Update"); } else if (GUILayout.Button("View Sprites")) { SpriteSelector.ShowSelected(); } GUILayout.Space(20f); GUILayout.EndHorizontal(); } else { EditorGUILayout.HelpBox("You can create a new atlas by selecting one or more textures in the Project View window, then clicking \"Create\".", MessageType.Info); EditorGUI.BeginDisabledGroup(textures.Count == 0); GUILayout.BeginHorizontal(); GUILayout.Space(20f); bool create = GUILayout.Button("Create"); GUILayout.Space(20f); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); if (create) { string path = EditorUtility.SaveFilePanelInProject("Save As", "New Atlas.prefab", "prefab", "Save atlas as...", NGUISettings.currentPath); if (!string.IsNullOrEmpty(path)) { NGUISettings.currentPath = System.IO.Path.GetDirectoryName(path); GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; string matPath = path.Replace(".prefab", ".mat"); replace = true; // Try to load the material Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find(NGUISettings.atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } // Create a new prefab for the atlas Object prefab = (go != null) ? go : PrefabUtility.CreateEmptyPrefab(path); // Create a new game object for the atlas string atlasName = path.Replace(".prefab", ""); atlasName = atlasName.Substring(path.LastIndexOfAny(new char[] { '/', '\\' }) + 1); go = new GameObject(atlasName); go.AddComponent <UIAtlas>().spriteMaterial = mat; // Update the prefab PrefabUtility.ReplacePrefab(go, prefab); DestroyImmediate(go); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Select the atlas go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; NGUISettings.atlas = go.GetComponent <UIAtlas>(); Selection.activeGameObject = go; } } } string selection = null; Dictionary <string, int> spriteList = GetSpriteList(textures); if (spriteList.Count > 0) { NGUIEditorTools.DrawHeader("Sprites", true); { GUILayout.BeginHorizontal(); GUILayout.Space(3f); GUILayout.BeginVertical(); mScroll = GUILayout.BeginScrollView(mScroll); bool delete = false; int index = 0; foreach (KeyValuePair <string, int> iter in spriteList) { ++index; GUILayout.Space(-1f); bool highlight = (UIAtlasInspector.instance != null) && (NGUISettings.selectedSprite == iter.Key); GUI.backgroundColor = highlight ? Color.white : new Color(0.8f, 0.8f, 0.8f); GUILayout.BeginHorizontal(NGUIEditorTools.textArea, GUILayout.MinHeight(20f)); GUI.backgroundColor = Color.white; GUILayout.Label(index.ToString(), GUILayout.Width(24f)); if (GUILayout.Button(iter.Key, "OL TextField", GUILayout.Height(20f))) { selection = iter.Key; } if (iter.Value == 2) { GUI.color = Color.green; GUILayout.Label("Add", GUILayout.Width(27f)); GUI.color = Color.white; } else if (iter.Value == 1) { GUI.color = Color.cyan; GUILayout.Label("Update", GUILayout.Width(45f)); GUI.color = Color.white; } else { if (mDelNames.Contains(iter.Key)) { GUI.backgroundColor = Color.red; if (GUILayout.Button("Delete", GUILayout.Width(60f))) { delete = true; } GUI.backgroundColor = Color.green; if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Remove(iter.Key); delete = false; } GUI.backgroundColor = Color.white; } else { // If we have not yet selected a sprite for deletion, show a small "X" button if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Add(iter.Key); } } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(3f); GUILayout.EndHorizontal(); // If this sprite was marked for deletion, remove it from the atlas if (delete) { List <SpriteEntry> sprites = new List <SpriteEntry>(); ExtractSprites(NGUISettings.atlas, sprites); for (int i = sprites.Count; i > 0;) { SpriteEntry ent = sprites[--i]; if (mDelNames.Contains(ent.name)) { sprites.RemoveAt(i); } } UpdateAtlas(NGUISettings.atlas, sprites); mDelNames.Clear(); NGUIEditorTools.RepaintSprites(); } else if (update) { UpdateAtlas(textures, true); } else if (replace) { UpdateAtlas(textures, false); } if (NGUISettings.atlas != null && !string.IsNullOrEmpty(selection)) { NGUIEditorTools.SelectSprite(selection); } else if (NGUISettings.autoUpgradeSprites && (update || replace)) { NGUIEditorTools.UpgradeTexturesToSprites(NGUISettings.atlas); NGUIEditorTools.RepaintSprites(); } } } if (NGUISettings.atlas != null && textures.Count == 0) { EditorGUILayout.HelpBox("You can reveal more options by selecting one or more textures in the Project View window.", MessageType.Info); } // Uncomment this line if you want to be able to force-sort the atlas //if (NGUISettings.atlas != null && GUILayout.Button("Sort Alphabetically")) NGUISettings.atlas.SortAlphabetically(); }
private void RenderSpriteLine() { List <SpriteEntry> entries = new List <SpriteEntry>(); int tolerance = ObjectSize ? 16 : 8; // Get all entries from OAM ram for (int i = 0; i < 40; i++) { SpriteEntry entry = new SpriteEntry(); entry.Y = oam[i * 4] - 16; entry.X = oam[i * 4 + 1] - 8; entry.TileNumber = oam[i * 4 + 2]; entry.Attributes = oam[i * 4 + 3]; entry.TableIndex = i; // Is this sprite in the renderable area? if (entry.X < 160 && entry.Y < 144 && entry.Y > LY - tolerance && entry.Y < LY + 1) { entries.Add(entry); } } // TODO: Sort (out) // Render foreach (SpriteEntry entry in entries) { int displacementy = LY - entry.Y; int colorpalette = entry.Attributes & 7; int tilenumber = entry.TileNumber; int tilebank = (entry.Attributes >> 3) & 1; int palette = (entry.Attributes & 0x10) == 0x10 ? OBP1 : OBP0; bool flipx = (entry.Attributes & 0x20) == 0x20; bool flipy = (entry.Attributes & 0x40) == 0x40; bool behind = (entry.Attributes & 0x80) == 0x80; uint[] tileline; if (ObjectSize) // size is 8 * 16 { if (displacementy > 7) { // use the second tile tilenumber |= 1; displacementy -= 8; } else { // use the first tile tilenumber &= 0xFE; } } if (flipy) { displacementy = 7 - displacementy; } if (rom.HasColorFeatures) { int attributes = colorpalette | (tilebank << 3); tileline = ReadTileLineColor(tilenumber * 16, displacementy, attributes, pram2, true); } else { tileline = ReadTileLine(tilenumber * 16, displacementy, palette, true); } if (flipx) { Array.Reverse(tileline); } DrawTile(tileline, entry.X, LY); } }
/// <summary> /// Draw the UI for this tool. /// </summary> void OnGUI() { bool create = false; bool update = false; bool replace = false; string prefabPath = ""; string matPath = ""; // If we have an atlas to work with, see if we can figure out the path for it and its material if (NGUISettings.atlas != null && NGUISettings.atlas.name == NGUISettings.atlasName) { prefabPath = AssetDatabase.GetAssetPath(NGUISettings.atlas.gameObject.GetInstanceID()); if (NGUISettings.atlas.spriteMaterial != null) { matPath = AssetDatabase.GetAssetPath(NGUISettings.atlas.spriteMaterial.GetInstanceID()); } } // Assume default values if needed if (string.IsNullOrEmpty(NGUISettings.atlasName)) { NGUISettings.atlasName = "New Atlas"; } if (string.IsNullOrEmpty(prefabPath)) { prefabPath = NGUIEditorTools.GetSelectionFolder() + NGUISettings.atlasName + ".prefab"; } if (string.IsNullOrEmpty(matPath)) { matPath = NGUIEditorTools.GetSelectionFolder() + NGUISettings.atlasName + ".mat"; } // Try to load the prefab GameObject go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; if (NGUISettings.atlas == null && go != null) { NGUISettings.atlas = go.GetComponent <UIAtlas>(); } EditorGUIUtility.LookLikeControls(80f); GUILayout.Space(6f); GUILayout.BeginHorizontal(); if (go == null) { GUI.backgroundColor = Color.green; create = GUILayout.Button("Create", GUILayout.Width(76f)); } else { GUI.backgroundColor = Color.red; create = GUILayout.Button("Replace", GUILayout.Width(76f)); } GUI.backgroundColor = Color.white; NGUISettings.atlasName = GUILayout.TextField(NGUISettings.atlasName); GUILayout.EndHorizontal(); if (create) { // If the prefab already exists, confirm that we want to overwrite it if (go == null || EditorUtility.DisplayDialog("Are you sure?", "Are you sure you want to replace the contents of the " + NGUISettings.atlasName + " atlas with the textures currently selected in the Project View? All other sprites will be deleted.", "Yes", "No")) { replace = true; // Try to load the material Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } if (NGUISettings.atlas == null || NGUISettings.atlas.name != NGUISettings.atlasName) { // Create a new prefab for the atlas #if UNITY_3_4 Object prefab = (go != null) ? go : EditorUtility.CreateEmptyPrefab(prefabPath); #else Object prefab = (go != null) ? go : PrefabUtility.CreateEmptyPrefab(prefabPath); #endif // Create a new game object for the atlas go = new GameObject(NGUISettings.atlasName); go.AddComponent <UIAtlas>().spriteMaterial = mat; // Update the prefab #if UNITY_3_4 EditorUtility.ReplacePrefab(go, prefab); #else PrefabUtility.ReplacePrefab(go, prefab); #endif DestroyImmediate(go); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Select the atlas go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; NGUISettings.atlas = go.GetComponent <UIAtlas>(); } } } ComponentSelector.Draw <UIAtlas>("Select", NGUISettings.atlas, OnSelectAtlas); List <Texture> textures = GetSelectedTextures(); if (NGUISettings.atlas != null && NGUISettings.atlas.name == NGUISettings.atlasName) { Material mat = NGUISettings.atlas.spriteMaterial; Texture tex = NGUISettings.atlas.texture; // Material information GUILayout.BeginHorizontal(); { if (mat != null) { if (GUILayout.Button("Material", GUILayout.Width(76f))) { Selection.activeObject = mat; } GUILayout.Label(" " + mat.name); } else { GUI.color = Color.grey; GUILayout.Button("Material", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); // Texture atlas information GUILayout.BeginHorizontal(); { if (tex != null) { if (GUILayout.Button("Texture", GUILayout.Width(76f))) { Selection.activeObject = tex; } GUILayout.Label(" " + tex.width + "x" + tex.height); } else { GUI.color = Color.grey; GUILayout.Button("Texture", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasPadding = Mathf.Clamp(EditorGUILayout.IntField("Padding", NGUISettings.atlasPadding, GUILayout.Width(100f)), 0, 8); GUILayout.Label("in pixels in-between of sprites"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasTrimming = EditorGUILayout.Toggle("Trim Alpha", NGUISettings.atlasTrimming, GUILayout.Width(100f)); GUILayout.Label("Remove empty space"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.unityPacking = EditorGUILayout.Toggle("Unity Packer", NGUISettings.unityPacking, GUILayout.Width(100f)); GUILayout.Label("if off, use a custom packer"); GUILayout.EndHorizontal(); if (textures.Count > 0) { GUI.backgroundColor = Color.green; update = GUILayout.Button("Add/Update All"); GUI.backgroundColor = Color.white; } else { NGUIEditorTools.DrawSeparator(); GUILayout.Label("You can reveal more options by selecting\none or more textures in the Project View\nwindow."); } } else { NGUIEditorTools.DrawSeparator(); GUILayout.Label("You can create a new atlas by selecting\none or more textures in the Project View\nwindow, then clicking \"Create\"."); } Dictionary <string, int> spriteList = GetSpriteList(textures); if (spriteList.Count > 0) { NGUIEditorTools.DrawHeader("Sprites"); GUILayout.Space(-7f); mScroll = GUILayout.BeginScrollView(mScroll); bool delete = false; int index = 0; foreach (KeyValuePair <string, int> iter in spriteList) { ++index; NGUIEditorTools.HighlightLine(new Color(0.6f, 0.6f, 0.6f)); GUILayout.BeginHorizontal(); GUILayout.Label(index.ToString(), GUILayout.Width(24f)); GUILayout.Label(iter.Key); if (iter.Value == 2) { GUI.color = Color.green; GUILayout.Label("Add", GUILayout.Width(27f)); GUI.color = Color.white; } else if (iter.Value == 1) { GUI.color = Color.cyan; GUILayout.Label("Update", GUILayout.Width(45f)); GUI.color = Color.white; } else { if (mDelNames.Contains(iter.Key)) { GUI.backgroundColor = Color.red; if (GUILayout.Button("Delete", GUILayout.Width(60f))) { delete = true; } GUI.backgroundColor = Color.green; if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Remove(iter.Key); delete = false; } GUI.backgroundColor = Color.white; } else { // If we have not yet selected a sprite for deletion, show a small "X" button if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Add(iter.Key); } } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); // If this sprite was marked for deletion, remove it from the atlas if (delete) { List <SpriteEntry> sprites = new List <SpriteEntry>(); ExtractSprites(NGUISettings.atlas, sprites); for (int i = sprites.Count; i > 0;) { SpriteEntry ent = sprites[--i]; if (mDelNames.Contains(ent.tex.name)) { sprites.RemoveAt(i); } } UpdateAtlas(NGUISettings.atlas, sprites); mDelNames.Clear(); } else if (update) { UpdateAtlas(textures, true); } else if (replace) { UpdateAtlas(textures, false); } return; } }
/// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static public void ExtractSprites (UIAtlas atlas, List<SpriteEntry> finalSprites) { // Make the atlas texture readable Texture2D atlasTex = NGUIEditorTools.ImportTexture(atlas.texture, true, false, !atlas.premultipliedAlpha); if (atlasTex != null) { Color32[] oldPixels = null; int oldWidth = atlasTex.width; int oldHeight = atlasTex.height; List<UISpriteData> existingSprites = atlas.spriteList; foreach (UISpriteData es in existingSprites) { bool found = false; foreach (SpriteEntry fs in finalSprites) { if (es.name == fs.name) { fs.CopyBorderFrom(es); found = true; break; } } if (!found) { // Read the atlas if (oldPixels == null) oldPixels = atlasTex.GetPixels32(); int xmin = Mathf.Clamp(es.x, 0, oldWidth); int ymin = Mathf.Clamp(es.y, 0, oldHeight); int xmax = Mathf.Min(xmin + es.width, oldWidth - 1); int ymax = Mathf.Min(ymin + es.height, oldHeight - 1); int newWidth = Mathf.Clamp(es.width, 0, oldWidth); int newHeight = Mathf.Clamp(es.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) continue; Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { int cy = ymin + y; if (cy > ymax) cy = ymax; for (int x = 0; x < newWidth; ++x) { int cx = xmin + x; if (cx > xmax) cx = xmax; int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - cy) * oldWidth + cx; newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(es); sprite.SetRect(0, 0, newWidth, newHeight); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); finalSprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); }
/// <summary> /// Create a list of sprites using the specified list of textures. /// </summary> static List<SpriteEntry> CreateSprites(List<Texture> textures) { List<SpriteEntry> list = new List<SpriteEntry>(); foreach (Texture tex in textures) { Texture2D oldTex = NGUIEditorTools.ImportTexture(tex, true, false); if (oldTex == null) continue; Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels[y * xw + x]; if (c.a != 0) { if (y < ymin) ymin = y; if (y > ymax) ymax = y; if (x < xmin) xmin = x; if (x > xmax) xmax = x; } } } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.rect = new Rect(0f, 0f, oldTex.width, oldTex.height); if (newWidth == oldWidth && newHeight == oldHeight) { sprite.tex = oldTex; sprite.temporaryTexture = false; } else { Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = pixels[oldIndex]; } } // Create a new texture sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = oldTex.name; sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Remember the padding offset sprite.minX = xmin; sprite.maxX = oldWidth - newWidth - xmin; sprite.minY = ymin; sprite.maxY = oldHeight - newHeight - ymin; } list.Add(sprite); } } return list; }
/// <summary> /// Add the specified texture to the atlas, or update an existing one. /// </summary> static public void AddOrUpdate (UIAtlas atlas, SpriteEntry se) { if (atlas != null && se != null) { List<SpriteEntry> sprites = new List<SpriteEntry>(); sprites.Add(se); ExtractSprites(atlas, sprites); UpdateAtlas(atlas, sprites); } }
/// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static void ExtractSprites(UIAtlas atlas, List <SpriteEntry> sprites) { // Make the atlas texture readable Texture2D atlasTex = NGUIEditorTools.ImportTexture(atlas.texture, true, false); if (atlasTex != null) { atlas.coordinates = UIAtlas.Coordinates.Pixels; Color32[] oldPixels = null; int oldWidth = atlasTex.width; int oldHeight = atlasTex.height; List <UIAtlas.Sprite> list = atlas.spriteList; foreach (UIAtlas.Sprite asp in list) { bool found = false; foreach (SpriteEntry se in sprites) { if (asp.name == se.tex.name) { found = true; break; } } if (!found) { // Read the atlas if (oldPixels == null) { oldPixels = atlasTex.GetPixels32(); } Rect rect = asp.outer; rect.xMin = Mathf.Clamp(rect.xMin, 0f, oldWidth); rect.yMin = Mathf.Clamp(rect.yMin, 0f, oldHeight); rect.xMax = Mathf.Clamp(rect.xMax, 0f, oldWidth); rect.yMax = Mathf.Clamp(rect.yMax, 0f, oldHeight); int newWidth = Mathf.RoundToInt(rect.width); int newHeight = Mathf.RoundToInt(rect.height); if (newWidth == 0 || newHeight == 0) { continue; } Color32[] newPixels = new Color32[newWidth * newHeight]; int xmin = Mathf.RoundToInt(rect.x); int ymin = Mathf.RoundToInt(oldHeight - rect.yMax); for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = asp.name; sprite.rect = new Rect(0f, 0f, newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Min/max coordinates are in pixels sprite.minX = Mathf.RoundToInt(asp.paddingLeft * newWidth); sprite.maxX = Mathf.RoundToInt(asp.paddingRight * newWidth); sprite.minY = Mathf.RoundToInt(asp.paddingBottom * newHeight); sprite.maxY = Mathf.RoundToInt(asp.paddingTop * newHeight); sprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false); }
/// <summary> /// Extract sprites from the atlas, adding them to the list. /// </summary> static void ExtractSprites (UIAtlas atlas, List<SpriteEntry> sprites) { // Make the atlas texture readable Texture2D atlasTex = NGUIEditorTools.ImportTexture(atlas.texture, true, false); if (atlasTex != null) { atlas.coordinates = UIAtlas.Coordinates.Pixels; Color32[] oldPixels = null; int oldWidth = atlasTex.width; int oldHeight = atlasTex.height; List<UIAtlas.Sprite> list = atlas.spriteList; foreach (UIAtlas.Sprite asp in list) { bool found = false; foreach (SpriteEntry se in sprites) { if (asp.name == se.tex.name) { found = true; break; } } if (!found) { // Read the atlas if (oldPixels == null) oldPixels = atlasTex.GetPixels32(); Rect rect = asp.outer; rect.xMin = Mathf.Clamp(rect.xMin, 0f, oldWidth); rect.yMin = Mathf.Clamp(rect.yMin, 0f, oldHeight); rect.xMax = Mathf.Clamp(rect.xMax, 0f, oldWidth); rect.yMax = Mathf.Clamp(rect.yMax, 0f, oldHeight); int newWidth = Mathf.RoundToInt(rect.width); int newHeight = Mathf.RoundToInt(rect.height); if (newWidth == 0 || newHeight == 0) continue; Color32[] newPixels = new Color32[newWidth * newHeight]; int xmin = Mathf.RoundToInt(rect.x); int ymin = Mathf.RoundToInt(oldHeight - rect.yMax); for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); newPixels[newIndex] = oldPixels[oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.temporaryTexture = true; sprite.tex = new Texture2D(newWidth, newHeight); sprite.tex.name = asp.name; sprite.rect = new Rect(0f, 0f, newWidth, newHeight); sprite.tex.SetPixels32(newPixels); sprite.tex.Apply(); // Min/max coordinates are in pixels sprite.minX = Mathf.RoundToInt(asp.paddingLeft * newWidth); sprite.maxX = Mathf.RoundToInt(asp.paddingRight * newWidth); sprite.minY = Mathf.RoundToInt(asp.paddingBottom * newHeight); sprite.maxY = Mathf.RoundToInt(asp.paddingTop * newHeight); sprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false); }
private void FindAllEntries(BuildTarget buildTarget, List <SpriteEntry> spriteEntries, List <AtlasEntry> atlasEntries) { var platformString = GetPlatformString(buildTarget); foreach (var guid in AssetDatabase.FindAssets("t:Texture")) { var path = AssetDatabase.GUIDToAssetPath(guid); var importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer == null) { continue; } // 获取sprite列表 var sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(path) .Distinct() .OfType <Sprite>() .Where(x => x.packed) .ToArray(); for (var i = 0; i < sprites.Length; ++i) { var sprite = sprites[i]; Texture2D atlasTexture; string atlasName; Packer.GetAtlasDataForSprite(sprite, out atlasName, out atlasTexture); if (atlasTexture != null) { var entry = new SpriteEntry { Path = path, Sprite = sprite, Importer = importer, Texture = SpriteUtility.GetSpriteTexture(sprite, false), AtlasName = atlasName, Uvs = SpriteUtility.GetSpriteUVs(sprite, false), AtlasUvs = SpriteUtility.GetSpriteUVs(sprite, true), AtlasTexture = atlasTexture, }; spriteEntries.Add(entry); } } } // 获取atlas列表 var atlasGroups = from e in spriteEntries group e by e.AtlasTexture; foreach (var atlasGroup in atlasGroups) { var tex = atlasGroup.Key; var texName = tex.name; // 检查是否需要分离alpha通道 var atlasName = string.Empty; var needSeparateAlpha = false; foreach (var spriteEntry in atlasGroup) { var importer = spriteEntry.Importer; atlasName = importer.spritePackingTag; if (!string.IsNullOrEmpty(atlasName)) { var settings = importer.GetPlatformTextureSettings(platformString); var format = settings.format; if (format == TextureImporterFormat.Automatic) { format = importer.GetAutomaticFormat(platformString); } needSeparateAlpha = TextureUtility.IsTransparent(format); } } if (CustomAtlasConfig.ShouldKeepAlpha(atlasName)) { needSeparateAlpha = false; } var entry = new AtlasEntry { Name = texName, Texture = tex, SpriteEntries = atlasGroup.ToList(), NeedSeparateAlpha = needSeparateAlpha, }; atlasEntries.Add(entry); } }