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 static List <Vector2> offsets; //used for tiling of the textures. //objToCombine should be correctly assembled,meaning has MeshRenderer,filter and shares the same type of shader across materials //combines the number of materials in the mesh renderer, not the submesh count. public static GameObject CombineMaterials(GameObject objToCombine, string shaderUsed) { List <string> shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderUsed); Material[] materialsToCombine = objToCombine.GetComponent <MeshRenderer>().sharedMaterials; GetTexturesScalesAndOffsetsForShaderDefine(objToCombine, shaderDefines[0]); int atlasSize = CalculateAproxAtlasSizeForMaterials(objToCombine, shaderUsed); Atlasser atlas = new Atlasser(atlasSize, atlasSize); // generate atlas for the initial textures int resizeTimes = 1; for (int i = 0; i < texturesToAtlas.Count; i++) { Node resultNode = atlas.Insert(texturesToAtlas[i].width, texturesToAtlas[i].height); if (resultNode == null) { int resizedAtlasSize = atlasSize + Mathf.RoundToInt((float)atlasSize * Constants.AtlasResizeFactor * resizeTimes); atlas = new Atlasser(resizedAtlasSize, resizedAtlasSize); i = -1;//at the end of the loop 1 will be added and it will start in 0 resizeTimes++; } } //with the generated atlas, save the textures and load them and add them to the combinedMaterial string pathToAtlas = CreateFolderForCombinedObject(objToCombine); string fileName = "MaterialAtlas " + shaderUsed.Replace('/', '_'); string atlasTexturePath = pathToAtlas + Path.DirectorySeparatorChar + fileName; //create material and fill with the combined to be textures in the material Material combinedMaterial = new Material(Shader.Find(shaderUsed)); AssetDatabase.CreateAsset(combinedMaterial, atlasTexturePath + "Mat.mat"); AssetDatabase.ImportAsset(atlasTexturePath + "Mat.mat"); //AssetDatabase.Refresh(); combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(atlasTexturePath + "Mat.mat", typeof(Material)); for (int i = 0; i < shaderDefines.Count; i++) { GetTexturesScalesAndOffsetsForShaderDefine(objToCombine, shaderDefines[i]); // Debug.Log ("Creating atlas from multiple materials on gameobject"); atlas.SaveAtlasToFile(atlasTexturePath + i.ToString() + ".png", texturesToAtlas, scales, offsets); AssetDatabase.ImportAsset(atlasTexturePath + i.ToString() + ".png"); //AssetDatabase.Refresh(); Texture2D savedAtlasTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(atlasTexturePath + i.ToString() + ".png", typeof(Texture2D)); combinedMaterial.SetTexture(shaderDefines[i], savedAtlasTexture); } //AssetDatabase.Refresh(); Mesh masterMesh = objToCombine.GetComponent <MeshFilter>().sharedMesh; Mesh[] subMeshes = new Mesh[materialsToCombine.Length]; for (int i = 0; i < subMeshes.Length; i++) { subMeshes[i] = ExtractMesh(masterMesh, i); Vector2[] remappedUVs = subMeshes[i].uv; for (int j = 0; j < remappedUVs.Length; j++) { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlas.AtlasWidth, atlas.AtlasHeight, atlas.TexturePositions[i], objToCombine.name); } subMeshes[i].uv = remappedUVs; } GameObject combinedObj = GameObject.Instantiate(objToCombine, objToCombine.transform.position, objToCombine.transform.rotation) as GameObject; combinedObj.GetComponent <MeshRenderer>().sharedMaterials = new Material[] { combinedMaterial }; combinedObj.GetComponent <MeshFilter>().sharedMesh = Utils.CombineMeshes(subMeshes); combinedObj.transform.parent = objToCombine.transform.parent; combinedObj.transform.localScale = objToCombine.transform.localScale; combinedObj.name = objToCombine.name; return(combinedObj); }
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 static List <Vector2> offsets; //used for tiling of the textures. //objToCombine should be correctly assembled,meaning has MeshRenderer,filter and shares the same type of shader across materials //combines the number of materials in the mesh renderer, not the submesh count. public static GameObject CombineMaterials(GameObject objToCombine, string shaderUsed, bool usesSkinnedMeshRenderer) { List <string> shaderDefines = ShaderManager.Instance.GetShaderTexturesDefines(shaderUsed); Material[] materialsToCombine = usesSkinnedMeshRenderer ? objToCombine.GetComponent <SkinnedMeshRenderer>().sharedMaterials : objToCombine.GetComponent <MeshRenderer>().sharedMaterials; GetTexturesScalesAndOffsetsForShaderDefine(materialsToCombine, shaderDefines[0]); int objToCombineID = objToCombine.GetInstanceID(); int atlasSize = CalculateAproxAtlasSizeForMaterials(materialsToCombine, shaderUsed); Atlasser atlas = new Atlasser(atlasSize, atlasSize, false /*dont force the atlas to be power of 2 as we are combining a material*/); string atlasName = "Atlas"; // generate atlas for the initial textures int resizeTimes = 1; for (int i = 0; i < texturesToAtlas.Count; i++) { Node resultNode = atlas.Insert(texturesToAtlas[i].width, texturesToAtlas[i].height); string texturePath = AssetDatabase.GetAssetPath(texturesToAtlas[i]).Replace(Path.DirectorySeparatorChar, '-'); if (texturePath == "") //means we have a colored material, lets just peek at the color and get a hash { texturePath = Utils.CalculateMD5Hash(texturesToAtlas[i].GetPixel(0, 0).ToString()).ToString(); } atlasName += texturePath; if (resultNode == null) { int resizedAtlasSize = atlasSize + Mathf.RoundToInt((float)atlasSize * Constants.AtlasResizeFactor * resizeTimes); atlas = new Atlasser(resizedAtlasSize, resizedAtlasSize, false /*dont force the atlas to be power of 2 as we are combining a material*/); atlasName = "Atlas"; i = -1;//at the end of the loop 1 will be added and it will start in 0 resizeTimes++; } } atlasName = "Tex" + Utils.CalculateMD5Hash(atlasName).ToString();//Remove the MD5 for debuging purposes to know where each tex comes from //with the generated atlas, save the textures and load them and add them to the combinedMaterial string pathToCombinedMaterials = CreateFolderForCombinedObjects(); string atlasTexturePath = pathToCombinedMaterials + Path.DirectorySeparatorChar; //create material and fill with the combined to be textures in the material string shaderMaterialName = shaderUsed; if (Utils.IsShaderStandard(shaderMaterialName)) { shaderMaterialName = Utils.ExtractStandardShaderOriginalName(shaderMaterialName); } Material combinedMaterial = new Material(Shader.Find(shaderMaterialName)); string materialPathAndName = atlasTexturePath + "Mat" + objToCombineID + ".mat"; AssetDatabase.CreateAsset(combinedMaterial, materialPathAndName); AssetDatabase.ImportAsset(materialPathAndName); combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(materialPathAndName, typeof(Material)); for (int i = 0; i < shaderDefines.Count; i++) { string scaleOffsetsID = GetTexturesScalesAndOffsetsForShaderDefine(materialsToCombine, shaderDefines[i]); string atlasTexturePathAndName = atlasTexturePath + atlasName + i.ToString() + scaleOffsetsID + ".png"; if (!File.Exists(atlasTexturePathAndName)) { atlas.SaveAtlasToFile(atlasTexturePathAndName, texturesToAtlas, scales, offsets); } AssetDatabase.ImportAsset(atlasTexturePathAndName); Texture2D savedAtlasTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(atlasTexturePathAndName, typeof(Texture2D)); combinedMaterial.SetTexture(shaderDefines[i], savedAtlasTexture); } Mesh masterMesh = usesSkinnedMeshRenderer ? objToCombine.GetComponent <SkinnedMeshRenderer>().sharedMesh : objToCombine.GetComponent <MeshFilter>().sharedMesh; Mesh[] subMeshes = new Mesh[materialsToCombine.Length]; for (int i = 0; i < subMeshes.Length; i++) { //Debug.Log("Number of Meshes: " + subMeshes.Length + " i: " + i); subMeshes[i] = ExtractMesh(masterMesh, i); Vector2[] remappedUVs = subMeshes[i].uv; bool generatedTexture = (materialsToCombine[i].mainTexture == null); for (int j = 0; j < remappedUVs.Length; j++) { remappedUVs[j] = Utils.ReMapUV(remappedUVs[j], atlas.AtlasWidth, atlas.AtlasHeight, atlas.TexturePositions[i], objToCombine.name, generatedTexture); } subMeshes[i].uv = remappedUVs; } GameObject combinedObj = GameObject.Instantiate(objToCombine, objToCombine.transform.position, objToCombine.transform.rotation) as GameObject; if (usesSkinnedMeshRenderer) { combinedObj.GetComponent <SkinnedMeshRenderer>().sharedMaterials = new Material[] { combinedMaterial }; combinedObj.GetComponent <SkinnedMeshRenderer>().sharedMesh = Utils.CombineMeshes(subMeshes); combinedObj.GetComponent <SkinnedMeshRenderer>().sharedMesh.boneWeights = objToCombine.GetComponent <SkinnedMeshRenderer>().sharedMesh.boneWeights; combinedObj.GetComponent <SkinnedMeshRenderer>().sharedMesh.bindposes = objToCombine.GetComponent <SkinnedMeshRenderer>().sharedMesh.bindposes; } else { combinedObj.GetComponent <MeshRenderer>().sharedMaterials = new Material[] { combinedMaterial }; combinedObj.GetComponent <MeshFilter>().sharedMesh = Utils.CombineMeshes(subMeshes); } combinedObj.transform.parent = objToCombine.transform.parent; combinedObj.transform.localScale = objToCombine.transform.localScale; combinedObj.name = objToCombine.name; return(combinedObj); }
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); }
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); }