public static int GetAproxAtlasSize(int index, bool reuseTextures) { int atlasSize = 0; if (shadersUsed[index] == "") //we dont need to calculate atlas size on non-optimizable objects { return(atlasSize); } if (reuseTextures) { TextureReuseManager textureReuseManager = new TextureReuseManager(); for (int i = 0; i < sortedObjects[index].Count; i++) { if (sortedObjects[index][i] != null) { if (!textureReuseManager.TextureRefExists(sortedObjects[index][i])) { textureReuseManager.AddTextureRef(sortedObjects[index][i]); atlasSize += sortedObjects[index][i].TextureArea; } } } } else { for (int i = 0; i < sortedObjects[index].Count; i++) { if (sortedObjects[index][i] != null) { atlasSize += sortedObjects[index][i].TextureArea; } } } return(Mathf.RoundToInt(Mathf.Sqrt(atlasSize))); }
//calculates aprox atlas sizes with and without reusing textures //cacheAtlasSizeReuseTextures = NO_CACHED; //cacheAtlasSizeNoReuseTextures = NO_CACHED; public int CalculateAproxAtlasSize(bool reuseTextures, bool usePowerOf2Atlasses) { int aproxAtlasSize = 0; if (shaderName == "")//we dont need to calculate atlas size on non-optimizable objects { return(aproxAtlasSize); } if (reuseTextures) { if (cacheAtlasSizeReuseTextures == NO_CACHED) { //atlas size reuse textures TextureReuseManager textureReuseManager = new TextureReuseManager(); for (int i = 0; i < objectsToOptimize.Count; i++) { if (objectsToOptimize[i] != null) { if (!textureReuseManager.TextureRefExists(objectsToOptimize[i])) { textureReuseManager.AddTextureRef(objectsToOptimize[i]); aproxAtlasSize += objectsToOptimize[i].TextureArea; } } } cacheAtlasSizeReuseTextures = Mathf.RoundToInt(Mathf.Sqrt(aproxAtlasSize)); } return(usePowerOf2Atlasses ? Mathf.NextPowerOfTwo(cacheAtlasSizeReuseTextures) : cacheAtlasSizeReuseTextures); } else { if (cacheAtlasSizeNoReuseTextures == NO_CACHED) { //atlas size without reusing textures for (int i = 0; i < objectsToOptimize.Count; i++) { if (objectsToOptimize[i] != null) { aproxAtlasSize += objectsToOptimize[i].TextureArea; } } cacheAtlasSizeNoReuseTextures = Mathf.RoundToInt(Mathf.Sqrt(aproxAtlasSize)); } return(usePowerOf2Atlasses ? Mathf.NextPowerOfTwo(cacheAtlasSizeNoReuseTextures) : cacheAtlasSizeNoReuseTextures); } }
//calculates aprox atlas sizes with and without reusing textures public int CalculateAproxAtlasSize(bool reuseTextures) { int aproxAtlasSize = 0; if (shaderName == "")//we dont need to calculate atlas size on non-optimizable objects { return(aproxAtlasSize); } if (reuseTextures) { //atlas size reuse textures TextureReuseManager textureReuseManager = new TextureReuseManager(); for (int i = 0; i < objects.Count; i++) { if (objects[i] != null) { if (!textureReuseManager.TextureRefExists(objects[i])) { textureReuseManager.AddTextureRef(objects[i]); aproxAtlasSize += objects[i].TextureArea; } } } } else { //atlas size without reusing textures for (int i = 0; i < objects.Count; i++) { if (objects[i] != null) { aproxAtlasSize += objects[i].TextureArea; } } } aproxAtlasSize = Mathf.RoundToInt(Mathf.Sqrt(aproxAtlasSize)); return(aproxAtlasSize); }
public void OptimizeShader(bool reuseTextures, bool generatePrefabs) { if (shaderName == "")//unknown shader doesnt need to be optimed { return; } int currentAtlasSize = CalculateAproxAtlasSize(reuseTextures); //(reuseTextures) ? aproxAtlasSizeReuseTextures : aproxAtlasSize; if ((objects.Count > 1 || //more than 1 obj or 1 obj with multiple mat (objects.Count == 1 && objects[0] != null && objects[0].ObjHasMoreThanOneMaterial)) && currentAtlasSize < Constants.MaxAtlasSize) //check the generated atlas size doesnt exceed max supported texture size { List <Rect> texturePositions = new List <Rect>(); //creo que puede morir porque el atlasser tiene adentro un rect. Node resultNode = null; //nodes for the tree for atlasing Atlasser generatedAtlas = new Atlasser(currentAtlasSize, currentAtlasSize); int resizeTimes = 1; TextureReuseManager textureReuseManager = new TextureReuseManager(); for (int j = objects.Count - 1; j >= 0; j--) //start from the largest to the shortest textures { if (objects[j].ObjHasMoreThanOneMaterial) //before atlassing multiple materials obj, combine it. { objects[j].ProcessAndCombineMaterials(); } Vector2 textureToAtlasSize = objects[j].TextureSize; if (reuseTextures) { //if texture is not registered already if (!textureReuseManager.TextureRefExists(objects[j])) { //generate a node resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); if (resultNode != null) //save node if fits in atlas { textureReuseManager.AddTextureRef(objects[j], resultNode.NodeRect, j); } } } else { resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); } if (resultNode == null) { int resizedAtlasSize = currentAtlasSize + Mathf.RoundToInt((float)currentAtlasSize * Constants.AtlasResizeFactor * resizeTimes); generatedAtlas = new Atlasser(resizedAtlasSize, resizedAtlasSize); j = objects.Count;//Count and not .Count-1 bc at the end of the loop it will be substracted j-- and we want to start from Count-1 texturePositions.Clear(); textureReuseManager.ClearTextureRefs(); resizeTimes++; } else { if (reuseTextures) { texturePositions.Add(textureReuseManager.GetTextureRefPosition(objects[j])); } else { texturePositions.Add(resultNode.NodeRect);//save the texture rectangle } } } Material atlasMaterial = CreateAtlasMaterialAndTexture(generatedAtlas, shaderName, textureReuseManager); OptimizeDrawCalls(ref atlasMaterial, generatedAtlas.GetAtlasSize().x, generatedAtlas.GetAtlasSize().y, texturePositions, reuseTextures, textureReuseManager, generatePrefabs); //after the game object has been organized, remove the combined game objects. for (int i = 0; i < objects.Count; i++) { if (objects[i].ObjWasCombined) { objects[i].ClearCombinedObject(); } } } }
void OnGUI() { if (NeedToReload()) { ReloadDataStructures(); } selectedMenuOption = GUI.SelectionGrid(new Rect(5, 8, window.position.width - 10, 20), selectedMenuOption, menuOptions, 3); switch (selectedMenuOption) { case 0: ObjectsGUI.Instance.DrawGUI(window); AdvancedMenuGUI.Instance.ClearConsole(); menuOptions[0] = "Objects"; break; case 1: ShadersGUI.Instance.DrawGUI(window); AdvancedMenuGUI.Instance.ClearConsole(); menuOptions[0] = "Objects(" + ObjSorter.GetTotalSortedObjects() + ")"; break; case 2: AdvancedMenuGUI.Instance.DrawGUI(window); menuOptions[0] = "Objects(" + ObjSorter.GetTotalSortedObjects() + ")"; break; default: Debug.LogError("Unrecognized menu option: " + selectedMenuOption); break; } if (GUI.Button(new Rect(5, window.position.height - 35, window.position.width / 2 - 10, 33), "Clear Atlas")) { GameObject[] objsInHierarchy = Utils.GetAllObjectsInHierarchy(); foreach (GameObject obj in objsInHierarchy) { if (obj.name.Contains(Constants.OptimizedObjIdentifier)) { DestroyImmediate(obj); } else if (obj.GetComponent <MeshRenderer>() != null) { obj.GetComponent <MeshRenderer>().enabled = true; } } // delete the folder where the atlas reside. string folderOfAtlas = EditorApplication.currentScene; if (folderOfAtlas == "") //scene is not saved yet. { folderOfAtlas = Constants.NonSavedSceneFolderName + ".unity"; Debug.LogWarning("WARNING: Scene has not been saved, clearing baked objects from NOT_SAVED_SCENE folder"); } folderOfAtlas = folderOfAtlas.Substring(0, folderOfAtlas.Length - 6) + "-Atlas"; //remove the ".unity" if (Directory.Exists(folderOfAtlas)) { FileUtil.DeleteFileOrDirectory(folderOfAtlas); AssetDatabase.Refresh(); } } GUI.enabled = CheckEmptyArray(); //if there are no textures deactivate the GUI if (GUI.Button(new Rect(window.position.width / 2, window.position.height - 35, window.position.width / 2 - 5, 33), "Bake Atlas")) { //Remove objects that are already optimized and start over. if (AdvancedMenuGUI.Instance.RemoveObjectsBeforeBaking) { GameObject[] objsInHierarchy = Utils.GetAllObjectsInHierarchy(); foreach (GameObject obj in objsInHierarchy) { if (obj.name.Contains(Constants.OptimizedObjIdentifier)) { GameObject.DestroyImmediate(obj); } } } List <Rect> texturePositions = new List <Rect>(); //creo que esto puede morir porque el atlasser tiene adentro un rect. string progressBarInfo = ""; float pace = 1 / (float)ObjSorter.GetRecognizableShadersCount(); float progress = pace; Node resultNode = null; //nodes for the tree for atlasing for (int shaderIndex = 0; shaderIndex < ObjSorter.GetObjs().Count; shaderIndex++) { EditorUtility.DisplayProgressBar("Optimization in progress... " + (AdvancedMenuGUI.Instance.CreatePrefabsForObjects ? " Get coffee this will take some time..." : ""), progressBarInfo, progress); progress += pace; texturePositions.Clear(); TextureReuseManager textureReuseManager = new TextureReuseManager(); string shaderToAtlas = (ObjSorter.GetObjs()[shaderIndex][0] != null && ObjSorter.GetObjs()[shaderIndex][0].IsCorrectlyAssembled) ? ObjSorter.GetObjs()[shaderIndex][0].ShaderName : ""; progressBarInfo = "Processing shader " + shaderToAtlas + "..."; int atlasSize = ObjSorter.GetAproxAtlasSize(shaderIndex, AdvancedMenuGUI.Instance.ReuseTextures); if (ShaderManager.Instance.ShaderExists(shaderToAtlas) && (ObjSorter.GetObjs()[shaderIndex].Count > 1 || (ObjSorter.GetObjs()[shaderIndex].Count == 1 && ObjSorter.GetObjs()[shaderIndex][0] != null && ObjSorter.GetObjs()[shaderIndex][0].ObjHasMoreThanOneMaterial)) && //more than 1 obj or 1obj wth multiple mat atlasSize < Constants.MaxAtlasSize) //check the generated atlas size doesnt exceed max supported texture size { generatedAtlas = new Atlasser(atlasSize, atlasSize); int resizeTimes = 1; for (int j = ObjSorter.GetObjs()[shaderIndex].Count - 1; j >= 0; j--) //start from the largest to the shortest textures //before atlassing multiple materials obj, combine it. { if (ObjSorter.GetObjs()[shaderIndex][j].ObjHasMoreThanOneMaterial) { progressBarInfo = "Combining materials..."; ObjSorter.GetObjs()[shaderIndex][j].ProcessAndCombineMaterials(); //mirar esto, aca esta el problema de multiple materiales y reimportacion } Vector2 textureToAtlasSize = ObjSorter.GetObjs()[shaderIndex][j].TextureSize; if (AdvancedMenuGUI.Instance.ReuseTextures) { //if texture is not registered already if (!textureReuseManager.TextureRefExists(ObjSorter.GetObjs()[shaderIndex][j])) { //generate a node resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); if (resultNode != null) //save node if fits in atlas { textureReuseManager.AddTextureRef(ObjSorter.GetObjs()[shaderIndex][j], resultNode.NodeRect, j); } } } else { resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); } if (resultNode == null) { int resizedAtlasSize = atlasSize + Mathf.RoundToInt((float)atlasSize * Constants.AtlasResizeFactor * resizeTimes); generatedAtlas = new Atlasser(resizedAtlasSize, resizedAtlasSize); j = ObjSorter.GetObjs()[shaderIndex].Count; //Count and not .Count-1 bc at the end of the loop it will be substracted j-- and we want to start from Count-1 texturePositions.Clear(); textureReuseManager.ClearTextureRefs(); resizeTimes++; } else { if (AdvancedMenuGUI.Instance.ReuseTextures) { texturePositions.Add(textureReuseManager.GetTextureRefPosition(ObjSorter.GetObjs()[shaderIndex][j])); } else { texturePositions.Add(resultNode.NodeRect); //save the texture rectangle } } } progressBarInfo = "Saving textures to atlas..."; Material atlasMaterial = CreateAtlasMaterialAndTexture(shaderToAtlas, shaderIndex, textureReuseManager); progressBarInfo = "Remapping coordinates..."; ObjSorter.OptimizeDrawCalls(ref atlasMaterial, shaderIndex, generatedAtlas.GetAtlasSize().x, generatedAtlas.GetAtlasSize().y, texturePositions, AdvancedMenuGUI.Instance.ReuseTextures, textureReuseManager, AdvancedMenuGUI.Instance.CreatePrefabsForObjects); } } //after the game object has been organized, remove the combined game objects. for (int shaderIndex = 0; shaderIndex < ObjSorter.GetObjs().Count; shaderIndex++) { for (int j = ObjSorter.GetObjs()[shaderIndex].Count - 1; j >= 0; j--) { if (ObjSorter.GetObjs()[shaderIndex][j].ObjWasCombined) { ObjSorter.GetObjs()[shaderIndex][j].ClearCombinedObject(); } } } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); //reimport the created atlases so they get displayed in the editor. } }
public void OptimizeShader(bool reuseTextures, bool generatePrefabs, bool generatePowerOf2Atlases) { optimizedObjects.Clear(); //used for generating hierearchy if (shaderName == "") //unknown shader doesnt need to be optimed { return; } int currentAtlasSize = Mathf.Min(CalculateAproxAtlasSize(reuseTextures, generatePowerOf2Atlases), Constants.MaxAtlasSize); /*Constants.MaxAtlasSize);*/ if (objectsToOptimize.Count > 1 || //more than 1 obj or 1 obj with multiple mat (objectsToOptimize.Count == 1 && objectsToOptimize[0] != null && objectsToOptimize[0].ObjHasMoreThanOneMaterial)) { // // // when generating prefabs // // // folderToSavePrefabs = (PersistenceHandler.Instance.PathToSaveOptimizedObjs != "") ? PersistenceHandler.Instance.PathToSaveOptimizedObjs + Path.DirectorySeparatorChar + Utils.GetCurrentSceneName() : EditorSceneManager.GetActiveScene().path;//<--5.4 + //EditorApplication.currentScene;//<- 5.3 - if (generatePrefabs) { //if(EditorApplication.currentScene == "") { //scene is not saved yet. if (EditorSceneManager.GetActiveScene().path == "")//scene not saved yet. { folderToSavePrefabs = Constants.NonSavedSceneFolderName + ".unity"; } folderToSavePrefabs = folderToSavePrefabs.Substring(0, folderToSavePrefabs.Length - 6) + "-Atlas";//remove the ".unity" folderToSavePrefabs += Path.DirectorySeparatorChar + "Prefabs"; if (!Directory.Exists(folderToSavePrefabs)) { Directory.CreateDirectory(folderToSavePrefabs); AssetDatabase.Refresh(); } } /////////////////////////////////////////// Node resultNode = null;//nodes for the tree for atlasing Atlasser generatedAtlas = new Atlasser(currentAtlasSize, currentAtlasSize, generatePowerOf2Atlases); int resizeTimes = 1; TextureReuseManager textureReuseManager = new TextureReuseManager(); int lastAtlasStartingIndex = objectsToOptimize.Count - 1; int atlasNumber = 1; //this is just for numbering the atlases int range = 0; //how many items have we placed inside the current atlas. for (int j = objectsToOptimize.Count - 1; j >= 0; j--) //start from the largest to the shortest textures { if (objectsToOptimize[j].ObjHasMoreThanOneMaterial) //before atlassing multiple materials obj, combine it. { objectsToOptimize[j].ProcessAndCombineMaterials(); } Vector2 textureToAtlasSize = objectsToOptimize[j].TextureSize; if (objectsToOptimize[j].TextureArea > Constants.MaxAtlasSize * Constants.MaxAtlasSize) { Debug.LogError("Texture for game object: " + objectsToOptimize[j].GameObj.name + " is bigger than max atlas size: " + Constants.MaxAtlasSize + "x" + Constants.MaxAtlasSize + " ABORTING"); return; } if (reuseTextures) { if (!textureReuseManager.TextureRefExists(objectsToOptimize[j]))//if texture is not registered already //generate a node { resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); if (resultNode != null) //save node if fits in atlas { textureReuseManager.AddTextureRef(objectsToOptimize[j], resultNode.NodeRect, j); } } } else { resultNode = generatedAtlas.Insert(Mathf.RoundToInt((textureToAtlasSize.x != Constants.NULLV2.x) ? textureToAtlasSize.x : Constants.NullTextureSize), Mathf.RoundToInt((textureToAtlasSize.y != Constants.NULLV2.y) ? textureToAtlasSize.y : Constants.NullTextureSize)); } if (resultNode == null) { int resizedAtlasSize = currentAtlasSize + Mathf.RoundToInt((float)currentAtlasSize * Constants.AtlasResizeFactor * resizeTimes); if (resizedAtlasSize <= Constants.MaxAtlasSize)//If we still can place textures inside the atlas then increase the atlas. { if (generatePowerOf2Atlases) { resizedAtlasSize = Mathf.NextPowerOfTwo(resizedAtlasSize); } generatedAtlas = new Atlasser(resizedAtlasSize, resizedAtlasSize, generatePowerOf2Atlases); j = lastAtlasStartingIndex + 1;//== Count and not .Count-1 bc at the end of the loop it will be substracted j-- and we want to start from Count-1 resizeTimes++; } else//lets save what we have gathered already into a material + objects and lets start over again. { lastAtlasStartingIndex = j; Material atlasMaterial1 = CreateAtlasMaterialAndTexture(generatedAtlas, shaderName, textureReuseManager, atlasNumber, lastAtlasStartingIndex + 1, lastAtlasStartingIndex + 1 + range); OptimizeDrawCalls(objectsToOptimize.GetRange(lastAtlasStartingIndex + 1, range), ref atlasMaterial1, generatedAtlas.GetAtlasSize().x, generatedAtlas.GetAtlasSize().y, generatedAtlas.TexturePositions, reuseTextures, textureReuseManager,//remember to clear this when creating one atlas. generatePrefabs); CombineObjectsSelectedForCombine(generatePrefabs, atlasMaterial1, lastAtlasStartingIndex + 1, lastAtlasStartingIndex + 1 + range); resizeTimes = 1; currentAtlasSize = Constants.MinAtlasSize; generatedAtlas = new Atlasser(currentAtlasSize, currentAtlasSize, generatePowerOf2Atlases); atlasNumber++; if (j == 0) { j++; //if we happen to be creating an atlas in the last iteration of the loop. then we need to iterate once more to get the 1rst elementx } } textureReuseManager.ClearTextureRefs(); range = 0; } else { range++;//increase range as the resultNode != null --> Insertion was successful } } Material atlasMaterial = CreateAtlasMaterialAndTexture(generatedAtlas, shaderName, textureReuseManager, atlasNumber, 0, range); OptimizeDrawCalls(objectsToOptimize.GetRange(0, range), ref atlasMaterial, generatedAtlas.GetAtlasSize().x, generatedAtlas.GetAtlasSize().y, generatedAtlas.TexturePositions, reuseTextures, textureReuseManager, generatePrefabs); CombineObjectsSelectedForCombine(generatePrefabs, atlasMaterial, 0, range); //after the game object has been organized, remove the combined game objects. for (int i = 0; i < objectsToOptimize.Count; i++) { if (objectsToOptimize[i].ObjWasCombined) { objectsToOptimize[i].ClearCombinedObject(); } } } }