private void BuildSingleAtlas() { texList = BuildTextureList(matSpriteMap[numAtlasesBuilt]); if (texList == null) { updateDelegate = UpdateBuildEnd; return; } // Make sure there are textures in the list: if (texList.textures.Count < 1) { Debug.LogWarning("Warning: No textures found for material \"" + texList.material.name + "\"."); --numAtlasesBuilt; } else { BuildAtlas(texList); } // Now assign the frame info: SetFrameInfo((MaterialSpriteList)matSpriteMap[numAtlasesBuilt], texList); // Save the atlas path: matSpriteMap[numAtlasesBuilt].texPath = texList.texPath; texList = null; // Try to free some memory: System.GC.Collect(); #if !UNITY_IPHONE || (UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9) Resources.UnloadUnusedAssets(); #endif ++numAtlasesBuilt; progress = (float)numAtlasesBuilt / (float)matSpriteMap.Count; if (numAtlasesBuilt < matSpriteMap.Count) { // set message one step prior since each step is non-threaded // we need to update message for next step prior to starting message = "Building atlas for " + matSpriteMap[numAtlasesBuilt].material.name; } else { // set message one step prior since each step is non-threaded // we need to update message for next step prior to starting message = "Importing generated atlases"; updateDelegate = UpdateBuildEnd; } }
// Assigns all frame-related information to // the sprites of an atlas: void SetFrameInfo(MaterialSpriteList msMap, AtlasTextureList texList) { SPRITE_FRAME frameInfo; ISpriteAggregator spAg; AutoSpriteBase spr; int index; for (int i = 0; i < msMap.sprites.Count; ++i) { spAg = (ISpriteAggregator)msMap.sprites[i]; // Assign each frame its UVs: for (int j = 0; j < spAg.SpriteFrames.Length; ++j) { // Validate that a texture has been selected: if (spAg.SourceTextures[j] == null) { // Set the frame size to zero to flag it // as a "null" frame: spAg.SpriteFrames[j].uvs = new Rect(0, 0, 0, 0); continue; } index = texList.textures.IndexOf(spAg.SourceTextures[j]); if (index < 0) { Debug.LogError("Error assigning frame info: frame " + j + " on sprite \"" + ((SpriteRoot)spAg).name + "\" not found in the texture list!"); continue; } frameInfo = (SPRITE_FRAME)texList.frames[index]; frameInfo.uvs = texList.uvs[index]; spAg.SpriteFrames[j].Copy(frameInfo); } spr = (AutoSpriteBase)spAg; // Update our sprite's appearance: if (spr.spriteMesh != null) { if (spr.DefaultFrame != null) { spr.SetUVs(spr.DefaultFrame.uvs); } } EditorUtility.SetDirty(spr.gameObject); } }
// Finds all textures which are to be part // of a given atlas: private AtlasTextureList BuildTextureList(MaterialSpriteList msMap) { ISpriteAggregator spr; Texture2D newTex = null; SPRITE_FRAME frameInfo = new SPRITE_FRAME(0); AtlasTextureList texList = new AtlasTextureList(); texList.material = msMap.material; // Get all the textures pointed to by the sprites: for (int i = 0; i < msMap.sprites.Count; ++i) { spr = (ISpriteAggregator)msMap.sprites[i]; spr.Aggregate(AssetDatabase.GUIDToAssetPath, AssetDatabase.LoadAssetAtPath, AssetDatabase.AssetPathToGUID); // Get the sprite ready to give us its textures if (spr.SourceTextures.Length <= 0) { Debug.LogError("Null texture reference detected on sprite \"" + ((Component)spr).name + "\"! Please verify that this was intentional."); return(null); } for (int j = 0; j < spr.SourceTextures.Length; ++j) { if (spr.SourceTextures[j] != null) { // See if the texture is already in our list: if (-1 >= texList.textures.IndexOf(spr.SourceTextures[j])) { VerifyImportSettings(spr.SourceTextures[j]); texList.textures.Add(spr.SourceTextures[j]); ProcessFrame(spr.SourceTextures[j], spr.DoNotTrimImages, ref newTex, ref frameInfo); texList.trimmedTextures.Add(newTex); texList.frames.Add(frameInfo); } } else { Debug.LogError("Null texture reference detected on sprite \"" + ((Component)spr).name + "\"! Please verify that this was intentional."); #if WARN_ON_NULL_TEXTURE Debug.LogWarning("Null texture reference detected on sprite \"" + ((Component)spr).name + "\"! Please verify that this was intentional."); #endif } } } return(texList); }
// Assigns all frame-related information to // the sprites of an atlas: void SetFrameInfo(MaterialSpriteList msMap, AtlasTextureList texList) { SPRITE_FRAME frameInfo; ISpriteAggregator spAg; int index; for (int i = 0; i < msMap.sprites.Count; ++i) { spAg = msMap.sprites[i]; // Assign each frame its UVs: for (int j = 0; j < spAg.SpriteFrames.Length; ++j) { // Validate that a texture has been selected: if (spAg.SourceTextures[j] == null) { // Set the frame size to zero to flag it // as a "null" frame: spAg.SpriteFrames[j].uvs = new Rect(0, 0, 0, 0); continue; } index = texList.textures.IndexOf(spAg.SourceTextures[j]); if (index < 0) { Debug.LogError("Error assigning frame info: frame " + j + " on object \"" + ((Component)spAg).name + "\" not found in the texture list!"); continue; } frameInfo = texList.frames[index]; frameInfo.uvs = texList.uvs[index]; spAg.SpriteFrames[j].Copy(frameInfo); } // Update our sprite's appearance: if ( !(spAg is AutoSpriteBase && ((AutoSpriteBase)spAg).spriteMesh == null) ) { if (spAg.DefaultFrame != null) { spAg.SetUVs(spAg.DefaultFrame.uvs); } } EditorUtility.SetDirty((Component)spAg); } }
// Builds an atlas from the specified texture list void BuildAtlas(AtlasTextureList texList) { Texture2D atlas; atlas = new Texture2D(4, 4); atlas.name = texList.material.name; //============================================= // The following is currently disabled as it // does not seem to work around the Unity bug: //============================================= // To get around the Unity bug where the max texture size is // limited to 1024x1024 when in iPhone mode for newly-created // textures, save this texture to disk as an asset and then // re-import it with modified import settings to set the max // texture size to what we want: byte[] bytes;// = atlas.EncodeToPNG(); string atlasPath = assetPath + "/" + atlas.name + ".png"; // string projectRelativePath = "Assets/" + atlasFolder + "/" + atlas.name + ".png"; // // // Write out the atlas file: // using (FileStream fs = File.Create(atlasPath)) // { // fs.Write(bytes, 0, bytes.Length); // } // // // Make sure the max texture size is set to what we want: // // Get the texture's importer: // TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(projectRelativePath); // if (importer.maxTextureSize < maxAtlasSize || importer.isReadable) // { // // Re-import it with the desired max texture size: // importer.maxTextureSize = maxAtlasSize; // importer.isReadable = true; // AssetDatabase.ImportAsset(projectRelativePath, ImportAssetOptions.ForceSynchronousImport); // } // // // Now re-open the texture asset: // atlas = (Texture2D) AssetDatabase.LoadAssetAtPath(projectRelativePath, typeof(Texture2D)); // Pack the textures to the atlas: texList.uvs = atlas.PackTextures(texList.trimmedTextures.ToArray(), padding, maxAtlasSize); // Check to see if the texture had to be resized to fit: if (texList.uvs[0].width * atlas.width != (texList.trimmedTextures[0]).width || texList.uvs[0].height * atlas.height != (texList.trimmedTextures[0]).height) { EditorUtility.DisplayDialog("WARNING: Textures resized!", "WARNING: Not all textures were able to fit on atlas \"" + atlas.name + "\" at their original sizes. These textures were scaled down to fit. To resolve this, assign some of your sprites to use a different material, or if possible, use a larger maximum texture size.", "Ok"); Debug.LogWarning("WARNING: Textures were resized to fit onto atlas \"" + atlas.name + "\"! To resolve this, assign some of your sprites a different material, or if possible, use a larger maximum texture size."); } // Free our trimmed texture memory: for (int i = 0; i < texList.trimmedTextures.Count; ++i) { // If this isn't stored as an asset, destroy it: if (AssetDatabase.GetAssetPath(texList.trimmedTextures[i]).Length < 1) DestroyImmediate(texList.trimmedTextures[i]); texList.trimmedTextures[i] = null; } texList.trimmedTextures.Clear(); #if !UNITY_IPHONE || (UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9) Resources.UnloadUnusedAssets(); #endif // See if the texture needs to be made square: if (forceSquare && atlas.width != atlas.height) { int size = Mathf.Max(atlas.width, atlas.height); // Create a square texture: Texture2D tempTex = (Texture2D)Instantiate(atlas); tempTex.name = atlas.name; tempTex.Resize(size, size, TextureFormat.ARGB32, false); // Copy the contents: tempTex.SetPixels(0, 0, atlas.width, atlas.height, atlas.GetPixels(0), 0); tempTex.Apply(false); // Scale the UVs to account for this: for (int j = 0; j < texList.uvs.Length; ++j) { // See which side we expanded: if (atlas.width > atlas.height) { texList.uvs[j].y = texList.uvs[j].y * 0.5f; texList.uvs[j].yMax = texList.uvs[j].y + (texList.uvs[j].height * 0.5f); } else { texList.uvs[j].x = texList.uvs[j].x * 0.5f; texList.uvs[j].xMax = texList.uvs[j].x + (texList.uvs[j].width * 0.5f); } } if (atlas != tempTex) DestroyImmediate(atlas); atlas = tempTex; } // See if we need to trim the atlas to fit its content: if (!forceSquare) { Texture2D tempTex = TrimAtlas(atlas); // Scale the UVs to account for this: float widthFactor = ((float)atlas.width) / ((float)tempTex.width); float heightFactor = ((float)atlas.height) / ((float)tempTex.height); for (int j = 0; j < texList.uvs.Length; ++j) { texList.uvs[j].x *= widthFactor; texList.uvs[j].y *= heightFactor; texList.uvs[j].width *= widthFactor; texList.uvs[j].height *= heightFactor; } if (atlas != tempTex) DestroyImmediate(atlas); atlas = tempTex; } // Save the atlas as an asset: bytes = atlas.EncodeToPNG(); // Write out the atlas file: using (FileStream fs = File.Create(atlasPath)) { fs.Write(bytes, 0, bytes.Length); } // Flag this memory to be freed: bytes = null; // Save the atlas path: texList.texPath = "Assets/" + atlasFolder + "/" + atlas.name + ".png"; Debug.Log("Finished building atlas \"" + texList.material.name + "\"..."); }
// Finds all textures which are to be part // of a given atlas: AtlasTextureList BuildTextureList(MaterialSpriteList msMap) { ISpriteAggregator spr; Texture2D newTex = null; SPRITE_FRAME frameInfo = new SPRITE_FRAME(0); AtlasTextureList texList = new AtlasTextureList(); texList.material = msMap.material; // Get all the textures pointed to by the sprites: for (int i = 0; i < msMap.sprites.Count; ++i) { spr = msMap.sprites[i]; spr.Aggregate(AssetDatabase.GUIDToAssetPath, AssetDatabase.LoadAssetAtPath, AssetDatabase.AssetPathToGUID); // Get the sprite ready to give us its textures for (int j = 0; j < spr.SourceTextures.Length; ++j) { if (spr.SourceTextures[j] != null) { // See if the texture is already in our list: if (-1 >= texList.textures.IndexOf(spr.SourceTextures[j])) { VerifyImportSettings(spr.SourceTextures[j]); texList.textures.Add(spr.SourceTextures[j]); ProcessFrame(spr.SourceTextures[j], spr.DoNotTrimImages, ref newTex, ref frameInfo); texList.trimmedTextures.Add(newTex); texList.frames.Add(frameInfo); } } else { #if WARN_ON_NULL_TEXTURE Debug.LogWarning("Null texture reference detected on sprite \"" + ((Component)spr).name + "\"! Please verify that this was intentional."); #endif } } } return texList; }
// Builds an atlas from the specified texture list void BuildAtlas(AtlasTextureList texList) { Texture2D atlas; atlas = new Texture2D(4, 4); atlas.name = texList.material.name; //============================================= // The following is currently disabled as it // does not seem to work around the Unity bug: //============================================= // To get around the Unity bug where the max texture size is // limited to 1024x1024 when in iPhone mode for newly-created // textures, save this texture to disk as an asset and then // re-import it with modified import settings to set the max // texture size to what we want: byte[] bytes; // = atlas.EncodeToPNG(); string atlasPath = assetPath + "/" + atlas.name + ".png"; // string projectRelativePath = "Assets/" + atlasFolder + "/" + atlas.name + ".png"; // // // Write out the atlas file: // using (FileStream fs = File.Create(atlasPath)) // { // fs.Write(bytes, 0, bytes.Length); // } // // // Make sure the max texture size is set to what we want: // // Get the texture's importer: // TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(projectRelativePath); // if (importer.maxTextureSize < maxAtlasSize || importer.isReadable) // { // // Re-import it with the desired max texture size: // importer.maxTextureSize = maxAtlasSize; // importer.isReadable = true; // AssetDatabase.ImportAsset(projectRelativePath, ImportAssetOptions.ForceSynchronousImport); // } // // // Now re-open the texture asset: // atlas = (Texture2D) AssetDatabase.LoadAssetAtPath(projectRelativePath, typeof(Texture2D)); // Pack the textures to the atlas: texList.uvs = atlas.PackTextures((Texture2D[])texList.trimmedTextures.ToArray(typeof(Texture2D)), padding, maxAtlasSize); // Check to see if the texture had to be resized to fit: if (texList.uvs[0].width * atlas.width != ((Texture2D)texList.trimmedTextures[0]).width || texList.uvs[0].height * atlas.height != ((Texture2D)texList.trimmedTextures[0]).height) { EditorUtility.DisplayDialog("WARNING: Textures resized!", "WARNING: Not all textures were able to fit on atlas \"" + atlas.name + "\" at their original sizes. These textures were scaled down to fit. To resolve this, assign some of your sprites to use a different material, or if possible, use a larger maximum texture size.", "Ok"); Debug.LogWarning("WARNING: Textures were resized to fit onto atlas \"" + atlas.name + "\"! To resolve this, assign some of your sprites a different material, or if possible, use a larger maximum texture size."); } // Free our trimmed texture memory: for (int i = 0; i < texList.trimmedTextures.Count; ++i) { // If this isn't stored as an asset, destroy it: if (AssetDatabase.GetAssetPath((Texture2D)texList.trimmedTextures[i]).Length < 1) { DestroyImmediate((Texture2D)texList.trimmedTextures[i]); } texList.trimmedTextures[i] = null; } texList.trimmedTextures.Clear(); #if !UNITY_IPHONE || (UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9) Resources.UnloadUnusedAssets(); #endif // See if the texture needs to be made square: if (forceSquare && atlas.width != atlas.height) { int size = Mathf.Max(atlas.width, atlas.height); // Create a square texture: Texture2D tempTex = (Texture2D)Instantiate(atlas); tempTex.name = atlas.name; tempTex.Resize(size, size, TextureFormat.ARGB32, false); // Copy the contents: tempTex.SetPixels(0, 0, atlas.width, atlas.height, atlas.GetPixels(0), 0); tempTex.Apply(false); // Scale the UVs to account for this: for (int j = 0; j < texList.uvs.Length; ++j) { // See which side we expanded: if (atlas.width > atlas.height) { texList.uvs[j].y = texList.uvs[j].y * 0.5f; texList.uvs[j].yMax = texList.uvs[j].y + (texList.uvs[j].height * 0.5f); } else { texList.uvs[j].x = texList.uvs[j].x * 0.5f; texList.uvs[j].xMax = texList.uvs[j].x + (texList.uvs[j].width * 0.5f); } } if (atlas != tempTex) { DestroyImmediate(atlas); } atlas = tempTex; } // See if we need to trim the atlas to fit its content: if (!forceSquare) { Texture2D tempTex = TrimAtlas(atlas); // Scale the UVs to account for this: float widthFactor = ((float)atlas.width) / ((float)tempTex.width); float heightFactor = ((float)atlas.height) / ((float)tempTex.height); for (int j = 0; j < texList.uvs.Length; ++j) { texList.uvs[j].x *= widthFactor; texList.uvs[j].y *= heightFactor; texList.uvs[j].width *= widthFactor; texList.uvs[j].height *= heightFactor; } if (atlas != tempTex) { DestroyImmediate(atlas); } atlas = tempTex; } // Save the atlas as an asset: bytes = atlas.EncodeToPNG(); // Write out the atlas file: using (FileStream fs = File.Create(atlasPath)) { fs.Write(bytes, 0, bytes.Length); } // Flag this memory to be freed: bytes = null; // Save the atlas path: texList.texPath = "Assets/" + atlasFolder + "/" + atlas.name + ".png"; Debug.Log("Finished building atlas \"" + texList.material.name + "\"..."); }
// Builds an atlas from the specified texture list void BuildAtlas(AtlasTextureList texList) { Texture2D atlas; atlas = new Texture2D(32, 32); atlas.name = texList.material.name; texList.uvs = atlas.PackTextures((Texture2D[])texList.trimmedTextures.ToArray(typeof(Texture2D)), padding, maxAtlasSize); // Check to see if the texture had to be resized to fit: if (texList.uvs[0].width * atlas.width != ((Texture2D)texList.trimmedTextures[0]).width || texList.uvs[0].height * atlas.height != ((Texture2D)texList.trimmedTextures[0]).height) { EditorUtility.DisplayDialog("WARNING: Textures resized!", "WARNING: Not all textures were able to fit on atlas \"" + atlas.name + "\" at their original sizes. These textures were scaled down to fit. To resolve this, assign some of your sprites to use a different material, or if possible, use a larger maximum texture size.", "Ok"); Debug.LogWarning("WARNING: Textures were resized to fit onto atlas \"" + atlas.name + "\"! To resolve this, assign some of your sprites a different material, or if possible, use a larger maximum texture size."); } // Free our trimmed texture memory: for (int i = 0; i < texList.trimmedTextures.Count; ++i) { // If this isn't stored as an asset, destroy it: if (AssetDatabase.GetAssetPath((Texture2D)texList.trimmedTextures[i]).Length < 1) DestroyImmediate((Texture2D)texList.trimmedTextures[i]); texList.trimmedTextures[i] = null; } texList.trimmedTextures.Clear(); #if !UNITY_IPHONE || UNITY_3_0 Resources.UnloadUnusedAssets(); #endif // See if the texture needs to be made square: if (forceSquare && atlas.width != atlas.height) { int size = Mathf.Max(atlas.width, atlas.height); // Create a square texture: Texture2D tempTex = (Texture2D)Instantiate(atlas); tempTex.name = atlas.name; tempTex.Resize(size, size, TextureFormat.ARGB32, false); // Copy the contents: tempTex.SetPixels(0, 0, atlas.width, atlas.height, atlas.GetPixels(0), 0); tempTex.Apply(false); // Scale the UVs to account for this: for (int j = 0; j < texList.uvs.Length; ++j) { // See which side we expanded: if (atlas.width > atlas.height) { texList.uvs[j].y = texList.uvs[j].y * 0.5f; texList.uvs[j].yMax = texList.uvs[j].y + (texList.uvs[j].height * 0.5f); } else { texList.uvs[j].x = texList.uvs[j].x * 0.5f; texList.uvs[j].xMax = texList.uvs[j].x + (texList.uvs[j].width * 0.5f); } } if (atlas != tempTex) DestroyImmediate(atlas); atlas = tempTex; } // See if we need to trim the atlas to fit its content: if (!forceSquare) { Texture2D tempTex = TrimAtlas(atlas); // Scale the UVs to account for this: float widthFactor = ((float)atlas.width) / ((float)tempTex.width); float heightFactor = ((float)atlas.height) / ((float)tempTex.height); for (int j = 0; j < texList.uvs.Length; ++j) { texList.uvs[j].x *= widthFactor; texList.uvs[j].y *= heightFactor; texList.uvs[j].width *= widthFactor; texList.uvs[j].height *= heightFactor; } if (atlas != tempTex) DestroyImmediate(atlas); atlas = tempTex; } // Save the atlas as an asset: byte[] bytes = atlas.EncodeToPNG(); string atlasPath = assetPath + "/" + atlas.name + ".png"; // Write out the atlas file: using (FileStream fs = File.Create(atlasPath)) { fs.Write(bytes, 0, bytes.Length); } // Flag this memory to be freed: bytes = null; // Save the atlas path: texList.texPath = "Assets/" + atlasFolder + "/" + atlas.name + ".png"; Debug.Log("Finished building atlas \"" + texList.material.name + "\"..."); }