private void AdjustArraysWithObjSorter() { if (unfoldedObjects.Count != ObjSorter.GetOptShaders().Count) { int offset = ObjSorter.GetOptShaders().Count - unfoldedObjects.Count; bool removing = false; if (offset < 0) { offset *= -1; removing = true; } for (int i = 0; i < (offset < 0 ? offset * -1 : offset); i++) { if (removing) { unfoldedObjects.RemoveAt(unfoldedObjects.Count - 1); combineAllMeshesShortcut.RemoveAt(combineAllMeshesShortcut.Count - 1); } else { unfoldedObjects.Add(false); combineAllMeshesShortcut.Add(false); } } } }
private bool NeedToReload() { if (ObjSorter.GetOptShaders() == null) { return(true); } else { return(false); } }
//used to deactivate the "Bake Atlas" button if we dont have anything to bake private bool CheckEmptyArray() { for (int i = 0; i < ObjSorter.GetOptShaders().Count; i++) { if (ObjSorter.GetOptShaders()[i].Objects.Count > 1 || //check that at least there are 2 objects (regardless if tex are null) OR (ObjSorter.GetOptShaders()[i].Objects.Count == 1 && (ObjSorter.GetOptShaders()[i].Objects[0] != null && ObjSorter.GetOptShaders()[i].Objects[0].ObjHasMoreThanOneMaterial))) //there is at least 1 object that has multiple materials { return(true); } } return(false); }
private void EmptyObjsAndTexturesArray() { objectsSize = 1; ObjSorter.AdjustArraysSize(objectsSize); for (int i = 0; i < ObjSorter.GetOptShaders().Count; i++) { for (int j = 0; j < ObjSorter.GetOptShaders()[i].Objects.Count; j++) { ObjSorter.GetOptShaders()[i].SetObjectAtIndex(j, null); } ObjSorter.GetOptShaders()[i].Objects.Clear(); } }
private bool AtLeast1ObjectMarkedToCombine() { for (int i = 0; i < ObjSorter.GetOptShaders().Count; i++) { for (int j = 0; j < ObjSorter.GetOptShaders()[i].CombineMeshesFlags.Count; j++) { if (ObjSorter.GetOptShaders()[i].CombineMeshesFlags[j]) { return(true); } } } return(false); }
//checks if a gameObject is already in the list. private bool ObjectRepeated(GameObject g) { if (g == null) { return(false); } int instanceID = g.GetInstanceID(); for (int i = 0; i < ObjSorter.GetOptShaders().Count; i++) { for (int j = 0; j < ObjSorter.GetOptShaders()[i].Objects.Count; j++) { if (ObjSorter.GetOptShaders()[i].Objects[j] != null && instanceID == ObjSorter.GetOptShaders()[i].Objects[j].GameObj.GetInstanceID()) { return(true); } } } return(false); }
bool clickOnClearAtlas = false;//flag that is used when user clear atlas and asks if it wants to be deleted or not 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); ObjectSearchGUI.Instance.ClearConsole(); menuOptions[0] = "Objects"; break; case 1: ObjectSearchGUI.Instance.DrawGUI(window); menuOptions[0] = "Objects(" + ObjSorter.GetTotalSortedObjects() + ")"; break; case 2: SettingsMenuGUI.Instance.DrawGUI(window); menuOptions[0] = "Objects(" + ObjSorter.GetTotalSortedObjects() + ")"; break; default: Debug.LogError("Unrecognized menu option: " + selectedMenuOption); break; } if (clickOnClearAtlas) { GUIStyle centeredStyle = GUI.skin.GetStyle("Label"); centeredStyle.alignment = TextAnchor.UpperCenter; GUI.Label(new Rect(5, window.position.height - 42, window.position.width - 10, 33), "Are you sure you want to clear the current atlas?", centeredStyle); if (GUI.Button(new Rect(5, window.position.height - 27, window.position.width / 2 - 10, 25), "YES")) { System.GC.Collect(); clickOnClearAtlas = false; GameObject[] objsInHierarchy = Utils.GetAllObjectsInHierarchy(); foreach (GameObject obj in objsInHierarchy) { if (obj != null) { if (obj.name.Contains(Constants.OptimizedObjIdentifier)) { DestroyImmediate(obj); } else { if (obj.GetComponent <SkinnedMeshRenderer>() != null)//checks first if it has a skinned mesh renderer and activates it else it just activates the mesh renderer { obj.GetComponent <SkinnedMeshRenderer>().enabled = true; } else if (obj.GetComponent <MeshRenderer>() != null) { obj.GetComponent <MeshRenderer>().enabled = true; } } } } // delete the folder where the atlas reside. string folderOfAtlas = (PersistenceHandler.Instance.PathToSaveOptimizedObjs != "") ? PersistenceHandler.Instance.PathToSaveOptimizedObjs + Path.DirectorySeparatorChar + Utils.GetCurrentSceneName() : #if UNITY_5_4_OR_NEWER UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path; #else EditorApplication.currentScene; #endif #if UNITY_5_4_OR_NEWER if (UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path == "") { #else if (EditorApplication.currentScene == "") { #endif 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(); } } if (GUI.Button(new Rect(window.position.width / 2, window.position.height - 27, window.position.width / 2 - 5, 25), "Cancel")) { clickOnClearAtlas = false; } } else { if (GUI.Button(new Rect(5, window.position.height - 35, window.position.width / 2 - 10, 33), "Clear Atlas")) { clickOnClearAtlas = true; } 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 - 4, 33), "Bake Atlas")) { System.GC.Collect(); //Remove objects that are already optimized and start over. if (SettingsMenuGUI.Instance.RemoveObjectsBeforeBaking) { GameObject[] objsInHierarchy = Utils.GetAllObjectsInHierarchy(); foreach (GameObject obj in objsInHierarchy) { if (obj != null && obj.name.Contains(Constants.OptimizedObjIdentifier)) { GameObject.DestroyImmediate(obj); } } } string progressBarInfo = "Please wait..."; int shadersToOptimize = ObjSorter.GetOptShaders().Count; float pace = 1 / (float)shadersToOptimize; float progress = pace; List <Tuple <GameObject, int> > optimizedObjects = new List <Tuple <GameObject, int> >();//only useful when generating a hierarchy; ints are the InstanceIDs of each game obj parent. bool canGenerateHierarchies = true; for (int i = 0; i < shadersToOptimize; i++) { EditorUtility.DisplayProgressBar("Optimization in progress... " + (SettingsMenuGUI.Instance.CreatePrefabsForObjects ? " Get coffee this will take some time..." : ""), progressBarInfo, progress); progressBarInfo = "Processing shader: " + ObjSorter.GetOptShaders()[i].ShaderName; ObjSorter.GetOptShaders()[i].OptimizeShader(SettingsMenuGUI.Instance.ReuseTextures, SettingsMenuGUI.Instance.CreatePrefabsForObjects, SettingsMenuGUI.Instance.GenerateAtlassesPowerOf2); if (SettingsMenuGUI.Instance.GenerateHierarchiesForOptimizedObjects) { optimizedObjects.AddRange(ObjSorter.GetOptShaders()[i].GetOptimizedObjects()); if (canGenerateHierarchies) { canGenerateHierarchies = ObjSorter.GetOptShaders()[i].CanGenerateHierarchy(); } } progress += pace; } //chequear que no haya ningun objeto combinado para poder generar jerarquias if (SettingsMenuGUI.Instance.GenerateHierarchiesForOptimizedObjects) { if (canGenerateHierarchies) { progressBarInfo = "Generating hierarchies..."; Utils.GenerateHierarchy(optimizedObjects); } else { Debug.LogError("Cant generate hierarchies if you combine objects."); } } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh();//reimport the created atlases so they get displayed in the editor. } /*if(GUI.Button(new Rect(window.position.width-81, window.position.height - 35, 81-2,33), "Combine\nmeshes only")) { * //TODO * }*/ } }
//Fills the array of textures with the selected objects in the hierarchy view //adds to the end all the objects. public void FillArrayWithSelectedObjects(GameObject[] arr) { //dont include already optimized objects List <GameObject> filteredArray = new List <GameObject>(); for (int i = 0; i < arr.Length; i++) { if (!arr[i].name.Contains(Constants.OptimizedObjIdentifier)) { filteredArray.Add(arr[i]); } else { Debug.LogWarning("Skipping " + arr[i].name + " game object as is already optimized."); } } bool filledTexture = false; for (int i = 0; i < filteredArray.Count; i++) { filledTexture = false; for (int j = 0; j < ObjSorter.GetOptShaders().Count; j++) { for (int k = 0; k < ObjSorter.GetOptShaders()[j].Objects.Count; k++) { if (ObjSorter.GetOptShaders()[j].Objects[k] == null) { if (!ObjectRepeated(filteredArray[i])) { ObjSorter.GetOptShaders()[j].SetObjectAtIndex(k, new OptimizableObject(filteredArray[i])); filledTexture = true; break; } else { Debug.LogWarning("Game Object " + filteredArray[i].name + " is already in the list."); } } } if (filledTexture) { break; } } //if we didnt find an empty spot in the array, lets just add it to the texture list. if (!filledTexture) { if (!ObjectRepeated(filteredArray[i])) { ObjSorter.AddObject(filteredArray[i]); //adds also null internally to increase space for textures filledTexture = true; objectsSize++; } else { Debug.LogWarning("Game Object " + filteredArray[i].name + " is already in the list."); } } } }
public void DrawGUI(ProDrawCallOptimizerMenu window) { GUILayout.BeginArea(new Rect(5, 30, window.position.width - 10, 75)); GUILayout.Space(3); GUILayout.BeginHorizontal(); if (GUILayout.Button("Add all scene\nobjects", GUILayout.Width(85), GUILayout.Height(32))) { EmptyObjsAndTexturesArray(); FillArrayWithSelectedObjects(Utils.GetAllObjectsInHierarchy()); return; //wait for next frame to recalculate objects } GUI.enabled = (Selection.activeGameObject != null); if (GUILayout.Button("Add selected\nobjects", GUILayout.Width(85), GUILayout.Height(32))) { FillArrayWithSelectedObjects(Selection.gameObjects); return; } if (GUILayout.Button("Add selected\nand children", GUILayout.Width(85), GUILayout.Height(32))) { GameObject[] selectedGameObjects = Selection.gameObjects; List <GameObject> objsToAdd = new List <GameObject>(); for (int i = 0; i < selectedGameObjects.Length; i++) { Transform[] selectedObjs = selectedGameObjects[i].GetComponentsInChildren <Transform>(true); for (int j = 0; j < selectedObjs.Length; j++) { objsToAdd.Add(selectedObjs[j].gameObject); } } FillArrayWithSelectedObjects(objsToAdd.ToArray()); return; } GUI.enabled = true; GUILayout.BeginVertical(); GUILayout.Space(-0.5f); EditorGUILayout.HelpBox("- Click \'Advanced\' to search objects by tags or layers." + (AdvancedMenuGUI.Instance.CreatePrefabsForObjects ? "\n- You have marked \"Create prefabs\" on the advanced tab, this bake will be SLOW..." : ""), MessageType.Info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); objectsSize = ObjSorter.GetTotalSortedObjects(); GUILayout.Space(5); GUILayout.BeginHorizontal(); GUILayout.BeginVertical(GUILayout.Width(140)); GUILayout.Space(6); GUILayout.Label("Objects to optimize: " + objectsSize, GUILayout.Width(140)); GUILayout.EndVertical(); GUILayout.BeginVertical(GUILayout.Width(25)); if (GUILayout.Button("+", GUILayout.Width(23), GUILayout.Height(12))) { objectsSize++; } GUILayout.Space(-2); if (GUILayout.Button("-", GUILayout.Width(23), GUILayout.Height(12))) { objectsSize--; } GUILayout.EndVertical(); GUILayout.Space(-3); GUILayout.BeginVertical(GUILayout.Width(55)); GUILayout.Space(-0.5f); if (GUILayout.Button("Clear", GUILayout.Width(55), GUILayout.Height(24))) { EmptyObjsAndTexturesArray(); } GUILayout.EndVertical(); GUILayout.BeginVertical(); GUILayout.Space(-6); GUILayout.Label("Atlasses prefix name(Optional):"); GUILayout.Space(-3); customAtlasName = GUILayout.TextField(customAtlasName); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndArea(); /*EditorGUI.HelpBox(new Rect(237, 75, 133, 30), * "If checked, each time there is an atlas baking process starting all the optimized objects get destroyed, un check this when you want manually to keep track of your optimized objects", * MessageType.Info);*/ objectsSize = objectsSize < 1 ? 1 : objectsSize;//no neg size ObjSorter.AdjustArraysSize(objectsSize); ObjSorter.SortObjects(); AdjustArraysWithObjSorter(); arraysScrollPos = GUI.BeginScrollView(new Rect(0, 100, window.position.width, window.position.height - 138), arraysScrollPos, new Rect(0, 0, window.position.width - 20, (ObjSorter.GetTotalSortedObjects() + ObjSorter.GetOptShaders().Count) * (32.5f))); int drawingPos = 0; for (int i = 0; i < ObjSorter.GetOptShaders().Count; i++) { string shaderName = (ObjSorter.GetOptShaders()[i].Objects[0] != null && ObjSorter.GetOptShaders()[i].Objects[0].IsCorrectlyAssembled) ? ObjSorter.GetOptShaders()[i].Objects[0].ShaderName : ""; bool positionIsAShader = (shaderName != ""); string shaderLabel = (i + 1).ToString() + ((positionIsAShader) ? ". Shader: " + shaderName + "." : ". Not optimizable: ") + " (" + ObjSorter.GetOptShaders()[i].Objects.Count + ")"; unfoldedObjects[i] = EditorGUI.Foldout(new Rect(3, drawingPos * 30 + (positionIsAShader ? 19 : 24), 300, 15), unfoldedObjects[i], ""); GUI.Label(new Rect(20, drawingPos * 30 + (positionIsAShader ? 19 : 24), 300, 15), shaderLabel, (positionIsAShader) ? normalStyle : errorStyle); if (positionIsAShader) { if (ObjSorter.GetOptShaders()[i].Objects.Count > 1 || //array has at least more than one texture OR (ObjSorter.GetOptShaders()[i].Objects.Count == 1 && ObjSorter.GetOptShaders()[i].Objects[0].ObjHasMoreThanOneMaterial)) //if there is 1 object that has multiple materials { int aproxAtlasSize = ObjSorter.GetAproxAtlasSize(i, AdvancedMenuGUI.Instance.ReuseTextures); string msg = " Aprox Atlas Size: ~(" + aproxAtlasSize + "x" + aproxAtlasSize + ")+" + (Constants.AtlasResizeFactor * 100) + "%+"; GUIStyle msgStyle = smallTextStyle; if (aproxAtlasSize > Constants.MaxAtlasSize) { msg += " TOO BIG!!!"; msgStyle = smallTextErrorStyle; } else if (aproxAtlasSize > Constants.MaxSupportedUnityTexture) { msg += " Texture will be imported to 4096x4096max"; msgStyle = smallTextWarningStyle; } GUI.Label(new Rect(15, drawingPos * 30 + 33, 300, 10), msg, msgStyle); } else { GUI.Label(new Rect(15, drawingPos * 30 + 33, 300, 10), "Not optimizing as there needs to be at least 2 textures to atlas.", warningStyle); } } if (GUI.Button(new Rect(window.position.width - 40, drawingPos * 30 + 23, 23, 20), "X")) { if (ObjSorter.GetOptShaders().Count > 1) { unfoldedObjects.RemoveAt(i); ObjSorter.Remove(i); } else { ObjSorter.GetOptShaders()[0].Objects.Clear(); ObjSorter.GetOptShaders()[0].Objects.Add(null); //ObjSorter.GetObjs()[0].Clear(); //ObjSorter.GetObjs()[0].Add(null); } return; } drawingPos++; if (unfoldedObjects[i]) { for (int j = 0; j < ObjSorter.GetOptShaders()[i].Objects.Count; j++) { GUI.Label(new Rect(20, drawingPos * 30 + 20 + 6, 30, 25), (j + 1).ToString() + ":"); GameObject testObj = (GameObject)EditorGUI.ObjectField(new Rect(41, drawingPos * 30 + 24, 105, 17), "", (ObjSorter.GetOptShaders()[i].Objects[j] != null) ? ObjSorter.GetOptShaders()[i].Objects[j].GameObj : null, typeof(GameObject), true); //dont let repeated game objects get inserted in the list. if (testObj != null) { if (ObjSorter.GetOptShaders()[i].Objects[j] == null || testObj.GetInstanceID() != ObjSorter.GetOptShaders()[i].Objects[j].GameObj.GetInstanceID()) { if (!ObjectRepeated(testObj)) { ObjSorter.GetOptShaders()[i].Objects[j] = new OptimizableObject(testObj); } else { Debug.LogWarning("Game Object " + testObj.name + " is already in the list."); } } } if (ObjSorter.GetOptShaders()[i].Objects[j] != null) { if (ObjSorter.GetOptShaders()[i].Objects[j].GameObj != null) { if (ObjSorter.GetOptShaders()[i].Objects[j].IsCorrectlyAssembled) { if (ObjSorter.GetOptShaders()[i].Objects[j].MainTexture != null) { EditorGUI.DrawPreviewTexture(new Rect(170, drawingPos * 30 + 18, 25, 25), ObjSorter.GetOptShaders()[i].Objects[j].MainTexture, null, ScaleMode.StretchToFill); GUI.Label(new Rect(198, drawingPos * 30 + 24, 105, 25), ((ObjSorter.GetOptShaders()[i].Objects[j].ObjHasMoreThanOneMaterial)?"~":"") + "(" + ObjSorter.GetOptShaders()[i].Objects[j].TextureSize.x + "x" + ObjSorter.GetOptShaders()[i].Objects[j].TextureSize.y + ")" + ((ObjSorter.GetOptShaders()[i].Objects[j].ObjHasMoreThanOneMaterial)? "+":"")); } else { GUI.Label(new Rect(178, drawingPos * 30 + 16, 85, 25), ((ObjSorter.GetOptShaders()[i].Objects[j].ObjHasMoreThanOneMaterial)? "Aprox":"null")); GUI.Label(new Rect(170, drawingPos * 30 + 28, 85, 25), "(" + ObjSorter.GetOptShaders()[i].Objects[j].TextureSize.x + "x" + ObjSorter.GetOptShaders()[i].Objects[j].TextureSize.y + ")" + ((ObjSorter.GetOptShaders()[i].Objects[j].ObjHasMoreThanOneMaterial)? "+":"")); GUI.Label(new Rect(257, drawingPos * 30 + 17, 125, 20), "No texture found;\ncreating a texture\nwith the color", warningStyle); } if (ObjSorter.GetOptShaders()[i].Objects[j].ObjHasMoreThanOneMaterial) { GUI.Label(new Rect(330, drawingPos * 30 + 17, 59, 30), " Multiple\nMaterials"); } } else //obj not correctly assembled, display log { GUI.Label(new Rect(170, drawingPos * 30 + 18, 125, 14), ObjSorter.GetOptShaders()[i].Objects[j].IntegrityLog[0], errorStyle); GUI.Label(new Rect(170, drawingPos * 30 + 28, 125, 20), ObjSorter.GetOptShaders()[i].Objects[j].IntegrityLog[1], errorStyle); } } else { ObjSorter.RemoveAtPosition(i, j); } } if (GUI.Button(new Rect(150, drawingPos * 30 + 20, 18, 22), "-")) { if (ObjSorter.GetTotalSortedObjects() > 1) { ObjSorter.GetOptShaders()[i].RemoveObjectAt(j); } else { ObjSorter.GetOptShaders()[0].SetObjectAtIndex(0, null); } } drawingPos++; } } } GUI.EndScrollView(); }
void OnGUI() { if (NeedToReload()) { ReloadDataStructures(); } selectedMenuOption = GUI.SelectionGrid(new Rect(5, 8, window.position.width - 10, 20), selectedMenuOption, menuOptions, 2); switch (selectedMenuOption) { case 0: ObjectsGUI.Instance.DrawGUI(window); AdvancedMenuGUI.Instance.ClearConsole(); menuOptions[0] = "Objects"; break; case 1: 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); } } } string progressBarInfo = "Please wait..."; int shadersToOptimize = ObjSorter.GetOptShaders().Count; float pace = 1 / (float)shadersToOptimize; float progress = pace; for (int i = 0; i < shadersToOptimize; i++) { EditorUtility.DisplayProgressBar("Optimization in progress... " + (AdvancedMenuGUI.Instance.CreatePrefabsForObjects ? " Get coffee this will take some time..." : ""), progressBarInfo, progress); progressBarInfo = "Processing shader: " + ObjSorter.GetOptShaders()[i].ShaderName; ObjSorter.GetOptShaders()[i].OptimizeShader(AdvancedMenuGUI.Instance.ReuseTextures, AdvancedMenuGUI.Instance.CreatePrefabsForObjects); progress += pace; } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); //reimport the created atlases so they get displayed in the editor. } }