├── Intro ├── after.png ├── before.png ├── tex_after.png └── tex_before.png ├── README.md ├── .gitignore ├── LICENSE └── TextureCompressor.cs /Intro/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnomyxStudio/Texture-Compressor/HEAD/Intro/after.png -------------------------------------------------------------------------------- /Intro/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnomyxStudio/Texture-Compressor/HEAD/Intro/before.png -------------------------------------------------------------------------------- /Intro/tex_after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnomyxStudio/Texture-Compressor/HEAD/Intro/tex_after.png -------------------------------------------------------------------------------- /Intro/tex_before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnomyxStudio/Texture-Compressor/HEAD/Intro/tex_before.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Texture-Compressor 2 | A Unity texture helper. Easy to compress your texture and replace references for you. 3 | 4 | ## Usage 5 | 6 | 1. Adjust your texture max size on inspector 7 | 8 | 3. select TextureCompressor/Auto Encode Texture from context menu 9 | 10 | 4. Wait a moment 11 | 12 | 5. Done! 13 | 14 | ## Features 15 | 16 | 1. Auto replace exist dependencies 17 | 18 | 2. Auto select Jpg/Png encode format 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Visual Studio 2015 cache directory 9 | /.vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | 26 | # Unity3D generated meta files 27 | *.pidb.meta 28 | 29 | # Unity3D Generated File On Crash Reports 30 | sysinfo.txt 31 | 32 | # Builds 33 | *.apk 34 | *.unitypackage 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Skyrim Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TextureCompressor.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using System; 3 | using UnityEngine; 4 | using UnityEditor; 5 | using System.IO; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using Debug = UnityEngine.Debug; 9 | 10 | public static class TextureCompressor 11 | { 12 | private const string Version = "1.1.2"; 13 | 14 | private const int JPGQualityLevel = 100; 15 | 16 | private static readonly Dictionary> Sources = new Dictionary>(); 17 | 18 | // private static readonly string[] Filters = 19 | // { 20 | // ".jpg", ".jpeg" 21 | // }; 22 | 23 | [MenuItem("Assets/TextureCompressor/To PNG", true)] 24 | [MenuItem("Assets/TextureCompressor/To JPG", true)] 25 | [MenuItem("Assets/TextureCompressor/To PNG (Delete Old)", true)] 26 | [MenuItem("Assets/TextureCompressor/To JPG (Delete Old)", true)] 27 | [MenuItem("Assets/TextureCompressor/Auto Encode Texture", true)] 28 | [MenuItem("Assets/TextureCompressor/Auto Encode Texture(Delete Old)", true)] 29 | public static bool ValidateEncodeTetxure() 30 | { 31 | return Selection.activeObject != null && 32 | Selection.activeObject is Texture2D; 33 | } 34 | 35 | [MenuItem("Assets/TextureCompressor/To PNG")] 36 | public static void EncodeTetxureToPNGKeepOld() 37 | { 38 | AutoEncodeTexture(false, tex => true); 39 | } 40 | 41 | [MenuItem("Assets/TextureCompressor/To JPG")] 42 | public static void EncodeTetxureToJPGKeepOld() 43 | { 44 | AutoEncodeTexture(false, tex => false); 45 | } 46 | 47 | [MenuItem("Assets/TextureCompressor/To PNG (Delete Old)")] 48 | public static void EncodeTetxureToPNG() 49 | { 50 | AutoEncodeTexture(true, tex => true); 51 | } 52 | 53 | [MenuItem("Assets/TextureCompressor/To JPG (Delete Old)")] 54 | public static void EncodeTetxureToJPG() 55 | { 56 | AutoEncodeTexture(true, tex => false); 57 | } 58 | 59 | [MenuItem("Assets/TextureCompressor/Auto Encode Texture")] 60 | public static void AutoEncodeTexture() 61 | { 62 | AutoEncodeTexture(false, TextureHasAlpha); 63 | } 64 | 65 | [MenuItem("Assets/TextureCompressor/Auto Encode Texture(Delete Old)")] 66 | public static void AutoEncodeTextureAndDeleteOld() 67 | { 68 | AutoEncodeTexture(true, TextureHasAlpha); 69 | } 70 | 71 | private static bool TextureHasAlpha(Texture2D tex2D) 72 | { 73 | var path = AssetDatabase.GetAssetPath(tex2D); 74 | var importer = AssetImporter.GetAtPath(path) as TextureImporter; 75 | if (importer != null) 76 | { 77 | var hasAlpha = importer.DoesSourceTextureHaveAlpha(); 78 | if (hasAlpha) { Debug.Log(tex2D + " - has alpha channel"); } 79 | 80 | return hasAlpha; 81 | } 82 | 83 | Debug.LogError("Texture is not exists :" + tex2D); 84 | return true; 85 | } 86 | 87 | private static void AutoEncodeTexture(bool deleteOld, Predicate isPNG) 88 | { 89 | try 90 | { 91 | CacheAllMaterialInProject(); 92 | var length = Selection.objects.Length; 93 | var selecteds = Selection.objects.OrderBy(x => x.name).ToList(); 94 | for (var index = length - 1; index >= 0; index--) 95 | { 96 | var Obj = selecteds.ElementAt(index); 97 | if (Obj == null || !(Obj is Texture2D)) continue; 98 | 99 | EditorUtility.DisplayProgressBar("Encoding Textures", 100 | string.Format("Encoding : {0} ({1}/{2})", Obj.name, length - index, length), 101 | (length - index) / (float) length); 102 | 103 | var tex2D = Obj as Texture2D; 104 | EncodeSingle(Obj as Texture2D, deleteOld, isPNG(tex2D)); 105 | } 106 | 107 | EditorUtility.ClearProgressBar(); 108 | Sources.Clear(); 109 | } 110 | catch (Exception e) 111 | { 112 | Console.WriteLine(e); 113 | EditorUtility.ClearProgressBar(); 114 | throw; 115 | } 116 | } 117 | 118 | private static void CacheAllMaterialInProject() 119 | { 120 | var guids = AssetDatabase.FindAssets("t:material"); 121 | foreach (var guid in guids) 122 | { 123 | var path = AssetDatabase.GUIDToAssetPath(guid); 124 | var material = AssetDatabase.LoadAssetAtPath(path); 125 | var textures = FindAllTexture2Ds(material); 126 | foreach (var texture in textures) 127 | { 128 | if (Sources.ContainsKey(texture)) { Sources[texture].Add(material); } 129 | else { Sources.Add(texture, new List {material}); } 130 | } 131 | } 132 | } 133 | 134 | private static List FindAllTexture2Ds(Material material) 135 | { 136 | var allTexture = new List(); 137 | var shader = material.shader; 138 | for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) 139 | { 140 | if (ShaderUtil.GetPropertyType(shader, i) != ShaderUtil.ShaderPropertyType.TexEnv) continue; 141 | var texture = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) as Texture2D; 142 | if (texture != null) { allTexture.Add(texture); } 143 | } 144 | 145 | return allTexture; 146 | } 147 | 148 | private static void ReplaceDependcies(Texture2D original, Texture2D newTexture, Material material) 149 | { 150 | var shader = material.shader; 151 | for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++) 152 | { 153 | if (ShaderUtil.GetPropertyType(shader, i) != ShaderUtil.ShaderPropertyType.TexEnv) continue; 154 | var texture = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) as Texture2D; 155 | if (texture == original) { material.SetTexture(ShaderUtil.GetPropertyName(shader, i), newTexture); } 156 | } 157 | } 158 | 159 | public static void EncodeSingle(Texture2D texture, bool deleteOld, bool isPNG) 160 | { 161 | Texture2D origTexture = texture; 162 | var assetPath = AssetDatabase.GetAssetPath(origTexture); 163 | var importer = (TextureImporter) AssetImporter.GetAtPath(assetPath); 164 | 165 | bool needRevert = false; 166 | 167 | var tis = new TextureImporterSettings(); 168 | importer.ReadTextureSettings(tis); 169 | 170 | var compression = importer.textureCompression; 171 | 172 | var isBipmap = importer.mipmapEnabled; 173 | var isLinear = importer.sRGBTexture; 174 | 175 | if (!importer.isReadable) 176 | { 177 | needRevert = true; 178 | importer.isReadable = true; 179 | importer.SaveAndReimport(); 180 | AssetDatabase.Refresh(); 181 | } 182 | 183 | var pixels = origTexture.GetPixels(); 184 | var targetFormat = importer.DoesSourceTextureHaveAlpha() ? TextureFormat.ARGB32 : TextureFormat.RGB24; 185 | 186 | var newTxt = new Texture2D(origTexture.width, origTexture.height, 187 | targetFormat, isBipmap, isLinear); 188 | 189 | if (importer.textureType == TextureImporterType.NormalMap) { DTXnmColors(newTxt, pixels); } 190 | else { newTxt.SetPixels(pixels); } 191 | 192 | var buff = isPNG ? newTxt.EncodeToPNG() : newTxt.EncodeToJPG(JPGQualityLevel); 193 | 194 | var ext = isPNG ? ".png" : ".jpg"; 195 | var filePath = Path.GetDirectoryName(assetPath) + "/" + origTexture.name + 196 | (deleteOld ? ext : "_new" + ext); 197 | File.WriteAllBytes(filePath, buff); 198 | 199 | if (needRevert) 200 | { 201 | importer.isReadable = false; 202 | importer.SaveAndReimport(); 203 | } 204 | 205 | AssetDatabase.Refresh(); 206 | 207 | importer = (TextureImporter) AssetImporter.GetAtPath(filePath); 208 | if (importer != null) 209 | { 210 | importer.SetTextureSettings(tis); 211 | importer.textureCompression = compression; 212 | importer.SaveAndReimport(); 213 | AssetDatabase.Refresh(); 214 | } 215 | 216 | var newTexture = AssetDatabase.LoadAssetAtPath(filePath); 217 | 218 | var oriCol = EditorGUIUtility.isProSkin ? "#00b300" : "#b300b3"; 219 | var toCol = EditorGUIUtility.isProSkin ? "#fc0" : "#03f"; 220 | 221 | if (newTexture == null) { Debug.Log("Waiting for new texture - " + Path.GetFileName(filePath)); } 222 | 223 | Debug.Log(string.Format("Encode {0} to: {1}", 224 | Path.GetFileName(assetPath), Path.GetFileName(filePath), oriCol, toCol), 225 | newTexture); 226 | 227 | // Replace in dependcies 228 | if (Sources.ContainsKey(origTexture)) 229 | { 230 | Debug.LogFormat("We found {0} materials realted to {1}.", Sources[origTexture].Count, origTexture); 231 | foreach (var material in Sources[origTexture]) 232 | { 233 | ReplaceDependcies(origTexture, newTexture as Texture2D, material); 234 | } 235 | 236 | Sources.Remove(origTexture); 237 | AssetDatabase.Refresh(); 238 | } 239 | 240 | Selection.activeObject = newTexture; 241 | if (!deleteOld) { return; } 242 | 243 | if (filePath == assetPath) { return; } 244 | 245 | Debug.Log("Deleting @" + assetPath); 246 | AssetDatabase.DeleteAsset(assetPath); 247 | AssetDatabase.Refresh(); 248 | } 249 | 250 | private static void DTXnmColors(Texture2D target, Color[] colors) 251 | { 252 | #if UNITY_IPHONE || UNITY_ANDROID 253 | target.SetPixels(colors); 254 | return; 255 | #else 256 | for (int i = 0; i < colors.Length; i++) 257 | { 258 | Color c = colors[i]; 259 | c.r = c.a * 2 - 1; //red<-alpha (x<-w) 260 | c.g = c.g * 2 - 1; //green is always the same (y) 261 | Vector2 xy = new Vector2(c.r, c.g); //this is the xy vector 262 | c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(xy, xy))); //recalculate the blue channel (z) 263 | colors[i] = new Color(c.r * 0.5f + 0.5f, c.g * 0.5f + 0.5f, c.b * 0.5f + 0.5f); //back to 0-1 range 264 | } 265 | 266 | target.SetPixels(colors); //apply pixels to the texture 267 | target.Apply(); 268 | #endif 269 | } 270 | 271 | private static Texture2D NormalMap(Texture2D source) 272 | { 273 | var normalTexture = new Texture2D(source.width, source.height, TextureFormat.ARGB32, true); 274 | Color theColour = new Color(); 275 | for (int x = 0; x < source.width; x++) 276 | { 277 | for (int y = 0; y < source.height; y++) 278 | { 279 | theColour.r = 0; 280 | theColour.g = source.GetPixel(x, y).g; 281 | theColour.b = 0; 282 | theColour.a = source.GetPixel(x, y).r; 283 | normalTexture.SetPixel(x, y, theColour); 284 | } 285 | } 286 | 287 | normalTexture.Apply(); 288 | return normalTexture; 289 | } 290 | } 291 | #endif 292 | --------------------------------------------------------------------------------