public bool DoAnyResultMatsUseConsiderMeshUVs() { if (resultType == ResultType.atlas) { if (resultMaterials == null) { return(false); } for (int i = 0; i < resultMaterials.Length; i++) { if (resultMaterials[i].considerMeshUVs) { return(true); } } return(false); } else { if (resultMaterialsTexArray == null) { return(false); } for (int i = 0; i < resultMaterialsTexArray.Length; i++) { MB_MultiMaterialTexArray resMat = resultMaterialsTexArray[i]; for (int j = 0; j < resMat.slices.Count; j++) { if (resMat.slices[j].considerMeshUVs) { return(true); } } } return(false); } }
internal IEnumerator _CreateAtlasesCoroutineTextureArray(MB3_TextureCombiner combiner, ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { MB_TextureArrayResultMaterial[] bakedMatsAndSlices = null; // Validate the formats if (textureArrayOutputFormats == null || textureArrayOutputFormats.Length == 0) { Debug.LogError("No Texture Array Output Formats. There must be at least one entry."); coroutineResult.isFinished = true; yield break; } for (int i = 0; i < textureArrayOutputFormats.Length; i++) { if (!textureArrayOutputFormats[i].ValidateTextureImporterFormatsExistsForTextureFormats(editorMethods, i)) { Debug.LogError("Could not map the selected texture format to a Texture Importer Format. Safest options are ARGB32, or RGB24."); coroutineResult.isFinished = true; yield break; } } for (int resMatIdx = 0; resMatIdx < resultMaterialsTexArray.Length; resMatIdx++) { MB_MultiMaterialTexArray textureArraySliceConfig = resultMaterialsTexArray[resMatIdx]; if (textureArraySliceConfig.combinedMaterial == null) { Debug.LogError("Material is null for Texture Array Slice Configuration: " + resMatIdx + "."); coroutineResult.isFinished = true; yield break; } List <MB_TexArraySlice> slices = textureArraySliceConfig.slices; for (int sliceIdx = 0; sliceIdx < slices.Count; sliceIdx++) { for (int srcMatIdx = 0; srcMatIdx < slices[sliceIdx].sourceMaterials.Count; srcMatIdx++) { MB_TexArraySliceRendererMatPair sourceMat = slices[sliceIdx].sourceMaterials[srcMatIdx]; if (sourceMat.sourceMaterial == null) { Debug.LogError("Source material is null for Texture Array Slice Configuration: " + resMatIdx + " slice: " + sliceIdx); coroutineResult.isFinished = true; yield break; } if (slices[sliceIdx].considerMeshUVs) { if (sourceMat.renderer == null) { Debug.LogError("Renderer is null for Texture Array Slice Configuration: " + resMatIdx + " slice: " + sliceIdx + ". If considerUVs is enabled then a renderer must be supplied for each source material. The same source material can be used multiple times."); coroutineResult.isFinished = true; yield break; } } else { // TODO check for duplicate source mats. } } } } // initialize structure to store results. For texture arrays the structure is two layers deep. // First layer is resultMaterial / submesh (each result material can use a different shader) // Second layer is a set of TextureArrays for the TextureProperties on that result material. int numResultMats = resultMaterialsTexArray.Length; bakedMatsAndSlices = new MB_TextureArrayResultMaterial[numResultMats]; for (int resMatIdx = 0; resMatIdx < bakedMatsAndSlices.Length; resMatIdx++) { bakedMatsAndSlices[resMatIdx] = new MB_TextureArrayResultMaterial(); int numSlices = resultMaterialsTexArray[resMatIdx].slices.Count; MB_AtlasesAndRects[] slices = bakedMatsAndSlices[resMatIdx].slices = new MB_AtlasesAndRects[numSlices]; for (int j = 0; j < numSlices; j++) { slices[j] = new MB_AtlasesAndRects(); } } // Some of the slices will be atlases (more than one atlas per slice). // Do the material combining for these. First loop over the result materials (1 per submeshes). for (int resMatIdx = 0; resMatIdx < bakedMatsAndSlices.Length; resMatIdx++) { yield return(MB_TextureArrays._CreateAtlasesCoroutineSingleResultMaterial(resMatIdx, bakedMatsAndSlices[resMatIdx], resultMaterialsTexArray[resMatIdx], objsToMesh, combiner, textureArrayOutputFormats, resultMaterialsTexArray, customShaderProperties, progressInfo, coroutineResult, saveAtlasesAsAssets, editorMethods, maxTimePerFrame)); if (!coroutineResult.success) { yield break; } } if (coroutineResult.success) { // Save the results into the TextureBakeResults. unpackMat2RectMap(bakedMatsAndSlices); if (editorMethods != null) { editorMethods.GetMaterialPrimaryKeysIfAddressables(textureBakeResults); } textureBakeResults.resultType = MB2_TextureBakeResults.ResultType.textureArray; textureBakeResults.resultMaterials = new MB_MultiMaterial[0]; textureBakeResults.resultMaterialsTexArray = resultMaterialsTexArray; if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Texture2DArrays"); } } else { if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Failed to create Texture2DArrays"); } } }
public static void ConfigureTextureArraysFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterialsTexArrays, SerializedObject textureBaker) { if (mom.GetObjectsToCombine().Count == 0) { Debug.LogError("You need to add some objects to combine before building the texture array result materials."); return; } if (resultMaterialsTexArrays.arraySize > 0) { Debug.LogError("You already have some texture array result materials configured. You must remove these before doing this operation."); return; } if (mom.textureBakeResults == null) { Debug.LogError("Texture Bake Result asset must be set before using this operation."); return; } //validate that the objects to be combined are valid for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) { GameObject go = mom.GetObjectsToCombine()[i]; if (go == null) { Debug.LogError("Null object in list of objects to combine at position " + i); return; } if (MB_Utility.GetMesh(go) == null) { Debug.LogError("Could not get mesh for object in list of objects to combine at position " + i); return; } Renderer r = go.GetComponent <Renderer>(); if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) { Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); return; } if (r.sharedMaterial == null) { Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); return; } } //Will sort into "result material" // slices Dictionary <MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List <Slice> > shader2ResultMat_map = new Dictionary <MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List <Slice> >(); // first pass split by shader and analyse meshes. List <GameObject> objsToCombine = mom.GetObjectsToCombine(); for (int meshIdx = 0; meshIdx < objsToCombine.Count; meshIdx++) { GameObject srcGo = objsToCombine[meshIdx]; Mesh mesh = MB_Utility.GetMesh(srcGo); Renderer r = MB_Utility.GetRenderer(srcGo); if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("1st Pass 'Split By Shader' Processing Mesh: " + mesh + " Num submeshes: " + r.sharedMaterials.Length); } for (int submeshIdx = 0; submeshIdx < r.sharedMaterials.Length; submeshIdx++) { if (r.sharedMaterials[submeshIdx] == null) { continue; } MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo newKey = new MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo(r.sharedMaterials[submeshIdx].shader, r.sharedMaterials[submeshIdx]); // Initially we fill the list of srcMaterials with garbage MB_MaterialAndUVRects. Will get proper ones when we atlas pack. MB_MaterialAndUVRect submeshMaterial = new MB_MaterialAndUVRect( r.sharedMaterials[submeshIdx], new Rect(0, 0, 0, 0), // garbage value false, //garbage value new Rect(0, 0, 0, 0), // garbage value new Rect(0, 0, 0, 0), // garbage value new Rect(0, 0, 1, 1), // garbage value MB_TextureTilingTreatment.unknown, // garbage value r.name); submeshMaterial.objectsThatUse = new List <GameObject>(); submeshMaterial.objectsThatUse.Add(r.gameObject); if (!shader2ResultMat_map.ContainsKey(newKey)) { // this is a new shader create a new result material Slice srcMaterials = new Slice { atlasRects = new List <MB_MaterialAndUVRect>(), numAtlasRects = 1, }; srcMaterials.atlasRects.Add(submeshMaterial); List <Slice> binsOfMatsThatUseShader = new List <Slice>(); binsOfMatsThatUseShader.Add(srcMaterials); if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(" Adding Source Material: " + submeshMaterial.material); } shader2ResultMat_map.Add(newKey, binsOfMatsThatUseShader); } else { // there is a result material that uses this shader. Add this source material Slice srcMaterials = shader2ResultMat_map[newKey][0]; // There should only be one list of source materials if (srcMaterials.atlasRects.Find(x => x.material == submeshMaterial.material) == null) { if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(" Adding Source Material: " + submeshMaterial.material); } srcMaterials.atlasRects.Add(submeshMaterial); } } } } int resMatCount = 0; foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resultMat in shader2ResultMat_map.Keys) { // at this point there there should be only one slice with all the source materials resMatCount++; // For each result material, all source materials are in the first slice. // We will now split these using a texture packer. Each "atlas" generated by the packer will be a slice. { // All source materials should be in the first slice at this point. List <Slice> slices = shader2ResultMat_map[resultMat]; List <Slice> newSlices = new List <Slice>(); Slice firstSlice = slices[0]; List <Material> allMatsThatUserShader = new List <Material>(); List <GameObject> objsThatUseFirstSlice = new List <GameObject>(); for (int i = 0; i < firstSlice.atlasRects.Count; i++) { allMatsThatUserShader.Add(firstSlice.atlasRects[i].material); if (!objsThatUseFirstSlice.Contains(firstSlice.atlasRects[i].objectsThatUse[0])) { objsThatUseFirstSlice.Add(firstSlice.atlasRects[i].objectsThatUse[0]); } } MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); combiner.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; combiner.saveAtlasesAsAssets = false; combiner.fixOutOfBoundsUVs = true; combiner.doMergeDistinctMaterialTexturesThatWouldExceedAtlasSize = true; List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempMat = new Material(resultMat.shader); if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("======== 2nd pass. Use atlas packer to split the first slice into multiple if it exceeds atlas size. "); } combiner.CombineTexturesIntoAtlases(null, null, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults, onlyPackRects: true, splitAtlasWhenPackingIfTooBig: true); if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("======== Completed packing with texture packer. numPackingResults: " + packingResults.Count); } newSlices.Clear(); // The texture packing just split the atlas into multiple atlases. Each atlas will become a "slice". for (int newSliceIdx = 0; newSliceIdx < packingResults.Count; newSliceIdx++) { List <MB_MaterialAndUVRect> sourceMats = new List <MB_MaterialAndUVRect>(); List <MB_MaterialAndUVRect> packedMatRects = (List <MB_MaterialAndUVRect>)packingResults[newSliceIdx].data; HashSet <Rect> distinctAtlasRects = new HashSet <Rect>(); for (int packedMatRectIdx = 0; packedMatRectIdx < packedMatRects.Count; packedMatRectIdx++) { MB_MaterialAndUVRect muvr = packedMatRects[packedMatRectIdx]; distinctAtlasRects.Add(muvr.atlasRect); { Rect encapsulatingRect = muvr.GetEncapsulatingRect(); Vector2 sizeInAtlas_px = new Vector2( packingResults[newSliceIdx].atlasX * encapsulatingRect.width, packingResults[newSliceIdx].atlasY * encapsulatingRect.height); } sourceMats.Add(muvr); } Slice slice = new Slice() { atlasRects = sourceMats, packingResult = packingResults[newSliceIdx], numAtlasRects = distinctAtlasRects.Count, }; newSlices.Add(slice); } // Replace first slice with split version. if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("First slice exceeded atlas size splitting it into " + newSlices.Count + " slices"); } slices.RemoveAt(0); for (int i = 0; i < newSlices.Count; i++) { slices.Insert(i, newSlices[i]); } } } // build the texture array result materials if (shader2ResultMat_map.Count == 0) { Debug.LogError("Found no materials in list of objects to combine"); } mom.resultMaterialsTexArray = new MB_MultiMaterialTexArray[shader2ResultMat_map.Count]; int k = 0; foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resMatKey in shader2ResultMat_map.Keys) { List <Slice> srcSlices = shader2ResultMat_map[resMatKey]; MB_MultiMaterialTexArray mm = mom.resultMaterialsTexArray[k] = new MB_MultiMaterialTexArray(); for (int sliceIdx = 0; sliceIdx < srcSlices.Count; sliceIdx++) { Slice slice = srcSlices[sliceIdx]; MB_TexArraySlice resSlice = new MB_TexArraySlice(); List <Material> usedMats = new List <Material>(); for (int srcMatIdx = 0; srcMatIdx < slice.atlasRects.Count; srcMatIdx++) { MB_MaterialAndUVRect matAndUVRect = slice.atlasRects[srcMatIdx]; List <GameObject> objsThatUse = matAndUVRect.objectsThatUse; for (int objsThatUseIdx = 0; objsThatUseIdx < objsThatUse.Count; objsThatUseIdx++) { GameObject obj = objsThatUse[objsThatUseIdx]; if (!resSlice.ContainsMaterialAndMesh(slice.atlasRects[srcMatIdx].material, MB_Utility.GetMesh(obj))) { resSlice.sourceMaterials.Add( new MB_TexArraySliceRendererMatPair() { renderer = obj, sourceMaterial = slice.atlasRects[srcMatIdx].material } ); } } } { // Should we use considerUVs bool doConsiderUVs = false; // If there is more than one atlas rectangle in a slice then use considerUVs if (slice.numAtlasRects > 1) { doConsiderUVs = true; } else { // There is only one source material, could be: // - lots of tiling (don't want consider UVs) // - We are extracting a small part of a large atlas (want considerUVs) if (slice.packingResult.atlasX >= mom.maxAtlasSize || slice.packingResult.atlasY >= mom.maxAtlasSize) { doConsiderUVs = false; // lots of tiling } else { doConsiderUVs = true; // extracting a small part of an atlas } } resSlice.considerMeshUVs = doConsiderUVs; } mm.slices.Add(resSlice); } // Enforce integrity. If a material appears in more than one slice then all those slices must be considerUVs=true { // collect all distinct materials HashSet <Material> distinctMats = new HashSet <Material>(); Dictionary <Material, int> mat2sliceCount = new Dictionary <Material, int>(); for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { for (int sliceMatIdx = 0; sliceMatIdx < mm.slices[sliceIdx].sourceMaterials.Count; sliceMatIdx++) { Material mat = mm.slices[sliceIdx].sourceMaterials[sliceMatIdx].sourceMaterial; distinctMats.Add(mat); mat2sliceCount[mat] = 0; } } // Count the number of slices that use each material. foreach (Material mat in distinctMats) { for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].ContainsMaterial(mat)) { mat2sliceCount[mat] = mat2sliceCount[mat] + 1; } } } // Check that considerUVs is true for any materials that appear more than once foreach (Material mat in distinctMats) { if (mat2sliceCount[mat] > 1) { for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].ContainsMaterial(mat)) { if (mom.LOG_LEVEL >= MB2_LogLevel.debug && mm.slices[sliceIdx].considerMeshUVs) { Debug.Log("There was a material " + mat + " that was used by more than one slice and considerUVs was false. sliceIdx:" + sliceIdx); } mm.slices[sliceIdx].considerMeshUVs = true; } } } } } // Cleanup. remove "Renderer"s from source materials that do not use considerUVs and delete extra { // put any slices with consider UVs first List <MB_TexArraySlice> newSlices = new List <MB_TexArraySlice>(); for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].considerMeshUVs == true) { newSlices.Add(mm.slices[sliceIdx]); } } // for any slices without considerUVs, remove "renderer" and truncate for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { MB_TexArraySlice slice = mm.slices[sliceIdx]; if (slice.considerMeshUVs == false) { newSlices.Add(slice); HashSet <Material> distinctMats = slice.GetDistinctMaterials(); slice.sourceMaterials.Clear(); foreach (Material mat in distinctMats) { slice.sourceMaterials.Add(new MB_TexArraySliceRendererMatPair() { sourceMaterial = mat }); } } } mm.slices = newSlices; } string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); string baseName = Path.GetFileNameWithoutExtension(pth); string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); string matName = folderPath + baseName + "-mat" + k + ".mat"; Material existingAsset = AssetDatabase.LoadAssetAtPath <Material>(matName); if (!existingAsset) { Material newMat = new Material(Shader.Find("Standard")); // Don't try to configure the material we need the user to pick a shader that has TextureArrays AssetDatabase.CreateAsset(newMat, matName); } mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); k++; } MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); textureBaker.Update(); }
public static IEnumerator _CreateAtlasesCoroutineSingleResultMaterial(int resMatIdx, MB_TextureArrayResultMaterial bakedMatsAndSlicesResMat, MB_MultiMaterialTexArray resMatConfig, List <GameObject> objsToMesh, MB3_TextureCombiner combiner, MB_TextureArrayFormatSet[] textureArrayOutputFormats, MB_MultiMaterialTexArray[] resultMaterialsTexArray, List <ShaderTextureProperty> customShaderProperties, ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { MB2_LogLevel LOG_LEVEL = combiner.LOG_LEVEL; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Baking atlases for result material " + resMatIdx + " num slices:" + resMatConfig.slices.Count); } // Each result material can be one set of slices per textureProperty. Each slice can be an atlas. // Create atlases for each slice. List <MB3_TextureCombiner.TemporaryTexture> generatedTemporaryAtlases = new List <MB3_TextureCombiner.TemporaryTexture>(); { combiner.saveAtlasesAsAssets = false; // Don't want generated atlas slices to be assets List <MB_TexArraySlice> slicesConfig = resMatConfig.slices; for (int sliceIdx = 0; sliceIdx < slicesConfig.Count; sliceIdx++) { Material resMatToPass = null; List <MB_TexArraySliceRendererMatPair> srcMatAndObjPairs = slicesConfig[sliceIdx].sourceMaterials; if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(" Baking atlases for result material:" + resMatIdx + " slice:" + sliceIdx); } resMatToPass = resMatConfig.combinedMaterial; combiner.fixOutOfBoundsUVs = slicesConfig[sliceIdx].considerMeshUVs; MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult(); MB_AtlasesAndRects sliceAtlasesAndRectOutput = bakedMatsAndSlicesResMat.slices[sliceIdx]; List <Material> usedMats = new List <Material>(); slicesConfig[sliceIdx].GetAllUsedMaterials(usedMats); yield return(combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, sliceAtlasesAndRectOutput, resMatToPass, slicesConfig[sliceIdx].GetAllUsedRenderers(objsToMesh), usedMats, editorMethods, coroutineResult2, maxTimePerFrame, onlyPackRects: false, splitAtlasWhenPackingIfTooBig: false)); coroutineResult.success = coroutineResult2.success; if (!coroutineResult.success) { coroutineResult.isFinished = true; yield break; } // Track which slices are new generated texture instances. Atlases could be original texture assets (one tex per atlas) or temporary texture instances in memory that will need to be destroyed. { for (int texPropIdx = 0; texPropIdx < sliceAtlasesAndRectOutput.atlases.Length; texPropIdx++) { Texture2D atlas = sliceAtlasesAndRectOutput.atlases[texPropIdx]; if (atlas != null) { bool atlasWasASourceTexture = false; for (int srcMatIdx = 0; srcMatIdx < srcMatAndObjPairs.Count; srcMatIdx++) { Material srcMat = srcMatAndObjPairs[srcMatIdx].sourceMaterial; if (srcMat.HasProperty(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx]) && srcMat.GetTexture(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx]) == atlas) { atlasWasASourceTexture = true; break; } } if (!atlasWasASourceTexture) { generatedTemporaryAtlases.Add(new MB3_TextureCombiner.TemporaryTexture(sliceAtlasesAndRectOutput.texPropertyNames[texPropIdx], atlas)); } } } } // end visit slices Debug.Assert(combiner._getNumTemporaryTextures() == 0, "Combiner should have no temporary textures."); } combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; // Restore original setting. } // Generated atlas textures are temporary for texture arrays. They exist only in memory. Need to be cleaned up after we create slices. for (int i = 0; i < generatedTemporaryAtlases.Count; i++) { combiner.AddTemporaryTexture(generatedTemporaryAtlases[i]); } List <ShaderTextureProperty> texPropertyNames = new List <ShaderTextureProperty>(); MB3_TextureCombinerPipeline._CollectPropertyNames(texPropertyNames, customShaderProperties, resMatConfig.combinedMaterial, LOG_LEVEL); // The slices are built from different source-material-lists. Each slice can have different sets of texture properties missing (nulls). // Build a master list of texture properties. bool[] hasTexForProperty = MB_TextureArrays.DetermineWhichPropertiesHaveTextures(bakedMatsAndSlicesResMat.slices); List <Texture2D> temporaryTextureAssets = new List <Texture2D>(); try { MB_MultiMaterialTexArray resMaterial = resMatConfig; Dictionary <string, MB_TexArrayForProperty> resTexArraysByProperty = new Dictionary <string, MB_TexArrayForProperty>(); { // Initialize so I don't need to check if properties exist later. for (int propIdx = 0; propIdx < texPropertyNames.Count; propIdx++) { if (hasTexForProperty[propIdx]) { resTexArraysByProperty[texPropertyNames[propIdx].name] = new MB_TexArrayForProperty(texPropertyNames[propIdx].name, new MB_TextureArrayReference[textureArrayOutputFormats.Length]); } } } MB3_TextureCombinerNonTextureProperties textureBlender = null; textureBlender = new MB3_TextureCombinerNonTextureProperties(LOG_LEVEL, combiner.considerNonTextureProperties); textureBlender.LoadTextureBlendersIfNeeded(resMatConfig.combinedMaterial); textureBlender.AdjustNonTextureProperties(resMatConfig.combinedMaterial, texPropertyNames, editorMethods); // Vist each TextureFormatSet for (int texFormatSetIdx = 0; texFormatSetIdx < textureArrayOutputFormats.Length; texFormatSetIdx++) { MB_TextureArrayFormatSet textureArrayFormatSet = textureArrayOutputFormats[texFormatSetIdx]; editorMethods.Clear(); MB_TextureArrays.TexturePropertyData texPropertyData = new MB_TextureArrays.TexturePropertyData(); MB_TextureArrays.FindBestSizeAndMipCountAndFormatForTextureArrays(texPropertyNames, combiner.maxAtlasSize, textureArrayFormatSet, bakedMatsAndSlicesResMat.slices, texPropertyData); // Create textures we might need to create if they don't exist. { for (int propIdx = 0; propIdx < hasTexForProperty.Length; propIdx++) { if (hasTexForProperty[propIdx]) { TextureFormat format = texPropertyData.formats[propIdx]; int numSlices = bakedMatsAndSlicesResMat.slices.Length; int targetWidth = (int)texPropertyData.sizes[propIdx].x; int targetHeight = (int)texPropertyData.sizes[propIdx].y; for (int sliceIdx = 0; sliceIdx < numSlices; sliceIdx++) { if (bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx] == null) { // Can only setSolidColor on truecolor textures. First create a texture in trucolor format Texture2D sliceTex = new Texture2D(targetWidth, targetHeight, TextureFormat.ARGB32, texPropertyData.doMips[propIdx]); Color col = textureBlender.GetColorForTemporaryTexture(resMatConfig.slices[sliceIdx].sourceMaterials[0].sourceMaterial, texPropertyNames[propIdx]); MB_Utility.setSolidColor(sliceTex, col); // Now create a copy of this texture in target format. bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx] = editorMethods.CreateTemporaryAssetCopy(texPropertyNames[propIdx], sliceTex, targetWidth, targetHeight, format, LOG_LEVEL); temporaryTextureAssets.Add(bakedMatsAndSlicesResMat.slices[sliceIdx].atlases[propIdx]); MB_Utility.Destroy(sliceTex); } } } } } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Converting source textures to readable formats."); } if (MB_TextureArrays.ConvertTexturesToReadableFormat(texPropertyData, bakedMatsAndSlicesResMat.slices, hasTexForProperty, texPropertyNames, combiner, LOG_LEVEL, temporaryTextureAssets, editorMethods)) { // We now have a set of slices (one per textureProperty). Build these into Texture2DArray's. if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Creating texture arrays"); } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("THERE MAY BE ERRORS IN THE CONSOLE ABOUT 'Rebuilding mipmaps ... not supported'. THESE ARE PROBABLY FALSE POSITIVES AND CAN BE IGNORED."); } Texture2DArray[] textureArrays = MB_TextureArrays.CreateTextureArraysForResultMaterial(texPropertyData, texPropertyNames, bakedMatsAndSlicesResMat.slices, hasTexForProperty, combiner, LOG_LEVEL); // Now have texture arrays for a result material, for all props. Save it. for (int propIdx = 0; propIdx < textureArrays.Length; propIdx++) { if (hasTexForProperty[propIdx]) { MB_TextureArrayReference texRef = new MB_TextureArrayReference(textureArrayFormatSet.name, textureArrays[propIdx]); resTexArraysByProperty[texPropertyNames[propIdx].name].formats[texFormatSetIdx] = texRef; if (saveAtlasesAsAssets) { editorMethods.SaveTextureArrayToAssetDatabase(textureArrays[propIdx], textureArrayFormatSet.GetFormatForProperty(texPropertyNames[propIdx].name), bakedMatsAndSlicesResMat.slices[0].texPropertyNames[propIdx], propIdx, resMaterial.combinedMaterial); } } } } } // end vist format set resMaterial.textureProperties = new List <MB_TexArrayForProperty>(); foreach (MB_TexArrayForProperty val in resTexArraysByProperty.Values) { resMaterial.textureProperties.Add(val); } } catch (Exception e) { Debug.LogError(e.Message + "\n" + e.StackTrace.ToString()); coroutineResult.isFinished = true; coroutineResult.success = false; } finally { editorMethods.RestoreReadFlagsAndFormats(progressInfo); combiner._destroyAllTemporaryTextures(); for (int i = 0; i < temporaryTextureAssets.Count; i++) { editorMethods.DestroyAsset(temporaryTextureAssets[i]); } temporaryTextureAssets.Clear(); } }
public static string ReportTextureSizesAndFormats(MB3_TextureBaker mom) { if (mom.resultType != MB2_TextureBakeResults.ResultType.textureArray) { Debug.LogError("Result Type must be Texture Array."); return(""); } for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) { MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; if (resMatTexArray.combinedMaterial == null) { Debug.LogError("Result Material " + resMatIdx + " is null"); return(""); } } System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Visit each result material for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) { MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; // Do an atlas pack in order to collect all the textures needed by the result material // And group these by material texture property. MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); combiner.saveAtlasesAsAssets = false; combiner.fixOutOfBoundsUVs = false; List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempMat = new Material(resMatTexArray.combinedMaterial.shader); List <Material> allSourceMaterials = new List <Material>(); for (int sliceIdx = 0; sliceIdx < resMatTexArray.slices.Count; sliceIdx++) { List <Material> srcMats = new List <Material>(); resMatTexArray.slices[sliceIdx].GetAllUsedMaterials(srcMats); for (int srcMatIdx = 0; srcMatIdx < srcMats.Count; srcMatIdx++) { if (srcMats[srcMatIdx] != null && !allSourceMaterials.Contains(srcMats[srcMatIdx])) { allSourceMaterials.Add(srcMats[srcMatIdx]); } } } MB_AtlasesAndRects atlasesAndRects = new MB_AtlasesAndRects(); combiner.CombineTexturesIntoAtlases(null, atlasesAndRects, tempMat, mom.GetObjectsToCombine(), allSourceMaterials, null, packingResults, onlyPackRects: true, splitAtlasWhenPackingIfTooBig: false); // Now vist the packing results and collect all the textures Debug.Assert(packingResults.Count == 1); for (int texPropIdx = 0; texPropIdx < atlasesAndRects.texPropertyNames.Length; texPropIdx++) { string propertyName = atlasesAndRects.texPropertyNames[texPropIdx]; sb.AppendLine(String.Format("Prop: {0}", propertyName)); List <MB_MaterialAndUVRect> matsData = (List <MB_MaterialAndUVRect>)packingResults[0].data; HashSet <Material> visitedMats = new HashSet <Material>(); for (int matAndGoIdx = 0; matAndGoIdx < matsData.Count; matAndGoIdx++) { Material mat = matsData[matAndGoIdx].material; if (visitedMats.Contains(mat)) { continue; } visitedMats.Add(mat); if (mat.HasProperty(propertyName)) { Texture tex = mat.GetTexture(propertyName); if (tex != null) { string texFormatString = "UnknownFormat"; string texWrapMode = "UnknownClampMode"; if (tex is Texture2D) { texFormatString = ((Texture2D)tex).format.ToString(); texWrapMode = ((Texture2D)tex).wrapMode.ToString(); } sb.AppendLine(String.Format(" {0} x {1} format:{2} wrapMode:{3} {4}", tex.width.ToString().PadLeft(6, ' '), tex.height.ToString().PadRight(6, ' '), texFormatString.PadRight(20, ' '), texWrapMode.PadRight(12, ' '), tex.name)); } } } } } return(sb.ToString()); }