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.GetObjs() == 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 void EmptyObjsAndTexturesArray() { objectsSize = 1; ObjSorter.AdjustArraysSize(objectsSize); for (int i = 0; i < ObjSorter.GetObjs().Count; i++) { for (int j = 0; j < ObjSorter.GetObjs()[i].Count; j++) { ObjSorter.GetObjs()[i][j] = null; } ObjSorter.GetObjs()[i].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); }
private static void Init() { ObjSorter.Initialize(); window = (ProDrawCallOptimizerMenu)EditorWindow.GetWindow(typeof(ProDrawCallOptimizerMenu)); window.minSize = new Vector2(550, 200); window.Show(); ObjectsGUI.Instance.Initialize(); SettingsMenuGUI.Instance.Initialize(); //menuOptions = new string[] { "Objects", "Advanced" }; just for reference initialized on declaration selectedMenuOption = 0; }
private static void Init() { ObjSorter.Initialize(); window = (ProDrawCallOptimizerMenu)EditorWindow.GetWindow(typeof(ProDrawCallOptimizerMenu)); window.minSize = new Vector2(445, 200); window.Show(); ObjectsGUI.Instance.Initialize(); AdvancedMenuGUI.Instance.Initialize(); menuOptions = new string[] { "Objects", "Advanced" }; selectedMenuOption = 0; }
private Material CreateAtlasMaterialAndTexture(string shaderToAtlas, int shaderIndex, TextureReuseManager textureReuseManager) { string fileName = ((ObjectsGUI.CustomAtlasName == "") ? "Atlas " : (ObjectsGUI.CustomAtlasName + " ")) + shaderToAtlas.Replace('/', '_'); string folderToSaveAssets = EditorApplication.currentScene; if (folderToSaveAssets == "") //scene is not saved yet. { folderToSaveAssets = Constants.NonSavedSceneFolderName + ".unity"; Debug.LogWarning("WARNING: Scene has not been saved, saving baked objects to: " + Constants.NonSavedSceneFolderName + " folder"); } folderToSaveAssets = folderToSaveAssets.Substring(0, folderToSaveAssets.Length - 6) + "-Atlas"; //remove the ".unity" and add "-Atlas" if (!Directory.Exists(folderToSaveAssets)) { Directory.CreateDirectory(folderToSaveAssets); AssetDatabase.ImportAsset(folderToSaveAssets); } string atlasTexturePath = folderToSaveAssets + Path.DirectorySeparatorChar + fileName; //create the material in the project and set the shader material to shaderToAtlas Material atlasMaterial = new Material(Shader.Find(shaderToAtlas)); //save the material to the project view AssetDatabase.CreateAsset(atlasMaterial, atlasTexturePath + "Mat.mat"); AssetDatabase.ImportAsset(atlasTexturePath + "Mat.mat"); //load a reference from the project view to the material (this is done to be able to set the texture to the material in the project view) atlasMaterial = (Material)AssetDatabase.LoadAssetAtPath(atlasTexturePath + "Mat.mat", typeof(Material)); List <string> shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderToAtlas); for (int k = 0; k < shaderDefines.Count; k++) //go trough each property of the shader. { List <Texture2D> texturesOfShader = ObjSorter.GetTexturesToAtlasForShaderDefine(shaderIndex, shaderDefines[k]); //Get thtextures for the property shderDefines[k] to atlas them List <Vector2> scales = ObjSorter.GetScalesToAtlasForShaderDefine(shaderIndex, shaderDefines[k]); List <Vector2> offsets = ObjSorter.GetOffsetsToAtlasForShaderDefine(shaderIndex, shaderDefines[k]); if (AdvancedMenuGUI.Instance.ReuseTextures) { texturesOfShader = Utils.FilterTexsByIndex(texturesOfShader, textureReuseManager.GetTextureIndexes()); scales = Utils.FilterVec2ByIndex(scales, textureReuseManager.GetTextureIndexes()); offsets = Utils.FilterVec2ByIndex(offsets, textureReuseManager.GetTextureIndexes()); } generatedAtlas.SaveAtlasToFile(atlasTexturePath + k.ToString() + ".png", texturesOfShader, scales, offsets); //save the atlas with the retrieved textures AssetDatabase.ImportAsset(atlasTexturePath + k.ToString() + ".png"); Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath(atlasTexturePath + k.ToString() + ".png", typeof(Texture2D)); atlasMaterial.SetTexture(shaderDefines[k], //set property shderDefines[k] for shader shaderToAtlas tex); } return(atlasMaterial); }
//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.GetObjs().Count; i++) { for (int j = 0; j < ObjSorter.GetObjs()[i].Count; j++) { if (ObjSorter.GetObjs()[i][j] != null && instanceID == ObjSorter.GetObjs()[i][j].GameObj.GetInstanceID()) { return(true); } } } return(false); }
private void AdjustArraysWithObjSorter() { if (unfoldedObjects.Count != ObjSorter.GetObjs().Count) { int offset = ObjSorter.GetObjs().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); } else { unfoldedObjects.Add(true); } } } }
//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.GetObjs().Count; j++) { for (int k = 0; k < ObjSorter.GetObjs()[j].Count; k++) { if (ObjSorter.GetObjs()[j][k] == null) { if (!ObjectRepeated(filteredArray[i])) { ObjSorter.GetObjs()[j][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.", 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.GetObjs().Count) * (32.5f))); int drawingPos = 0; for (int i = 0; i < ObjSorter.GetObjs().Count; i++) { string shaderName = (ObjSorter.GetObjs()[i][0] != null && ObjSorter.GetObjs()[i][0].IsCorrectlyAssembled) ? ObjSorter.GetObjs()[i][0].ShaderName : ""; bool shaderRecognized = ShaderManager.Instance.ShaderExists(shaderName); bool positionIsAShader = (shaderName != ""); string shaderLabel = (i + 1).ToString() + ((positionIsAShader) ? ". Shader: " : ". ") + (shaderName == "" ? "Not optimizable: " : shaderName + ".") + " (" + ObjSorter.GetObjs()[i].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, (shaderRecognized || !positionIsAShader) ? normalStyle : errorStyle); if (positionIsAShader) { if (shaderRecognized) { if (ObjSorter.GetObjs()[i].Count > 1 || //array has at least more than one texture OR (ObjSorter.GetObjs()[i].Count == 1 && ObjSorter.GetObjs()[i][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); } } else { GUI.Label(new Rect(15, drawingPos * 30 + 33, 300, 10), "Shader not recognized/supported, add it in the custom shaders tab.", smallTextStyle); } } if (GUI.Button(new Rect(window.position.width - 40, drawingPos * 30 + 23, 23, 20), "X")) { if (ObjSorter.GetObjs().Count > 1) { unfoldedObjects.RemoveAt(i); ObjSorter.Remove(i); } else { ObjSorter.GetObjs()[0].Clear(); ObjSorter.GetObjs()[0].Add(null); } return; } drawingPos++; if (unfoldedObjects[i]) { for (int j = 0; j < ObjSorter.GetObjs()[i].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.GetObjs()[i][j] != null) ? ObjSorter.GetObjs()[i][j].GameObj : null, typeof(GameObject), true); //dont let repeated game objects get inserted in the list. if (testObj != null) { if (ObjSorter.GetObjs()[i][j] == null || testObj.GetInstanceID() != ObjSorter.GetObjs()[i][j].GameObj.GetInstanceID()) { if (!ObjectRepeated(testObj)) { ObjSorter.GetObjs()[i][j] = new OptimizableObject(testObj); } else { Debug.LogWarning("Game Object " + testObj.name + " is already in the list."); } } } if (ObjSorter.GetObjs()[i][j] != null) { if (ObjSorter.GetObjs()[i][j].GameObj != null) { if (ObjSorter.GetObjs()[i][j].IsCorrectlyAssembled) { if (ObjSorter.GetObjs()[i][j].MainTexture != null) { EditorGUI.DrawPreviewTexture(new Rect(170, drawingPos * 30 + 18, 25, 25), ObjSorter.GetObjs()[i][j].MainTexture, null, ScaleMode.StretchToFill); GUI.Label(new Rect(198, drawingPos * 30 + 24, 105, 25), ((ObjSorter.GetObjs()[i][j].ObjHasMoreThanOneMaterial)?"~":"") + "(" + ObjSorter.GetObjs()[i][j].TextureSize.x + "x" + ObjSorter.GetObjs()[i][j].TextureSize.y + ")" + ((ObjSorter.GetObjs()[i][j].ObjHasMoreThanOneMaterial)? "+":"")); } else { GUI.Label(new Rect(178, drawingPos * 30 + 16, 85, 25), ((ObjSorter.GetObjs()[i][j].ObjHasMoreThanOneMaterial)? "Aprox":"null")); GUI.Label(new Rect(170, drawingPos * 30 + 28, 85, 25), "(" + ObjSorter.GetObjs()[i][j].TextureSize.x + "x" + ObjSorter.GetObjs()[i][j].TextureSize.y + ")" + ((ObjSorter.GetObjs()[i][j].ObjHasMoreThanOneMaterial)? "+":"")); GUI.Label(new Rect(257, drawingPos * 30 + 17, 125, 20), "No texture found;\ncreating a texture\nwith the color", warningStyle); } if (ObjSorter.GetObjs()[i][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.GetObjs()[i][j].IntegrityLog[0], errorStyle); GUI.Label(new Rect(170, drawingPos * 30 + 28, 125, 20), ObjSorter.GetObjs()[i][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.GetObjs()[i].RemoveAt(j); } else { ObjSorter.GetObjs()[0][0] = null; } } drawingPos++; } } } GUI.EndScrollView(); }
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 * }*/ } }
private bool objectsNeedToBeSorted = true;//this flag is used to send a message to sort objects when a button is pressed and returns at the end of the button logic. public void DrawGUI(ProDrawCallOptimizerMenu window) { InitializeGUIStyles(); GUILayout.BeginArea(new Rect(5, 30, window.position.width - 10, 90)); GUILayout.Space(3); GUILayout.BeginHorizontal(); if (GUILayout.Button("Add all scene\nobjects", GUILayout.Width(85), GUILayout.Height(32))) { EmptyObjsAndTexturesArray(); FillArrayWithSelectedObjects(Utils.GetAllObjectsInHierarchy()); objectsNeedToBeSorted = true; 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); objectsNeedToBeSorted = true; 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()); objectsNeedToBeSorted = true; return; } GUI.enabled = true; GUILayout.BeginVertical(); GUILayout.Space(-0.5f); EditorGUILayout.HelpBox("- Search objects by tags or layers in\n\'Object Search\'" + (SettingsMenuGUI.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(); 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\nObjects", GUILayout.Width(55), GUILayout.Height(27))) { EmptyObjsAndTexturesArray(); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(); GUILayout.Space(-6); //GUILayout.BeginHorizontal(); GUILayout.Label("Atlases prefix(Optional):", GUILayout.Width(145)); customAtlasName = GUILayout.TextField(customAtlasName); //GUILayout.EndHorizontal(); 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); if (GUI.changed || objectsNeedToBeSorted) { ObjSorter.SortObjects(); objectsNeedToBeSorted = false; } AdjustArraysWithObjSorter(); arraysScrollPos = GUI.BeginScrollView(new Rect(0, 110, window.position.width, window.position.height - 148), 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++) { if (ObjSorter.GetOptShaders()[i].Objects.Count == 0) { break; //might happen when changing scenes. } 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), 350, 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, SettingsMenuGUI.Instance.ReuseTextures, SettingsMenuGUI.Instance.GenerateAtlassesPowerOf2); string msg = " Aprox Atlas Size: ~(" + aproxAtlasSize + "x" + aproxAtlasSize + ")+" + (Constants.AtlasResizeFactor * 100) + "%+"; GUIStyle msgStyle = smallTextStyle; if (aproxAtlasSize > Constants.MaxAtlasSize) { msg += " TOO BIG, Atlas will be split."; msgStyle = smallTextWarningStyle; } else if (aproxAtlasSize > Constants.MaxSupportedUnityTexture) { msg += " Texture might be resized by Unity."; msgStyle = smallTextWarningStyle; } GUI.Label(new Rect(15, drawingPos * 30 + 33, 350, 15), msg, msgStyle); } else { GUI.Label(new Rect(15, drawingPos * 30 + 33, 300, 15), "Not optimizing as there needs to be at least 2 textures to atlas.", warningStyle); } } if (positionIsAShader) { GUI.Label(new Rect(window.position.width - 155, drawingPos * 30 + 26, 100, 20), "Combine:"); if (GUI.Button(new Rect(window.position.width - 78, drawingPos * 30 + 26, 39, 15), combineAllMeshesShortcut[i] ? "all" : "none")) { combineAllMeshesShortcut[i] = !combineAllMeshesShortcut[i]; ObjSorter.GetOptShaders()[i].SetCombineAllMeshes(combineAllMeshesShortcut[i]); } } if (GUI.Button(new Rect(window.position.width - 38, drawingPos * 30 + 23, 23, 20), "X")) { if (ObjSorter.GetOptShaders().Count > 1) { unfoldedObjects.RemoveAt(i); combineAllMeshesShortcut.RemoveAt(i); ObjSorter.Remove(i); } else { ObjSorter.GetOptShaders()[0].Objects.Clear(); ObjSorter.GetOptShaders()[0].Objects.Add(null); } objectsNeedToBeSorted = true; 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"); } bool usesSkinnedMesh = ObjSorter.GetOptShaders()[i].Objects[j].UsesSkinnedMeshRenderer; GUI.enabled = !usesSkinnedMesh; ObjSorter.GetOptShaders()[i].CombineMeshesFlags[j] = GUI.Toggle(new Rect(window.position.width - 50, drawingPos * 30 + 22.5f, 10, 30), ObjSorter.GetOptShaders()[i].CombineMeshesFlags[j], "");//combine checkbox for each obj if (usesSkinnedMesh) { ObjSorter.GetOptShaders()[i].CombineMeshesFlags[j] = false; } GUI.enabled = true; } 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); ObjSorter.GetOptShaders()[i].ForceCalculateAproxAtlasSize(); } else { ObjSorter.GetOptShaders()[0].SetObjectAtIndex(0, null, false); } } drawingPos++; } } } GUI.EndScrollView(); if (AtLeast1ObjectMarkedToCombine()) { EditorGUI.HelpBox(new Rect(12.5f, window.position.height - 65, window.position.width - 25, 27.5f), "Some objects are marked for combine. If you have lightmaps you might need to recalculate them in case combined objects look dark.", MessageType.None); } }
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. } }
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. } }