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(); } } } }
private Material CreateAtlasMaterialAndTexture(Atlasser generatedAtlas, string shaderToAtlas, 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 = GetTexturesToAtlasForShaderDefine(shaderDefines[k]); //Get thtextures for the property shderDefines[k] to atlas them List <Vector2> scales = GetScalesToAtlasForShaderDefine(shaderDefines[k]); List <Vector2> offsets = GetOffsetsToAtlasForShaderDefine(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); }
private void OptimizeDrawCalls(ref Material atlasMaterial, float atlasWidth, float atlasHeight, List <Rect> texturePos, bool reuseTextures, TextureReuseManager texReuseMgr, bool generatePrefabsForObjects) { GameObject trash = new GameObject("Trash");//stores unnecesary objects that might be cloned and are children of objects // // // when generating prefabs // // // string folderToSavePrefabs = EditorApplication.currentScene; if (generatePrefabsForObjects) { if (folderToSavePrefabs == "") //scene is 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(); } } /////////////////////////////////////////// for (int i = 0; i < objects.Count; i++) { string optimizedObjID = objects[i].GameObj.name + Constants.OptimizedObjIdentifier; objects[i].GameObj.GetComponent <MeshRenderer>().enabled = true;//activate renderers for instantiating GameObject instance = GameObject.Instantiate(objects[i].GameObj, objects[i].GameObj.transform.position, objects[i].GameObj.transform.rotation) as GameObject; Undo.RegisterCreatedObjectUndo(instance, "CreateObj" + optimizedObjID); //remove children of the created instance. Transform[] children = instance.GetComponentsInChildren <Transform>(); for (int j = 0; j < children.Length; j++) { children[j].transform.parent = trash.transform; } instance.transform.parent = objects[i].GameObj.transform.parent; instance.transform.localScale = objects[i].GameObj.transform.localScale; instance.GetComponent <Renderer>().sharedMaterial = atlasMaterial; instance.name = optimizedObjID; instance.GetComponent <MeshFilter>().sharedMesh = Utils.CopyMesh(objects[i].GameObj.GetComponent <MeshFilter>().sharedMesh); //Remap uvs Mesh remappedMesh = instance.GetComponent <MeshFilter>().sharedMesh; Vector2[] remappedUVs = instance.GetComponent <MeshFilter>().sharedMesh.uv; for (int j = 0; j < remappedUVs.Length; j++) { if (reuseTextures) { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlasWidth, atlasHeight, texReuseMgr.GetTextureRefPosition(objects[i]), instance.name); } else { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlasWidth, atlasHeight, texturePos[i], instance.name); } } remappedMesh.uv = remappedUVs; instance.GetComponent <MeshFilter>().sharedMesh = remappedMesh; Undo.RecordObject(objects[i].GameObj.GetComponent <MeshRenderer>(), "Active Obj"); //if the gameObject has multiple materials, search for the original one (the uncombined) in order to deactivate it if (objects[i].ObjWasCombined) { objects[i].UncombinedObject.GetComponent <MeshRenderer>().enabled = false; } else { objects[i].GameObj.GetComponent <MeshRenderer>().enabled = false; } if (generatePrefabsForObjects) { string prefabName = Utils.GetValiName(instance.name) + " " + instance.GetInstanceID(); string assetPath = folderToSavePrefabs + Path.DirectorySeparatorChar + prefabName; AssetDatabase.CreateAsset(instance.GetComponent <MeshFilter>().sharedMesh, assetPath + ".asset"); PrefabUtility.CreatePrefab(assetPath + ".prefab", instance, ReplacePrefabOptions.ConnectToPrefab); } } GameObject.DestroyImmediate(trash); }
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. } }
private Material CreateAtlasMaterialAndTexture(Atlasser generatedAtlas, string shaderToAtlas, TextureReuseManager textureReuseManager, int atlasNumber /*in case we have several atlases*/, int start, int end /*start & end used for multiple atlases*/) { string fileName = ((ObjectsGUI.CustomAtlasName == "") ? "Atlas " : (ObjectsGUI.CustomAtlasName + " ")) + atlasNumber + shaderToAtlas.Replace('/', '_'); string folderToSaveAssets = (PersistenceHandler.Instance.PathToSaveOptimizedObjs != "") ? PersistenceHandler.Instance.PathToSaveOptimizedObjs + Path.DirectorySeparatorChar + Utils.GetCurrentSceneName() : EditorSceneManager.GetActiveScene().path; //<- 5.4+ -- 5.3 -> EditorApplication.currentScene; if (EditorSceneManager.GetActiveScene().path == "") //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(standardShader ? Utils.ExtractStandardShaderOriginalName(shaderToAtlas) : 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; if (standardShader) { shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderToAtlas, false, objectsToOptimize[0].ObjectMaterial);//we need the 1rst object in the list to know what textures are used. } else { shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderToAtlas); } for (int k = 0; k < shaderDefines.Count; k++) //go trough each property of the shader. { if (SettingsMenuGUI.Instance.ReuseTextures) //if we are reusing textures, get all the textures and then filter them by the TextureReuseManager { start = 0; end = objectsToOptimize.Count; } List <Texture2D> texturesOfShader = GetTexturesToAtlasForShaderDefine(shaderDefines[k], start, end);//Get the textures for the property shaderDefines[k] to atlas them List <Vector2> scales = GetScalesToAtlasForShaderDefine(shaderDefines[k], start, end); List <Vector2> offsets = GetOffsetsToAtlasForShaderDefine(shaderDefines[k], start, end); if (SettingsMenuGUI.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 shaderDefines[k] for shader shaderToAtlas tex); } return(atlasMaterial); }
//Optimizable object slice is a slice of the optimizableObjects in case they dont fit in a simple atlas private void OptimizeDrawCalls(List <OptimizableObject> objectsToOptimizeSlice, ref Material atlasMaterial, float atlasWidth, float atlasHeight, List <Rect> texturePos, bool reuseTextures, TextureReuseManager texReuseMgr, bool generatePrefabsForObjects) { GameObject trash = new GameObject("Trash");//stores unnecesary objects that might be cloned and are children of objects for (int i = 0; i < objectsToOptimizeSlice.Count; i++) { string optimizedObjStrID = objectsToOptimizeSlice[i].GameObj.name + Constants.OptimizedObjIdentifier; if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { objectsToOptimizeSlice[i].GameObj.GetComponent <SkinnedMeshRenderer>().enabled = true;//activate renderers for instantiating } else { objectsToOptimizeSlice[i].GameObj.GetComponent <MeshRenderer>().enabled = true; } GameObject instance = GameObject.Instantiate(objectsToOptimizeSlice[i].GameObj, objectsToOptimizeSlice[i].GameObj.transform.position, objectsToOptimizeSlice[i].GameObj.transform.rotation) as GameObject; Undo.RegisterCreatedObjectUndo(instance, "CreateObj" + optimizedObjStrID); //remove children of the created instance. Transform[] children = instance.GetComponentsInChildren <Transform>(); for (int j = 0; j < children.Length; j++) { children[j].transform.parent = trash.transform; } instance.transform.parent = objectsToOptimizeSlice[i].GameObj.transform.parent; instance.transform.localScale = objectsToOptimizeSlice[i].GameObj.transform.localScale; if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { instance.GetComponent <SkinnedMeshRenderer>().sharedMaterial = atlasMaterial; } else { instance.GetComponent <MeshRenderer>().sharedMaterial = atlasMaterial; } instance.name = optimizedObjStrID; if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { instance.GetComponent <SkinnedMeshRenderer>().sharedMesh = Utils.CopyMesh(objectsToOptimizeSlice[i].GameObj.GetComponent <SkinnedMeshRenderer>().sharedMesh); } else { instance.GetComponent <MeshFilter>().sharedMesh = Utils.CopyMesh(objectsToOptimizeSlice[i].GameObj.GetComponent <MeshFilter>().sharedMesh); } // ************************************ Remap uvs ***************************************** // Mesh remappedMesh = objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer ? instance.GetComponent <SkinnedMeshRenderer>().sharedMesh : instance.GetComponent <MeshFilter>().sharedMesh; Vector2[] remappedUVs = remappedMesh.uv;//objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer ? instance.GetComponent<SkinnedMeshRenderer>().sharedMesh.uv : instance.GetComponent<MeshFilter>().sharedMesh.uv; Vector2[] remappedUVs2 = remappedMesh.uv2; Vector2[] remappedUVs3 = remappedMesh.uv3; Vector2[] remappedUVs4 = remappedMesh.uv4; bool hasUv2Channel = remappedUVs2.Length > 0; bool hasUv3Channel = remappedUVs3.Length > 0; bool hasUv4Channel = remappedUVs4.Length > 0; bool generatedTexture = objectsToOptimizeSlice[i].MainTexture == null; for (int j = 0; j < remappedUVs.Length; j++) { if (reuseTextures) { if (SettingsMenuGUI.Instance.ModifyMainUV) { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlasWidth, atlasHeight, texReuseMgr.GetTextureRefPosition(objectsToOptimizeSlice[i]), instance.name, generatedTexture); } if (hasUv2Channel && SettingsMenuGUI.Instance.ModifyUV2) { remappedUVs2[j] = Utils.ReMapUV(remappedUVs2[j], atlasWidth, atlasHeight, texReuseMgr.GetTextureRefPosition(objectsToOptimizeSlice[i]), instance.name, generatedTexture); } if (hasUv3Channel && SettingsMenuGUI.Instance.ModifyUV3) { remappedUVs3[j] = Utils.ReMapUV(remappedUVs3[j], atlasWidth, atlasHeight, texReuseMgr.GetTextureRefPosition(objectsToOptimizeSlice[i]), instance.name, generatedTexture); } if (hasUv4Channel && SettingsMenuGUI.Instance.ModifyUV4) { remappedUVs4[j] = Utils.ReMapUV(remappedUVs4[j], atlasWidth, atlasHeight, texReuseMgr.GetTextureRefPosition(objectsToOptimizeSlice[i]), instance.name, generatedTexture); } } else { if (SettingsMenuGUI.Instance.ModifyMainUV) { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlasWidth, atlasHeight, texturePos[i], instance.name, generatedTexture); } if (hasUv2Channel && SettingsMenuGUI.Instance.ModifyUV2) { remappedUVs2[j] = Utils.ReMapUV(remappedUVs2[j], atlasWidth, atlasHeight, texturePos[i], instance.name, generatedTexture); } if (hasUv3Channel && SettingsMenuGUI.Instance.ModifyUV3) { remappedUVs3[j] = Utils.ReMapUV(remappedUVs3[j], atlasWidth, atlasHeight, texturePos[i], instance.name, generatedTexture); } if (hasUv4Channel && SettingsMenuGUI.Instance.ModifyUV4) { remappedUVs4[j] = Utils.ReMapUV(remappedUVs4[j], atlasWidth, atlasHeight, texturePos[i], instance.name, generatedTexture); } } } remappedMesh.uv = remappedUVs; if (hasUv2Channel) { remappedMesh.uv2 = remappedUVs2; } if (hasUv3Channel) { remappedMesh.uv3 = remappedUVs3; } if (hasUv4Channel) { remappedMesh.uv4 = remappedUVs4; } if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { instance.GetComponent <SkinnedMeshRenderer>().sharedMesh = remappedMesh; Undo.RecordObject(objectsToOptimizeSlice[i].GameObj.GetComponent <SkinnedMeshRenderer>(), "Active Obj"); } else { instance.GetComponent <MeshFilter>().sharedMesh = remappedMesh; Undo.RecordObject(objectsToOptimizeSlice[i].GameObj.GetComponent <MeshRenderer>(), "Active Obj"); } //if the gameObject has multiple materials, search for the original one (the uncombined) in order to deactivate it if (objectsToOptimizeSlice[i].ObjWasCombined) { if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { objectsToOptimizeSlice[i].UncombinedObject.GetComponent <SkinnedMeshRenderer>().enabled = false; } else { objectsToOptimizeSlice[i].UncombinedObject.GetComponent <MeshRenderer>().enabled = false; } } else { if (objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer) { objectsToOptimizeSlice[i].GameObj.GetComponent <SkinnedMeshRenderer>().enabled = false; } else { objectsToOptimizeSlice[i].GameObj.GetComponent <MeshRenderer>().enabled = false; } } if (generatePrefabsForObjects && !combineMeshesFlags[i])//lets not generate a prefab for an object that is marked for combine as later on will be combined and made a prefab { string prefabName = Utils.GetValiName(instance.name) + " " + instance.GetInstanceID(); string assetPath = folderToSavePrefabs + Path.DirectorySeparatorChar + prefabName; Utils.GeneratePrefab(instance, assetPath, objectsToOptimizeSlice[i].UsesSkinnedMeshRenderer); } //useful only when building hierarchies //instanceID of the transform as we are comparing against parent transforms when building hierachies int originalOptimizedObjectInstanceID = objectsToOptimizeSlice[i].ObjWasCombined ? objectsToOptimizeSlice[i].UncombinedObject.transform.GetInstanceID() : objectsToOptimizeSlice[i].GameObj.transform.GetInstanceID(); optimizedObjects.Add(new Tuple <GameObject, int>(instance, originalOptimizedObjectInstanceID)); } GameObject.DestroyImmediate(trash); }
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(); } } } }
private Material CreateAtlasMaterialAndTexture(Atlasser generatedAtlas, string shaderToAtlas, TextureReuseManager textureReuseManager, int atlasNumber /*in case we have several atlases*/, int start, int end /*start & end used for multiple atlases*/) { string fileName = ((ObjectsGUI.CustomAtlasName == "") ? "Atlas " : (ObjectsGUI.CustomAtlasName + " ")) + atlasNumber + shaderToAtlas.Replace('/', '_'); string folderToSaveAssets = (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 == "") //scene is not saved yet. { #endif 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(standardShader ? Utils.ExtractStandardShaderOriginalName(shaderToAtlas) : shaderToAtlas)); /* * if(standardShader) { * atlasMaterial.EnableKeyword("_ALPHAPREMULTIPLY_ON"); * atlasMaterial.SetFloat("_Mode", 2); * atlasMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); * atlasMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); * atlasMaterial.SetInt("_ZWrite", 0); * atlasMaterial.DisableKeyword("_ALPHATEST_ON"); * atlasMaterial.EnableKeyword("_ALPHABLEND_ON"); * atlasMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON"); * atlasMaterial.renderQueue = 3000; * //atlasMaterial.SetFloat("_Mode", 3f);d * } * //ACA CAMBIAR EL RENDERING MODE AL SHADER!!! *///SOLO FUCNIONA PARA RENDER MODE TRANSPARENTE, NO FUNCIONA PARA CUTOUT //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; if (standardShader) { shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderToAtlas, false, objectsToOptimize[0].ObjectMaterial);//we need the 1rst object in the list to know what textures are used. } else { shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderToAtlas); } for (int k = 0; k < shaderDefines.Count; k++) //go trough each property of the shader. { if (SettingsMenuGUI.Instance.ReuseTextures) //if we are reusing textures, get all the textures and then filter them by the TextureReuseManager { start = 0; end = objectsToOptimize.Count; } List <Texture2D> texturesOfShader = GetTexturesToAtlasForShaderDefine(shaderDefines[k], start, end);//Get the textures for the property shaderDefines[k] to atlas them List <Vector2> scales = GetScalesToAtlasForShaderDefine(shaderDefines[k], start, end); List <Vector2> offsets = GetOffsetsToAtlasForShaderDefine(shaderDefines[k], start, end); if (SettingsMenuGUI.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 shaderDefines[k] for shader shaderToAtlas tex); } return(atlasMaterial); }