public virtual IEnumerator ConvertTexturesToReadableFormats(ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult result, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Debug.Assert(!data.OnlyOneTextureInAtlasReuseTextures()); //MakeProceduralTexturesReadable(progressInfo, result, data, combiner, textureEditorMethods, LOG_LEVEL); for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { for (int j = 0; j < data.texPropertyNames.Count; j++) { MeshBakerMaterialTexture ts = data.distinctMaterialTextures[i].ts[j]; if (!ts.isNull) { if (textureEditorMethods != null) { Texture tx = ts.GetTexture2D(); TextureFormat format = TextureFormat.ARGB32; if (progressInfo != null) { progressInfo(String.Format("Convert texture {0} to readable format ", tx), .5f); } textureEditorMethods.AddTextureFormat((Texture2D)tx, format, data.texPropertyNames[j].isNormalMap); } } } } yield break; }
//Fills distinctMaterialTextures and usedObjsToMesh //If allowedMaterialsFilter is empty then all materials on allObjsToMesh will be collected and usedObjsToMesh will be same as allObjsToMesh //else only materials in allowedMaterialsFilter will be included and usedObjsToMesh will be objs that use those materials. bool __Step1_CollectDistinctMatTexturesAndUsedObjects(List<GameObject> allObjsToMesh, List<Material> allowedMaterialsFilter, List<string> texPropertyNames, MB2_EditorMethodsInterface textureEditorMethods, List<MB_TexSet> distinctMaterialTextures, //Will be populated List<GameObject> usedObjsToMesh) //Will be populated, is a subset of allObjsToMesh { // Collect distinct list of textures to combine from the materials on objsToCombine bool outOfBoundsUVs = false; for (int i = 0; i < allObjsToMesh.Count; i++){ GameObject obj = allObjsToMesh[i]; if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Collecting textures for object " + obj); if (obj == null){ Debug.LogError("The list of objects to mesh contained nulls."); return false; } Mesh sharedMesh = MB_Utility.GetMesh(obj); if (sharedMesh == null){ Debug.LogError("Object " + obj.name + " in the list of objects to mesh has no mesh."); return false; } Material[] sharedMaterials = MB_Utility.GetGOMaterials(obj); if (sharedMaterials == null){ Debug.LogError("Object " + obj.name + " in the list of objects has no materials."); return false; } for(int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++){ Material mat = sharedMaterials[matIdx]; //check if this material is in the list of source materaials if (allowedMaterialsFilter != null && !allowedMaterialsFilter.Contains(mat)){ continue; } Rect uvBounds = new Rect(); bool mcOutOfBoundsUVs = MB_Utility.hasOutOfBoundsUVs(sharedMesh,ref uvBounds,matIdx); outOfBoundsUVs = outOfBoundsUVs || mcOutOfBoundsUVs; if (mat.name.Contains("(Instance)")){ Debug.LogError("The sharedMaterial on object " + obj.name + " has been 'Instanced'. This was probably caused by a script accessing the meshRender.material property in the editor. " + " The material to UV Rectangle mapping will be incorrect. To fix this recreate the object from its prefab or re-assign its material from the correct asset."); return false; } if (fixOutOfBoundsUVs){ if (!MB_Utility.validateOBuvsMultiMaterial(sharedMaterials)){ if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Object " + obj.name + " uses the same material on multiple submeshes. This may generate strange resultAtlasesAndRects especially when used with fix out of bounds uvs. Try duplicating the material."); } } //collect textures scale and offset for each texture in objects material MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[texPropertyNames.Count]; for (int j = 0; j < texPropertyNames.Count; j++){ Texture2D tx = null; Vector2 scale = Vector2.one; Vector2 offset = Vector2.zero; Vector2 obUVscale = Vector2.one; Vector2 obUVoffset = Vector2.zero; if (mat.HasProperty(texPropertyNames[j])){ Texture txx = mat.GetTexture(texPropertyNames[j]); if (txx != null){ if (txx is Texture2D){ tx = (Texture2D) txx; TextureFormat f = tx.format; bool isNormalMap = false; if (!Application.isPlaying && textureEditorMethods != null) isNormalMap = textureEditorMethods.IsNormalMap(tx); if ((f == TextureFormat.ARGB32 || f == TextureFormat.RGBA32 || f == TextureFormat.BGRA32 || f == TextureFormat.RGB24 || f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work { //good } else { //TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewed //MB2_Log.Log(MB2_LogLevel.warn,obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These formats cannot be resized. MeshBaker will create duplicates."); //tx = createTextureCopy(tx); if (!Application.isPlaying){ //Debug.LogWarning("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'"); if (textureEditorMethods != null) textureEditorMethods.AddTextureFormat(tx, isNormalMap); tx = (Texture2D) mat.GetTexture(texPropertyNames[j]); } else { Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'" ); return false; } } } else { Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases."); return false; } } offset = mat.GetTextureOffset(texPropertyNames[j]); scale = mat.GetTextureScale(texPropertyNames[j]); } if (tx == null){ if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("No texture selected for " + texPropertyNames[j] + " in object " + allObjsToMesh[i].name + ". A 2x2 clear texture will be generated and used in the atlas."); } if (mcOutOfBoundsUVs){ obUVscale = new Vector2(uvBounds.width,uvBounds.height); obUVoffset = new Vector2(uvBounds.x,uvBounds.y); } mts[j] = new MeshBakerMaterialTexture(tx,offset,scale,obUVoffset,obUVscale); } //Add to distinct set of textures if not already there MB_TexSet setOfTexs = new MB_TexSet(mts); MB_TexSet setOfTexs2 = distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs,fixOutOfBoundsUVs)); if (setOfTexs2 != null){ setOfTexs = setOfTexs2; } else { distinctMaterialTextures.Add(setOfTexs); } if (!setOfTexs.mats.Contains(mat)){ setOfTexs.mats.Add(mat); } if (!setOfTexs.gos.Contains(obj)){ setOfTexs.gos.Add(obj); if (!usedObjsToMesh.Contains(obj)) usedObjsToMesh.Add(obj); } } } return true; }
//Fills distinctMaterialTextures and usedObjsToMesh //If allowedMaterialsFilter is empty then all materials on allObjsToMesh will be collected and usedObjsToMesh will be same as allObjsToMesh //else only materials in allowedMaterialsFilter will be included and usedObjsToMesh will be objs that use those materials. bool __Step1_CollectDistinctMatTexturesAndUsedObjects(List<GameObject> allObjsToMesh, List<Material> allowedMaterialsFilter, List<ShaderTextureProperty> texPropertyNames, MB2_EditorMethodsInterface textureEditorMethods, List<MB_TexSet> distinctMaterialTextures, //Will be populated List<GameObject> usedObjsToMesh) //Will be populated, is a subset of allObjsToMesh { System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); // Collect distinct list of textures to combine from the materials on objsToCombine bool outOfBoundsUVs = false; Dictionary<int,MB_Utility.MeshAnalysisResult[]> meshAnalysisResultsCache = new Dictionary<int, MB_Utility.MeshAnalysisResult[]>(); //cache results for (int i = 0; i < allObjsToMesh.Count; i++){ GameObject obj = allObjsToMesh[i]; if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Collecting textures for object " + obj); if (obj == null){ Debug.LogError("The list of objects to mesh contained nulls."); return false; } Mesh sharedMesh = MB_Utility.GetMesh(obj); if (sharedMesh == null){ Debug.LogError("Object " + obj.name + " in the list of objects to mesh has no mesh."); return false; } Material[] sharedMaterials = MB_Utility.GetGOMaterials(obj); if (sharedMaterials == null){ Debug.LogError("Object " + obj.name + " in the list of objects has no materials."); return false; } //analyze mesh or grab cached result of previous analysis MB_Utility.MeshAnalysisResult[] mar; if (!meshAnalysisResultsCache.TryGetValue(sharedMesh.GetInstanceID(),out mar)){ mar = new MB_Utility.MeshAnalysisResult[sharedMesh.subMeshCount]; for (int j = 0; j < sharedMesh.subMeshCount; j++){ Rect outOfBoundsUVRect = new Rect(); MB_Utility.hasOutOfBoundsUVs(sharedMesh,ref outOfBoundsUVRect,ref mar[j], j); } meshAnalysisResultsCache.Add(sharedMesh.GetInstanceID(),mar); } for(int matIdx = 0; matIdx < sharedMaterials.Length; matIdx++){ Material mat = sharedMaterials[matIdx]; //check if this material is in the list of source materaials if (allowedMaterialsFilter != null && !allowedMaterialsFilter.Contains(mat)){ continue; } //Rect uvBounds = mar[matIdx].uvRect; outOfBoundsUVs = outOfBoundsUVs || mar[matIdx].hasOutOfBoundsUVs; if (mat.name.Contains("(Instance)")){ Debug.LogError("The sharedMaterial on object " + obj.name + " has been 'Instanced'. This was probably caused by a script accessing the meshRender.material property in the editor. " + " The material to UV Rectangle mapping will be incorrect. To fix this recreate the object from its prefab or re-assign its material from the correct asset."); return false; } if (_fixOutOfBoundsUVs){ if (!MB_Utility.AreAllSharedMaterialsDistinct(sharedMaterials)){ if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("Object " + obj.name + " uses the same material on multiple submeshes. This may generate strange resultAtlasesAndRects especially when used with fix out of bounds uvs. Try duplicating the material."); } } //collect textures scale and offset for each texture in objects material MeshBakerMaterialTexture[] mts = new MeshBakerMaterialTexture[texPropertyNames.Count]; for (int j = 0; j < texPropertyNames.Count; j++){ Texture2D tx = null; Vector2 scale = Vector2.one; Vector2 offset = Vector2.zero; Vector2 obUVscale = Vector2.one; Vector2 obUVoffset = Vector2.zero; Color colorIfNoTexture = Color.clear; Color tintColor = GetColorIfNoTexture(mat,texPropertyNames[j]); if (mat.HasProperty(texPropertyNames[j].name)){ Texture txx = mat.GetTexture(texPropertyNames[j].name); if (txx != null){ if (txx is Texture2D){ tx = (Texture2D) txx; TextureFormat f = tx.format; bool isNormalMap = false; if (!Application.isPlaying && textureEditorMethods != null) isNormalMap = textureEditorMethods.IsNormalMap(tx); if ((f == TextureFormat.ARGB32 || f == TextureFormat.RGBA32 || f == TextureFormat.BGRA32 || f == TextureFormat.RGB24 || f == TextureFormat.Alpha8) && !isNormalMap) //DXT5 does not work { //good } else { //TRIED to copy texture using tex2.SetPixels(tex1.GetPixels()) but bug in 3.5 means DTX1 and 5 compressed textures come out skewed //MB2_Log.Log(MB2_LogLevel.warn,obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These formats cannot be resized. MeshBaker will create duplicates."); //tx = createTextureCopy(tx); if (Application.isPlaying && _packingAlgorithm != MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast) { Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses Texture "+tx.name+" uses format " + f + " that is not in: ARGB32, RGBA32, BGRA32, RGB24, Alpha8 or DXT. These textures cannot be resized at runtime. Try changing texture format. If format says 'compressed' try changing it to 'truecolor'" ); return false; } else { //only want to do this if we are saving the atlases to project if (textureEditorMethods != null && (_packingAlgorithm != MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Fast || isNormalMap)){ textureEditorMethods.AddTextureFormat(tx, isNormalMap); } tx = (Texture2D) mat.GetTexture(texPropertyNames[j].name); } } } else { Debug.LogError("Object " + obj.name + " in the list of objects to mesh uses a Texture that is not a Texture2D. Cannot build atlases."); return false; } } else { //has texture property but no texture try to set a resonable color from the other texture properties colorIfNoTexture = tintColor; } offset = mat.GetTextureOffset(texPropertyNames[j].name); scale = mat.GetTextureScale(texPropertyNames[j].name); } else { colorIfNoTexture = tintColor; } if (mar[matIdx].hasOutOfBoundsUVs){ obUVscale = new Vector2(mar[matIdx].uvRect.width,mar[matIdx].uvRect.height); obUVoffset = new Vector2(mar[matIdx].uvRect.x,mar[matIdx].uvRect.y); } mts[j] = new MeshBakerMaterialTexture(tx,offset,scale,obUVoffset,obUVscale,colorIfNoTexture,tintColor); } //Add to distinct set of textures if not already there MB_TexSet setOfTexs = new MB_TexSet(mts); MB_TexSet setOfTexs2 = distinctMaterialTextures.Find(x => x.IsEqual(setOfTexs,_fixOutOfBoundsUVs)); if (setOfTexs2 != null){ setOfTexs = setOfTexs2; } else { distinctMaterialTextures.Add(setOfTexs); } if (!setOfTexs.mats.Contains(mat)){ setOfTexs.mats.Add(mat); } if (!setOfTexs.gos.Contains(obj)){ setOfTexs.gos.Add(obj); if (!usedObjsToMesh.Contains(obj)) usedObjsToMesh.Add(obj); } } } if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Total time Step1_CollectDistinctTextures " + (sw.ElapsedMilliseconds).ToString("f5")); return true; }
internal static bool ConvertTexturesToReadableFormat(TexturePropertyData texturePropertyData, MB_AtlasesAndRects[] resultAtlasesAndRectSlices, bool[] hasTexForProperty, List <ShaderTextureProperty> textureShaderProperties, MB3_TextureCombiner combiner, MB2_LogLevel logLevel, List <Texture2D> createdTemporaryTextureAssets, MB2_EditorMethodsInterface textureEditorMethods) { for (int propIdx = 0; propIdx < hasTexForProperty.Length; propIdx++) { if (!hasTexForProperty[propIdx]) { continue; } TextureFormat format = texturePropertyData.formats[propIdx]; if (!textureEditorMethods.TextureImporterFormatExistsForTextureFormat(format)) { Debug.LogError("Could not find target importer format matching " + format); return(false); } int numSlices = resultAtlasesAndRectSlices.Length; int targetWidth = (int)texturePropertyData.sizes[propIdx].x; int targetHeight = (int)texturePropertyData.sizes[propIdx].y; for (int sliceIdx = 0; sliceIdx < numSlices; sliceIdx++) { Texture2D sliceTex = resultAtlasesAndRectSlices[sliceIdx].atlases[propIdx]; Debug.Assert(sliceTex != null, "sliceIdx " + sliceIdx + " " + propIdx); if (sliceTex != null) { if (!MBVersion.IsTextureReadable(sliceTex)) { textureEditorMethods.SetReadWriteFlag(sliceTex, true, true); } bool isAsset = textureEditorMethods.IsAnAsset(sliceTex); if (logLevel >= MB2_LogLevel.trace) { Debug.Log("Considering format of texture: " + sliceTex + " format:" + sliceTex.format); } if ((sliceTex.width != targetWidth || sliceTex.height != targetHeight) || (!isAsset && sliceTex.format != format)) { // Do this the horrible hard way. It is only possible to resize textures in TrueColor formats, // And only possible to switch formats using the Texture importer. // Create a resized temporary texture asset in ARGB32 format. Then set its texture format and reimport resultAtlasesAndRectSlices[sliceIdx].atlases[propIdx] = textureEditorMethods.CreateTemporaryAssetCopy(sliceTex, targetWidth, targetHeight, format, logLevel); createdTemporaryTextureAssets.Add(resultAtlasesAndRectSlices[sliceIdx].atlases[propIdx]); } else if (sliceTex.format != format) { textureEditorMethods.AddTextureFormat(sliceTex, format, textureShaderProperties[propIdx].isNormalMap); } } else { } if (resultAtlasesAndRectSlices[sliceIdx].atlases[propIdx].format != format) { Debug.LogError("Could not convert texture to format " + format + ". This can happen if the target build platform in build settings does not support textures in this format." + " It may be necessary to switch the build platform in order to build texture arrays in this format."); return(false); } } } return(true); }