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; }
public IEnumerator CreateAtlases(ProgressUpdateDelegate progressInfo, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Debug.Assert(data.OnlyOneTextureInAtlasReuseTextures()); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Only one image per atlas. Will re-use original texture"); } for (int i = 0; i < data.numAtlases; i++) { MeshBakerMaterialTexture dmt = data.distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.GetTexture2D(); if (data.resultType == MB2_TextureBakeResults.ResultType.atlas) { data.resultMaterial.SetTexture(data.texPropertyNames[i].name, atlases[i]); data.resultMaterial.SetTextureScale(data.texPropertyNames[i].name, Vector2.one); data.resultMaterial.SetTextureOffset(data.texPropertyNames[i].name, Vector2.zero); } } yield break; }
/// <summary> /// Creates the atlases. /// </summary> /// <returns> /// The atlases. /// </returns> /// <param name='progressInfo'> /// Progress info is a delegate function that displays a progress dialog. Can be null /// </param> /// <param name='saveAtlasesAsAssets'> /// if true atlases are saved as assets in the project folder. Othersise they are instances in memory /// </param> /// <param name='textureFormatTracker'> /// Texture format tracker. Contains editor functionality such as save assets. Can be null. /// </param> public MB_AtlasesAndRects[] CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface textureFormatTracker=null){ MB_AtlasesAndRects[] mAndAs = null; try{ mAndAs = _CreateAtlases(progressInfo, saveAtlasesAsAssets, textureFormatTracker); } catch(Exception e){ Debug.LogError(e); } finally { if (saveAtlasesAsAssets){ //Atlases were saved to project so we don't need these ones if (mAndAs != null){ for(int j = 0; j < mAndAs.Length; j++){ MB_AtlasesAndRects mAndA = mAndAs[j]; if (mAndA != null && mAndA.atlases != null){ for (int i = 0; i < mAndA.atlases.Length; i++){ if (mAndA.atlases[i] != null){ if (textureFormatTracker != null) textureFormatTracker.Destroy(mAndA.atlases[i]); else MB_Utility.Destroy(mAndA.atlases[i]); } } } } } } } return mAndAs; }
public static bool doCombinedValidate(MB2_MeshBakerRoot mom, MB_ObjsToCombineTypes objToCombineType, MB2_EditorMethodsInterface editorMethods){ if (mom.textureBakeResults == null){ Debug.LogError("Need to set Material Bake Result on " + mom); return false; } if (!(mom is MB2_TextureBaker)){ MB2_TextureBaker tb = mom.GetComponent<MB2_TextureBaker>(); if (tb != null && tb.textureBakeResults != mom.textureBakeResults){ Debug.LogWarning("Material Bake Result on this component is not the same as the Material Bake Result on the MB2_TextureBaker."); } } List<GameObject> objsToMesh = mom.GetObjectsToCombine(); for (int i = 0; i < objsToMesh.Count; i++){ GameObject go = objsToMesh[i]; if (go == null){ Debug.LogError("The list of objects to combine contains a null at position." + i + " Select and use [shift] delete to remove"); return false; } for (int j = i + 1; j < objsToMesh.Count; j++){ if (objsToMesh[i] == objsToMesh[j]){ Debug.LogError("The list of objects to combine contains duplicates."); return false; } } if (MB_Utility.GetGOMaterials(go) == null){ Debug.LogError("Object " + go + " in the list of objects to be combined does not have a material"); return false; } if (MB_Utility.GetMesh(go) == null){ Debug.LogError("Object " + go + " in the list of objects to be combined does not have a mesh"); return false; } } if (mom.textureBakeResults.doMultiMaterial){ if (!validateSubmeshOverlap(mom)){//only warns currently return false; } } List<GameObject> objs = objsToMesh; if (mom is MB2_MeshBaker){ MB2_TextureBaker tb = mom.GetComponent<MB2_TextureBaker>(); if (((MB2_MeshBaker)mom).useObjsToMeshFromTexBaker && tb != null) objs = tb.objsToMesh; if (objs == null || objs.Count == 0){ Debug.LogError("No meshes to combine. Please assign some meshes to combine."); return false; } if (mom is MB2_MeshBaker && ((MB2_MeshBaker)mom).renderType == MB_RenderType.skinnedMeshRenderer){ if (!editorMethods.ValidateSkinnedMeshes(objs)){ return false; } } } if (editorMethods != null){ editorMethods.CheckPrefabTypes(objToCombineType, objsToMesh); } return true; }
public 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()); yield break; }
public override void DestroyMeshEditor(MB2_EditorMethodsInterface editorMethods) { for (int i = 0; i < meshCombiners.Count; i++) { if (meshCombiners[i].combinedMesh.targetRenderer != null) { editorMethods.Destroy(meshCombiners[i].combinedMesh.targetRenderer.gameObject); } meshCombiners[i].combinedMesh.ClearMesh(); } obj2MeshCombinerMap.Clear(); meshCombiners.Clear(); }
public bool ValidateTextureImporterFormatsExistsForTextureFormats(MB2_EditorMethodsInterface editorMethods, int idx) { if (editorMethods == null) { return(true); } if (!editorMethods.TextureImporterFormatExistsForTextureFormat(defaultFormat)) { Debug.LogError("TextureImporter format does not exist for Texture Array Output Formats: " + idx + " Defaut Format " + defaultFormat); return(false); } for (int i = 0; i < formatOverrides.Length; i++) { if (!editorMethods.TextureImporterFormatExistsForTextureFormat(formatOverrides[i].format)) { Debug.LogError("TextureImporter format does not exist for Texture Array Output Formats: " + idx + " Format Overrides: " + i + " (" + formatOverrides[i].format + ")"); return(false); } } return(true); }
public static void MakeProceduralTexturesReadable(ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult result, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Debug.LogError("TODO this should be done as close to textures being used as possible due to memory issues."); //make procedural materials readable /* * for (int i = 0; i < combiner._proceduralMaterials.Count; i++) * { * if (!combiner._proceduralMaterials[i].proceduralMat.isReadable) * { * combiner._proceduralMaterials[i].originalIsReadableVal = combiner._proceduralMaterials[i].proceduralMat.isReadable; * combiner._proceduralMaterials[i].proceduralMat.isReadable = true; * //textureEditorMethods.AddProceduralMaterialFormat(_proceduralMaterials[i].proceduralMat); * combiner._proceduralMaterials[i].proceduralMat.RebuildTexturesImmediately(); * } * } * //convert procedural textures to RAW format * * for (int i = 0; i < distinctMaterialTextures.Count; i++) * { * for (int j = 0; j < texPropertyNames.Count; j++) * { * if (distinctMaterialTextures[i].ts[j].IsProceduralTexture()) * { * if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Converting procedural texture to Textur2D:" + distinctMaterialTextures[i].ts[j].GetTexName() + " property:" + texPropertyNames[i]); * Texture2D txx = distinctMaterialTextures[i].ts[j].ConvertProceduralToTexture2D(_temporaryTextures); * distinctMaterialTextures[i].ts[j].t = txx; * } * } * } */ }
Rect[] __CreateAtlasesMBTexturePackerFast(ProgressUpdateDelegate progressInfo, int numAtlases, List<MB_TexSet> distinctMaterialTextures, List<ShaderTextureProperty> texPropertyNames, bool[] allTexturesAreNullAndSameColor, Material resultMaterial, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, int _padding) { Rect[] uvRects; if (distinctMaterialTextures.Count == 1){ if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Only one image per atlas. Will re-use original texture"); uvRects = new Rect[1]; uvRects[0] = new Rect(0f,0f,1f,1f); for (int i = 0; i < numAtlases; i++){ MeshBakerMaterialTexture dmt = distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.t; resultMaterial.SetTexture(texPropertyNames[i].name,atlases[i]); resultMaterial.SetTextureScale(texPropertyNames[i].name,dmt.scale); resultMaterial.SetTextureOffset(texPropertyNames[i].name,dmt.offset); } } else { List<Vector2> imageSizes = new List<Vector2>(); for (int i = 0; i < distinctMaterialTextures.Count; i++){ imageSizes.Add(new Vector2(distinctMaterialTextures[i].idealWidth, distinctMaterialTextures[i].idealHeight)); } MB2_TexturePacker tp = new MB2_TexturePacker(); tp.doPowerOfTwoTextures = _meshBakerTexturePackerForcePowerOfTwo; int atlasSizeX = 1; int atlasSizeY = 1; //todo add sanity warnings for huge atlasesr int atlasMaxDimension = _maxAtlasSize; //if (textureEditorMethods != null) atlasMaxDimension = textureEditorMethods.GetMaximumAtlasDimension(); uvRects = tp.GetRects(imageSizes,atlasMaxDimension,_padding,out atlasSizeX, out atlasSizeY); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY + " (Max atlas size for platform: " + atlasMaxDimension + ")"); //create a game object GameObject renderAtlasesGO = new GameObject("MBrenderAtlasesGO"); MB3_AtlasPackerRenderTexture atlasRenderTexture = renderAtlasesGO.AddComponent<MB3_AtlasPackerRenderTexture>(); for (int i = 0; i < numAtlases; i++){ Texture2D atlas = null; if (allTexturesAreNullAndSameColor[i]){ atlas = null; if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Not creating atlas for " + texPropertyNames[i].name + " because textures are null and default value parameters are the same."); } else { GC.Collect(); if (progressInfo != null) progressInfo("Creating Atlas '" + texPropertyNames[i].name + "'", .01f); // =========== // configure it atlasRenderTexture.width = atlasSizeX; atlasRenderTexture.height = atlasSizeY; atlasRenderTexture.padding = _padding; atlasRenderTexture.rects = uvRects; atlasRenderTexture.textureSets = distinctMaterialTextures; atlasRenderTexture.indexOfTexSetToRender = 0; // call render on it atlas = atlasRenderTexture.OnRenderAtlas(); // destroy it // ============= if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Saving atlas " + texPropertyNames[i].name + " w=" + atlas.width + " h=" + atlas.height); } atlases[i] = atlas; if (progressInfo != null) progressInfo("Saving atlas: '" + texPropertyNames[i].name + "'", .04f); if (_saveAtlasesAsAssets && textureEditorMethods != null){ textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], texPropertyNames[i], i, resultMaterial); } else { resultMaterial.SetTexture(texPropertyNames[i].name, atlases[i]); } resultMaterial.SetTextureOffset(texPropertyNames[i].name, Vector2.zero); resultMaterial.SetTextureScale(texPropertyNames[i].name,Vector2.one); _destroyTemporaryTextures(); // need to save atlases before doing this } } return uvRects; }
//texPropertyNames is the list of texture properties in the resultMaterial //allowedMaterialsFilter is a list of materials. Objects without any of these materials will be ignored. // this is used by the multiple materials filter //textureEditorMethods encapsulates editor only functionality such as saving assets and tracking texture assets whos format was changed. Is null if using at runtime. bool __CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<ShaderTextureProperty> texPropertyNames, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods) { if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("__CombineTexturesIntoAtlases atlases:" + texPropertyNames.Count + " objsToMesh:" + objsToMesh.Count + " _fixOutOfBoundsUVs:" + _fixOutOfBoundsUVs); if (progressInfo != null) progressInfo("Collecting textures ", .01f); /* each atlas (maintex, bump, spec etc...) will have distinctMaterialTextures.Count images in it. each distinctMaterialTextures record is a set of textures, one for each atlas. And a list of materials that use that distinct set of textures. */ List<MB_TexSet> distinctMaterialTextures = new List<MB_TexSet>(); //one per distinct set of textures List<GameObject> usedObjsToMesh = new List<GameObject>(); if (!__Step1_CollectDistinctMatTexturesAndUsedObjects(objsToMesh, allowedMaterialsFilter, texPropertyNames, textureEditorMethods, distinctMaterialTextures, usedObjsToMesh)){ return false; } if (MB3_MeshCombiner.EVAL_VERSION){ bool usesAllowedShaders = true; for (int i = 0; i < distinctMaterialTextures.Count; i++){ for (int j = 0; j < distinctMaterialTextures[i].mats.Count; j++){ if (!distinctMaterialTextures[i].mats[j].shader.name.EndsWith("Diffuse") && !distinctMaterialTextures[i].mats[j].shader.name.EndsWith("Bumped Diffuse")){ Debug.LogError ("The free version of Mesh Baker only works with Diffuse and Bumped Diffuse Shaders. The full version can be used with any shader. Material " + distinctMaterialTextures[i].mats[j].name + " uses shader " + distinctMaterialTextures[i].mats[j].shader.name); usesAllowedShaders = false; } } } if (!usesAllowedShaders) return false; } //Textures in each material (_mainTex, Bump, Spec ect...) must be same size //Calculate the best sized to use. Takes into account tiling //if only one texture in atlas re-uses original sizes bool[] allTexturesAreNullAndSameColor = new bool[texPropertyNames.Count]; int _padding = __Step2_CalculateIdealSizesForTexturesInAtlasAndPadding(distinctMaterialTextures,texPropertyNames,allTexturesAreNullAndSameColor); __Step3_BuildAndSaveAtlasesAndStoreResults(progressInfo,distinctMaterialTextures,texPropertyNames,allTexturesAreNullAndSameColor,_padding,textureEditorMethods,resultAtlasesAndRects,resultMaterial); return true; }
/**<summary>Combines meshes and generates texture atlases.</summary> * <param name="createTextureAtlases">Whether or not texture atlases should be created. If not uvs will not be adjusted.</param> * <param name="progressInfo">A delegate function that will be called to report progress.</param> * <param name="textureEditorMethods">If called from the editor should be an instance of MB2_EditorMethods. If called at runtime should be null.</param> * <remarks>Combines meshes and generates texture atlases</remarks> */ public bool CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null) { return _CombineTexturesIntoAtlases(progressInfo,resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods); }
public virtual void ClearMesh(MB2_EditorMethodsInterface editorMethods) { meshCombiner.ClearMesh(editorMethods); }
/**<summary>Combines meshes and generates texture atlases. NOTE running coroutines at runtime does not work in Unity 4</summary> * <param name="progressInfo">A delegate function that will be called to report progress.</param> * <param name="textureEditorMethods">If called from the editor should be an instance of MB2_EditorMethods. If called at runtime should be null.</param> * <remarks>Combines meshes and generates texture atlases</remarks> */ public bool CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null, List <AtlasPackingResult> packingResults = null, bool onlyPackRects = false) { CombineTexturesIntoAtlasesCoroutineResult result = new CombineTexturesIntoAtlasesCoroutineResult(); RunCorutineWithoutPause(_CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods, packingResults, onlyPackRects), 0); return(result.success); }
Rect[] __CreateAtlasesUnityTexturePacker(ProgressUpdateDelegate progressInfo, int numAtlases, List<MB_TexSet> distinctMaterialTextures, List<string> texPropertyNames, Material resultMaterial, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, int _padding){ Rect[] uvRects; if (distinctMaterialTextures.Count == 1){ if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Only one image per atlas. Will re-use original texture"); uvRects = new Rect[1]; uvRects[0] = new Rect(0f,0f,1f,1f); for (int i = 0; i < numAtlases; i++){ MeshBakerMaterialTexture dmt = distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.t; resultMaterial.SetTexture(texPropertyNames[i],atlases[i]); resultMaterial.SetTextureScale(texPropertyNames[i],dmt.scale); resultMaterial.SetTextureOffset(texPropertyNames[i],dmt.offset); } } else { long estArea = 0; int atlasSizeX = 1; int atlasSizeY = 1; uvRects = null; for (int i = 0; i < numAtlases; i++){ //i is an atlas "MainTex", "BumpMap" etc... if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.LogWarning("Beginning loop " + i + " num temporary textures " + _temporaryTextures.Count); for(int j = 0; j < distinctMaterialTextures.Count; j++){ //j is a distinct set of textures one for each of "MainTex", "BumpMap" etc... MB_TexSet txs = distinctMaterialTextures[j]; int tWidth = txs.idealWidth; int tHeight = txs.idealHeight; Texture2D tx = txs.ts[i].t; if (tx == null) tx = txs.ts[i].t = _createTemporaryTexture(tWidth,tHeight,TextureFormat.ARGB32, true); if (progressInfo != null) progressInfo("Adjusting for scale and offset " + tx, .01f); if (textureEditorMethods != null) textureEditorMethods.SetReadWriteFlag(tx, true, true); tx = getAdjustedForScaleAndOffset2(txs.ts[i]); //create a resized copy if necessary if (tx.width != tWidth || tx.height != tHeight) { if (progressInfo != null) progressInfo("Resizing texture '" + tx + "'", .01f); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.LogWarning("Copying and resizing texture " + texPropertyNames[i] + " from " + tx.width + "x" + tx.height + " to " + tWidth + "x" + tHeight); if (textureEditorMethods != null) textureEditorMethods.SetReadWriteFlag((Texture2D) tx, true, true); tx = _resizeTexture((Texture2D) tx,tWidth,tHeight); } txs.ts[i].t = tx; } Texture2D[] texToPack = new Texture2D[distinctMaterialTextures.Count]; for (int j = 0; j < distinctMaterialTextures.Count;j++){ Texture2D tx = distinctMaterialTextures[j].ts[i].t; estArea += tx.width * tx.height; texToPack[j] = tx; } if (textureEditorMethods != null) textureEditorMethods.CheckBuildSettings(estArea); if (Math.Sqrt(estArea) > 3500f){ if (LOG_LEVEL >= MB2_LogLevel.warn) Debug.LogWarning("The maximum possible atlas size is 4096. Textures may be shrunk"); } atlases[i] = new Texture2D(1,1,TextureFormat.ARGB32,true); if (progressInfo != null) progressInfo("Packing texture atlas " + texPropertyNames[i], .25f); if (i == 0){ if (progressInfo != null) progressInfo("Estimated min size of atlases: " + Math.Sqrt(estArea).ToString("F0"), .1f); if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("Estimated atlas minimum size:" + Math.Sqrt(estArea).ToString("F0")); _addWatermark(texToPack); if (distinctMaterialTextures.Count == 1){ //don't want to force power of 2 so tiling will still work uvRects = new Rect[1] {new Rect(0f,0f,1f,1f)}; atlases[i] = _copyTexturesIntoAtlas(texToPack,_padding,uvRects,texToPack[0].width,texToPack[0].height); } else { int maxAtlasSize = 4096; uvRects = atlases[i].PackTextures(texToPack,_padding,maxAtlasSize,false); } if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("After pack textures atlas size " + atlases[i].width + " " + atlases[i].height); atlasSizeX = atlases[i].width; atlasSizeY = atlases[i].height; atlases[i].Apply(); } else { if (progressInfo != null) progressInfo("Copying Textures Into: " + texPropertyNames[i], .1f); atlases[i] = _copyTexturesIntoAtlas(texToPack,_padding,uvRects, atlasSizeX, atlasSizeY); } if (saveAtlasesAsAssets && textureEditorMethods != null){ textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], texPropertyNames[i], i, resultMaterial); } else { resultMaterial.SetTexture(texPropertyNames[i], atlases[i]); } resultMaterial.SetTextureOffset(texPropertyNames[i], Vector2.zero); resultMaterial.SetTextureScale(texPropertyNames[i],Vector2.one); _destroyTemporaryTextures(); // need to save atlases before doing this GC.Collect(); } } return uvRects; }
//texPropertyNames is the list of texture properties in the resultMaterial //allowedMaterialsFilter is a list of materials. Objects without any of these materials will be ignored. // this is used by the multiple materials filter //textureEditorMethods encapsulates editor only functionality such as saving assets and tracking texture assets whos format was changed. Is null if using at runtime. bool __combineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<string> texPropertyNames, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods){ if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("__combineTexturesIntoAtlases atlases:" + texPropertyNames.Count + " objsToMesh:" + objsToMesh.Count + " fixOutOfBoundsUVs:" + fixOutOfBoundsUVs); if (progressInfo != null) progressInfo("Collecting textures ", .01f); /* each atlas (maintex, bump, spec etc...) will have distinctMaterialTextures.Count images in it. each distinctMaterialTextures record is a set of textures, one for each atlas. And a list of materials that use that distinct set of textures. */ List<MB_TexSet> distinctMaterialTextures = new List<MB_TexSet>(); //one per distinct set of textures List<GameObject> usedObjsToMesh = new List<GameObject>(); if (!__Step1_CollectDistinctMatTexturesAndUsedObjects(objsToMesh, allowedMaterialsFilter, texPropertyNames, textureEditorMethods, distinctMaterialTextures, usedObjsToMesh)){ return false; } //Textures in each material (_mainTex, Bump, Spec ect...) must be same size //Calculate the best sized to use. Takes into account tiling //if only one texture in atlas re-uses original sizes int _padding = __Step2_CalculateIdealSizesForTexturesInAtlasAndPadding(distinctMaterialTextures); __Step3_BuildAndSaveAtlasesAndStoreResults(progressInfo,distinctMaterialTextures,texPropertyNames,_padding,textureEditorMethods,resultAtlasesAndRects,resultMaterial); return true; }
public void CreateAndSaveAtlases(ProgressUpdateDelegate progressInfo, MB2_EditorMethodsInterface textureFormatTracker) { CreateAtlases(progressInfo, true, textureFormatTracker); }
MB_AtlasesAndRects[] _CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface textureFormatTracker=null){ //validation if (saveAtlasesAsAssets && textureFormatTracker == null){ Debug.LogError("Error in CreateAtlases If saveAtlasesAsAssets = true then textureFormatTracker cannot be null."); return null; } if (saveAtlasesAsAssets && !Application.isEditor){ Debug.LogError("Error in CreateAtlases If saveAtlasesAsAssets = true it must be called from the Unity Editor."); return null; } if (!doCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, textureFormatTracker)){ return null; } if (doMultiMaterial && !_ValidateResultMaterials()){ return null; } else if (!doMultiMaterial){ if (resultMaterial == null){ Debug.LogError("Combined Material is null please create and assign a result material."); return null; } Shader targShader = resultMaterial.shader; for (int i = 0; i < objsToMesh.Count; i++){ Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++){ Material m = ms[j]; if (m != null && m.shader != targShader){ Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not 2x2 clear textures will be generated."); } } } } for (int i = 0; i < objsToMesh.Count; i++){ Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++){ Material m = ms[j]; if (m == null){ Debug.LogError("Game object " + objsToMesh[i] + " has a null material. Can't build atlases"); return null; } } } MB_TextureCombiner combiner = new MB_TextureCombiner(); // if editor analyse meshes and suggest treatment if (!Application.isPlaying){ Material[] rms; if (doMultiMaterial){ rms = new Material[resultMaterials.Length]; for (int i = 0; i < rms.Length; i++) rms[i] = resultMaterials[i].combinedMaterial; } else { rms = new Material[1]; rms[0] = resultMaterial; } combiner.SuggestTreatment(objsToMesh, rms, customShaderPropNames); } //initialize structure to store results int numResults = 1; if (doMultiMaterial) numResults = resultMaterials.Length; MB_AtlasesAndRects[] resultAtlasesAndRects = new MB_AtlasesAndRects[numResults]; for (int i = 0; i < resultAtlasesAndRects.Length; i++){ resultAtlasesAndRects[i] = new MB_AtlasesAndRects(); } //Do the material combining. for (int i = 0; i < resultAtlasesAndRects.Length; i++){ Material resMatToPass = null; List<Material> sourceMats = null; if (doMultiMaterial) { sourceMats = resultMaterials[i].sourceMaterials; resMatToPass = resultMaterials[i].combinedMaterial; } else { resMatToPass = resultMaterial; } Debug.Log("Creating atlases for result material " + resMatToPass); if(!combiner.combineTexturesIntoAtlases(progressInfo, resultAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, atlasPadding, customShaderPropNames, resizePowerOfTwoTextures, fixOutOfBoundsUVs, maxTilingBakeSize, saveAtlasesAsAssets, texturePackingAlgorithm, textureFormatTracker)){ return null; } } //Save the results textureBakeResults.combinedMaterialInfo = resultAtlasesAndRects; textureBakeResults.doMultiMaterial = doMultiMaterial; textureBakeResults.resultMaterial = resultMaterial; textureBakeResults.resultMaterials = resultMaterials; textureBakeResults.fixOutOfBoundsUVs = fixOutOfBoundsUVs; unpackMat2RectMap(textureBakeResults); //originally did all the assign of atlases to the result materials here //don't touch result material assets until we are sure atlas creation worked. //unfortunatly Unity has a bug where it Destroys Texture2Ds without warning when memory gets low and generates MissingReferenceException //so if generating assets in editor then save and assign atlases as soon as each atlas is created // if (Application.isPlaying){ // if (doMultiMaterial){ // for (int j = 0; j < resultMaterials.Length; j++){ // Material resMat = resultMaterials[j].combinedMaterial; //resultMaterials[j].combinedMaterial; // Texture2D[] atlases = resultAtlasesAndRects[j].atlases; // for(int i = 0; i < atlases.Length;i++){ // resMat.SetTexture(resultAtlasesAndRects[j].texPropertyNames[i], atlases[i]); // } // } // } else { // Material resMat = resultMaterial; //resultMaterials[j].combinedMaterial; // Texture2D[] atlases = resultAtlasesAndRects[0].atlases; // for(int i = 0; i < atlases.Length;i++){ // resMat.SetTexture(resultAtlasesAndRects[0].texPropertyNames[i], atlases[i]); // } // } // } //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists MB2_MeshBakerCommon mb = GetComponent<MB2_MeshBakerCommon>(); if (mb != null){ mb.textureBakeResults = textureBakeResults; } if (VERBOSE) Debug.Log("Created Atlases"); return resultAtlasesAndRects; }
public virtual void DestroyMeshEditor(MB2_EditorMethodsInterface editorMethods) { meshCombiner.DestroyMeshEditor(editorMethods); }
MB_AtlasesAndRects[] _CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods=null){ //validation if (saveAtlasesAsAssets && editorMethods == null){ Debug.LogError("Error in CreateAtlases If saveAtlasesAsAssets = true then editorMethods cannot be null."); return null; } if (saveAtlasesAsAssets && !Application.isEditor){ Debug.LogError("Error in CreateAtlases If saveAtlasesAsAssets = true it must be called from the Unity Editor."); return null; } MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, editorMethods, vl)){ return null; } if (_doMultiMaterial && !_ValidateResultMaterials()){ return null; } else if (!_doMultiMaterial){ if (_resultMaterial == null){ Debug.LogError("Combined Material is null please create and assign a result material."); return null; } Shader targShader = _resultMaterial.shader; for (int i = 0; i < objsToMesh.Count; i++){ Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++){ Material m = ms[j]; if (m != null && m.shader != targShader){ Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not 2x2 clear textures will be generated."); } } } } for (int i = 0; i < objsToMesh.Count; i++){ Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++){ Material m = ms[j]; if (m == null){ Debug.LogError("Game object " + objsToMesh[i] + " has a null material. Can't build atlases"); return null; } } } MB3_TextureCombiner combiner = new MB3_TextureCombiner(); combiner.LOG_LEVEL = LOG_LEVEL; combiner.atlasPadding = _atlasPadding; combiner.maxAtlasSize = _maxAtlasSize; combiner.customShaderPropNames = _customShaderProperties; combiner.fixOutOfBoundsUVs = _fixOutOfBoundsUVs; combiner.maxTilingBakeSize = _maxTilingBakeSize; combiner.packingAlgorithm = _packingAlgorithm; combiner.meshBakerTexturePackerForcePowerOfTwo = _meshBakerTexturePackerForcePowerOfTwo; combiner.resizePowerOfTwoTextures = _resizePowerOfTwoTextures; combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; // if editor analyse meshes and suggest treatment if (!Application.isPlaying){ Material[] rms; if (_doMultiMaterial){ rms = new Material[resultMaterials.Length]; for (int i = 0; i < rms.Length; i++) rms[i] = resultMaterials[i].combinedMaterial; } else { rms = new Material[1]; rms[0] = _resultMaterial; } combiner.SuggestTreatment(objsToMesh, rms, combiner.customShaderPropNames); } //initialize structure to store results int numResults = 1; if (_doMultiMaterial) numResults = resultMaterials.Length; MB_AtlasesAndRects[] resultAtlasesAndRects = new MB_AtlasesAndRects[numResults]; for (int i = 0; i < resultAtlasesAndRects.Length; i++){ resultAtlasesAndRects[i] = new MB_AtlasesAndRects(); } //Do the material combining. for (int i = 0; i < resultAtlasesAndRects.Length; i++){ Material resMatToPass = null; List<Material> sourceMats = null; if (_doMultiMaterial) { sourceMats = resultMaterials[i].sourceMaterials; resMatToPass = resultMaterials[i].combinedMaterial; } else { resMatToPass = _resultMaterial; } Debug.Log("Creating atlases for result material " + resMatToPass); if(!combiner.CombineTexturesIntoAtlases(progressInfo, resultAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, editorMethods)){ return null; } } //Save the results textureBakeResults.combinedMaterialInfo = resultAtlasesAndRects; textureBakeResults.doMultiMaterial = _doMultiMaterial; textureBakeResults.resultMaterial = _resultMaterial; textureBakeResults.resultMaterials = resultMaterials; textureBakeResults.fixOutOfBoundsUVs = combiner.fixOutOfBoundsUVs; unpackMat2RectMap(textureBakeResults); //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists MB3_MeshBakerCommon[] mb = GetComponentsInChildren<MB3_MeshBakerCommon>(); for (int i = 0; i < mb.Length; i++){ mb[i].textureBakeResults = textureBakeResults; } if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("Created Atlases"); return resultAtlasesAndRects; }
public void DestroyMeshEditor(MB2_EditorMethodsInterface editorMethods){ for (int i = 0; i < meshCombiners.Count; i++){ if (meshCombiners[i].combinedMesh.targetRenderer != null){ editorMethods.Destroy(meshCombiners[i].combinedMesh.targetRenderer.gameObject); } meshCombiners[i].combinedMesh.ClearMesh(); } obj2MeshCombinerMap.Clear(); meshCombiners.Clear(); }
void __Step3_BuildAndSaveAtlasesAndStoreResults(ProgressUpdateDelegate progressInfo, List<MB_TexSet> distinctMaterialTextures, List<ShaderTextureProperty> texPropertyNames, bool[] allTexturesAreNullAndSameColor, int _padding, MB2_EditorMethodsInterface textureEditorMethods, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial){ System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); // note that we may not create some of the atlases because all textures are null int numAtlases = texPropertyNames.Count; //generate report want to do this before //todo if atlas is compressed then doesn't report correct compression StringBuilder report = new StringBuilder(); if (numAtlases > 0){ report = new StringBuilder(); report.AppendLine("Report"); for (int i = 0; i < distinctMaterialTextures.Count; i++){ MB_TexSet txs = distinctMaterialTextures[i]; report.AppendLine("----------"); report.Append("This set of textures will be resized to:" + txs.idealWidth + "x" + txs.idealHeight + "\n"); for (int j = 0; j < txs.ts.Length; j++){ if (txs.ts[j].t != null){ report.Append(" [" + texPropertyNames[j].name + " " + txs.ts[j].t.name + " " + txs.ts[j].t.width + "x" + txs.ts[j].t.height + "]"); if (txs.ts[j].scale != Vector2.one || txs.ts[j].offset != Vector2.zero) report.AppendFormat(" material scale {0} offset{1} ", txs.ts[j].scale.ToString("G4"), txs.ts[j].offset.ToString("G4")); if (txs.ts[j].obUVscale != Vector2.one || txs.ts[j].obUVoffset != Vector2.zero) report.AppendFormat(" obUV scale {0} offset{1} ", txs.ts[j].obUVscale.ToString("G4"), txs.ts[j].obUVoffset.ToString("G4")); report.AppendLine(""); } else { report.Append(" [" + texPropertyNames[j].name + " null "); if (allTexturesAreNullAndSameColor[j]){ report.Append ("no atlas will be created all textures null]\n"); } else { report.AppendFormat("a 16x16 texture will be created with color {0}]\n",txs.ts[j].colorIfNoTexture); } } } report.AppendLine(""); report.Append("Materials using:"); for (int j = 0; j < txs.mats.Count; j++){ report.Append(txs.mats[j].name + ", "); } report.AppendLine(""); } } if (progressInfo != null) progressInfo("Creating txture atlases.", .1f); //run the garbage collector to free up as much memory as possible before bake to reduce MissingReferenceException problems GC.Collect(); Texture2D[] atlases = new Texture2D[numAtlases]; Rect[] uvRects; if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("time Step 3 Create And Save Atlases part 1 " + sw.ElapsedMilliseconds.ToString("f5") ); if (_packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures){ uvRects = __CreateAtlasesUnityTexturePacker(progressInfo, numAtlases, distinctMaterialTextures, texPropertyNames, allTexturesAreNullAndSameColor, resultMaterial, atlases, textureEditorMethods, _padding); } else if (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker) { uvRects = __CreateAtlasesMBTexturePacker(progressInfo, numAtlases, distinctMaterialTextures, texPropertyNames, allTexturesAreNullAndSameColor, resultMaterial, atlases, textureEditorMethods, _padding); } else { uvRects = __CreateAtlasesMBTexturePackerFast(progressInfo, numAtlases, distinctMaterialTextures, texPropertyNames, allTexturesAreNullAndSameColor, resultMaterial, atlases, textureEditorMethods, _padding); } float t3 = sw.ElapsedMilliseconds; AdjustNonTextureProperties(resultMaterial,texPropertyNames,distinctMaterialTextures,textureEditorMethods); if (progressInfo != null) progressInfo("Building Report",.7f); //report on atlases created StringBuilder atlasMessage = new StringBuilder(); atlasMessage.AppendLine("---- Atlases ------"); for (int i = 0; i < numAtlases; i++){ if (atlases[i] != null){ atlasMessage.AppendLine("Created Atlas For: " + texPropertyNames[i].name + " h=" + atlases[i].height + " w=" + atlases[i].width); } else if (allTexturesAreNullAndSameColor[i]){ atlasMessage.AppendLine("Did not create atlas for " + texPropertyNames[i].name + " because all source textures were null."); } } report.Append(atlasMessage.ToString()); Dictionary<Material,Rect> mat2rect_map = new Dictionary<Material, Rect>(); for (int i = 0; i < distinctMaterialTextures.Count; i++){ List<Material> mats = distinctMaterialTextures[i].mats; for (int j = 0; j < mats.Count; j++){ if (!mat2rect_map.ContainsKey(mats[j])){ mat2rect_map.Add(mats[j],uvRects[i]); } } } resultAtlasesAndRects.atlases = atlases; // one per texture on source shader resultAtlasesAndRects.texPropertyNames = ShaderTextureProperty.GetNames(texPropertyNames); // one per texture on source shader resultAtlasesAndRects.mat2rect_map = mat2rect_map; if (progressInfo != null) progressInfo("Restoring Texture Formats & Read Flags",.8f); _destroyTemporaryTextures(); if (textureEditorMethods != null) textureEditorMethods.SetReadFlags(progressInfo); if (report != null && LOG_LEVEL >= MB2_LogLevel.info) Debug.Log(report.ToString()); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Time Step 3 Create And Save Atlases part 3 " + (sw.ElapsedMilliseconds - t3).ToString("f5")); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log ("Total time Step 3 Create And Save Atlases " + sw.ElapsedMilliseconds.ToString("f5")); }
public abstract void DestroyMeshEditor(MB2_EditorMethodsInterface editorMethods);
//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; }
/// <summary> /// Creates the atlases. /// </summary> /// <returns> /// The atlases. /// </returns> /// <param name='progressInfo'> /// Progress info is a delegate function that displays a progress dialog. Can be null /// </param> /// <param name='saveAtlasesAsAssets'> /// if true atlases are saved as assets in the project folder. Othersise they are instances in memory /// </param> /// <param name='textureFormatTracker'> /// Texture format tracker. Contains editor functionality such as save assets. Can be null. /// </param> public MB_AtlasesAndRects[] CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface textureFormatTracker = null) { MB_AtlasesAndRects[] mAndAs = null; try{ mAndAs = _CreateAtlases(progressInfo, saveAtlasesAsAssets, textureFormatTracker); } catch (Exception e) { Debug.LogError(e); } finally { if (saveAtlasesAsAssets) //Atlases were saved to project so we don't need these ones { if (mAndAs != null) { for (int j = 0; j < mAndAs.Length; j++) { MB_AtlasesAndRects mAndA = mAndAs[j]; if (mAndA != null && mAndA.atlases != null) { for (int i = 0; i < mAndA.atlases.Length; i++) { if (mAndA.atlases[i] != null) { if (textureFormatTracker != null) { textureFormatTracker.Destroy(mAndA.atlases[i]); } else { MB_Utility.Destroy(mAndA.atlases[i]); } } } } } } } } return(mAndAs); }
void __Step3_BuildAndSaveAtlasesAndStoreResults(ProgressUpdateDelegate progressInfo, List<MB_TexSet> distinctMaterialTextures, List<string> texPropertyNames, int _padding, MB2_EditorMethodsInterface textureEditorMethods, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial){ int numAtlases = texPropertyNames.Count; //generate report want to do this before //todo if atlas is compressed then doesn't report correct compression StringBuilder report = new StringBuilder(); if (numAtlases > 0){ report = new StringBuilder(); report.AppendLine("Report"); for (int i = 0; i < distinctMaterialTextures.Count; i++){ MB_TexSet txs = distinctMaterialTextures[i]; report.AppendLine("----------"); report.Append("This set of textures will be resized to:" + txs.idealWidth + "x" + txs.idealHeight + "\n"); for (int j = 0; j < txs.ts.Length; j++){ if (txs.ts[j].t != null){ report.Append(" [" + texPropertyNames[j] + " " + txs.ts[j].t.name + " " + txs.ts[j].t.width + "x" + txs.ts[j].t.height + "]"); if (txs.ts[j].scale != Vector2.one || txs.ts[j].offset != Vector2.zero) report.AppendFormat(" material scale {0} offset{1} ", txs.ts[j].scale.ToString("G4"), txs.ts[j].offset.ToString("G4")); if (txs.ts[j].obUVscale != Vector2.one || txs.ts[j].obUVoffset != Vector2.zero) report.AppendFormat(" obUV scale {0} offset{1} ", txs.ts[j].obUVscale.ToString("G4"), txs.ts[j].obUVoffset.ToString("G4")); report.AppendLine(""); } else { report.Append(" [" + texPropertyNames[j] + " null a blank texture will be created]\n"); } } report.AppendLine(""); report.Append("Materials using:"); for (int j = 0; j < txs.mats.Count; j++){ report.Append(txs.mats[j].name + ", "); } report.AppendLine(""); } } if (progressInfo != null) progressInfo("Creating txture atlases.", .1f); //run the garbage collector to free up as much memory as possible before bake to reduce MissingReferenceException problems GC.Collect(); Texture2D[] atlases = new Texture2D[numAtlases]; Rect[] uvRects; if (packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures){ uvRects = __CreateAtlasesUnityTexturePacker(progressInfo, numAtlases, distinctMaterialTextures, texPropertyNames, resultMaterial, atlases, textureEditorMethods, _padding); } else { uvRects = __CreateAtlasesMBTexturePacker(progressInfo, numAtlases, distinctMaterialTextures, texPropertyNames, resultMaterial, atlases, textureEditorMethods, _padding); } if (progressInfo != null) progressInfo("Building Report",.7f); //report on atlases created StringBuilder atlasMessage = new StringBuilder(); atlasMessage.AppendLine("---- Atlases ------"); for (int i = 0; i < numAtlases; i++){ if (atlases[i] != null) atlasMessage.AppendLine("Created Atlas For: " + texPropertyNames[i] + " h=" + atlases[i].height + " w=" + atlases[i].width); } report.Append(atlasMessage.ToString()); Dictionary<Material,Rect> mat2rect_map = new Dictionary<Material, Rect>(); for (int i = 0; i < distinctMaterialTextures.Count; i++){ List<Material> mats = distinctMaterialTextures[i].mats; for (int j = 0; j < mats.Count; j++){ if (!mat2rect_map.ContainsKey(mats[j])){ mat2rect_map.Add(mats[j],uvRects[i]); } } } resultAtlasesAndRects.atlases = atlases; // one per texture on source shader resultAtlasesAndRects.texPropertyNames = texPropertyNames.ToArray(); // one per texture on source shader resultAtlasesAndRects.mat2rect_map = mat2rect_map; if (progressInfo != null) progressInfo("Restoring Texture Formats & Read Flags",.8f); _destroyTemporaryTextures(); if (textureEditorMethods != null) textureEditorMethods.SetReadFlags(progressInfo); if (report != null && LOG_LEVEL >= MB2_LogLevel.info) Debug.Log(report.ToString()); }
public static bool doCombinedValidate(MB2_MeshBakerRoot mom, MB_ObjsToCombineTypes objToCombineType, MB2_EditorMethodsInterface editorMethods) { if (mom.textureBakeResults == null) { Debug.LogError("Need to set Material Bake Result on " + mom); return(false); } if (!(mom is MB2_TextureBaker)) { MB2_TextureBaker tb = mom.GetComponent <MB2_TextureBaker>(); if (tb != null && tb.textureBakeResults != mom.textureBakeResults) { Debug.LogWarning("Material Bake Result on this component is not the same as the Material Bake Result on the MB2_TextureBaker."); } } List <GameObject> objsToMesh = mom.GetObjectsToCombine(); for (int i = 0; i < objsToMesh.Count; i++) { GameObject go = objsToMesh[i]; if (go == null) { Debug.LogError("The list of objects to combine contains a null at position." + i + " Select and use [shift] delete to remove"); return(false); } for (int j = i + 1; j < objsToMesh.Count; j++) { if (objsToMesh[i] == objsToMesh[j]) { Debug.LogError("The list of objects to combine contains duplicates."); return(false); } } if (MB_Utility.GetGOMaterials(go) == null) { Debug.LogError("Object " + go + " in the list of objects to be combined does not have a material"); return(false); } if (MB_Utility.GetMesh(go) == null) { Debug.LogError("Object " + go + " in the list of objects to be combined does not have a mesh"); return(false); } } if (mom.textureBakeResults.doMultiMaterial) { if (!validateSubmeshOverlap(mom)) //only warns currently { return(false); } } List <GameObject> objs = objsToMesh; if (mom is MB2_MeshBaker) { MB2_TextureBaker tb = mom.GetComponent <MB2_TextureBaker>(); if (((MB2_MeshBaker)mom).useObjsToMeshFromTexBaker && tb != null) { objs = tb.objsToMesh; } if (objs == null || objs.Count == 0) { Debug.LogError("No meshes to combine. Please assign some meshes to combine."); return(false); } if (mom is MB2_MeshBaker && ((MB2_MeshBaker)mom).renderType == MB_RenderType.skinnedMeshRenderer) { if (!editorMethods.ValidateSkinnedMeshes(objs)) { return(false); } } } if (editorMethods != null) { editorMethods.CheckPrefabTypes(objToCombineType, objsToMesh); } return(true); }
public void CreateAndSaveAtlases(ProgressUpdateDelegate progressInfo, MB2_EditorMethodsInterface textureFormatTracker){ CreateAtlases(progressInfo,true,textureFormatTracker); }
public static bool DoCombinedValidate(MB3_MeshBakerRoot mom, MB_ObjsToCombineTypes objToCombineType, MB2_EditorMethodsInterface editorMethods, MB2_ValidationLevel validationLevel) { if (mom.textureBakeResults == null) { Debug.LogError("Need to set Texture Bake Result on " + mom); return(false); } if (mom is MB3_MeshBakerCommon) { MB3_MeshBakerCommon momMB = (MB3_MeshBakerCommon)mom; MB3_TextureBaker tb = momMB.GetTextureBaker(); if (tb != null && tb.textureBakeResults != mom.textureBakeResults) { Debug.LogWarning("Texture Bake Result on this component is not the same as the Texture Bake Result on the MB3_TextureBaker."); } } Dictionary <int, MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = null; if (validationLevel == MB2_ValidationLevel.robust) { meshAnalysisResultCache = new Dictionary <int, MB_Utility.MeshAnalysisResult>(); } List <GameObject> objsToMesh = mom.GetObjectsToCombine(); for (int i = 0; i < objsToMesh.Count; i++) { GameObject go = objsToMesh[i]; if (go == null) { Debug.LogError("The list of objects to combine contains a null at position." + i + " Select and use [shift] delete to remove"); return(false); } for (int j = i + 1; j < objsToMesh.Count; j++) { if (objsToMesh[i] == objsToMesh[j]) { Debug.LogError("The list of objects to combine contains duplicates at " + i + " and " + j); return(false); } } if (MB_Utility.GetGOMaterials(go).Length == 0) { Debug.LogError("Object " + go + " in the list of objects to be combined does not have a material"); return(false); } Mesh m = MB_Utility.GetMesh(go); if (m == null) { Debug.LogError("Object " + go + " in the list of objects to be combined does not have a mesh"); return(false); } if (m != null) //This check can be very expensive and it only warns so only do this if we are in the editor. { if (!Application.isEditor && Application.isPlaying && mom.textureBakeResults.doMultiMaterial && validationLevel >= MB2_ValidationLevel.robust) { MB_Utility.MeshAnalysisResult mar; if (!meshAnalysisResultCache.TryGetValue(m.GetInstanceID(), out mar)) { MB_Utility.doSubmeshesShareVertsOrTris(m, ref mar); meshAnalysisResultCache.Add(m.GetInstanceID(), mar); } if (mar.hasOverlappingSubmeshVerts) { Debug.LogWarning("Object " + objsToMesh[i] + " in the list of objects to combine has overlapping submeshes (submeshes share vertices). If the UVs associated with the shared vertices are important then this bake may not work. If you are using multiple materials then this object can only be combined with objects that use the exact same set of textures (each atlas contains one texture). There may be other undesirable side affects as well. Mesh Master, available in the asset store can fix overlapping submeshes."); } } } } List <GameObject> objs = objsToMesh; if (mom is MB3_MeshBaker) { objs = mom.GetObjectsToCombine(); //if (((MB3_MeshBaker)mom).useObjsToMeshFromTexBaker && tb != null) objs = tb.GetObjectsToCombine(); if (objs == null || objs.Count == 0) { Debug.LogError("No meshes to combine. Please assign some meshes to combine."); return(false); } if (mom is MB3_MeshBaker && ((MB3_MeshBaker)mom).meshCombiner.settings.renderType == MB_RenderType.skinnedMeshRenderer) { if (!editorMethods.ValidateSkinnedMeshes(objs)) { return(false); } } } if (editorMethods != null) { editorMethods.CheckPrefabTypes(objToCombineType, objsToMesh); } return(true); }
public override IEnumerator CreateAtlases(ProgressUpdateDelegate progressInfo, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Debug.Assert(!data.OnlyOneTextureInAtlasReuseTextures()); Rect[] uvRects = packedAtlasRects.rects; long estArea = 0; int atlasSizeX = 1; int atlasSizeY = 1; uvRects = null; for (int propIdx = 0; propIdx < data.numAtlases; propIdx++) { //----------------------- ShaderTextureProperty prop = data.texPropertyNames[propIdx]; Texture2D atlas = null; if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(propIdx, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.LogWarning("Beginning loop " + propIdx + " num temporary textures " + combiner._getNumTemporaryTextures()); } MB3_TextureCombinerPackerRoot.CreateTemporaryTexturesForAtlas(data.distinctMaterialTextures, combiner, propIdx, data); Texture2D[] texToPack = new Texture2D[data.distinctMaterialTextures.Count]; for (int texSetIdx = 0; texSetIdx < data.distinctMaterialTextures.Count; texSetIdx++) { MB_TexSet txs = data.distinctMaterialTextures[texSetIdx]; int tWidth = txs.idealWidth; int tHeight = txs.idealHeight; Texture2D tx = txs.ts[propIdx].GetTexture2D(); if (progressInfo != null) { progressInfo("Adjusting for scale and offset " + tx, .01f); } if (textureEditorMethods != null) { textureEditorMethods.SetReadWriteFlag(tx, true, true); } tx = GetAdjustedForScaleAndOffset2(prop.name, txs.ts[propIdx], txs.obUVoffset, txs.obUVscale, data, combiner, LOG_LEVEL); //create a resized copy if necessary if (tx.width != tWidth || tx.height != tHeight) { if (progressInfo != null) { progressInfo("Resizing texture '" + tx + "'", .01f); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.LogWarning("Copying and resizing texture " + prop.name + " from " + tx.width + "x" + tx.height + " to " + tWidth + "x" + tHeight); } tx = combiner._resizeTexture(prop.name, (Texture2D)tx, tWidth, tHeight); } estArea += tx.width * tx.height; if (data._considerNonTextureProperties) { //combine the tintColor with the texture tx = combiner._createTextureCopy(prop.name, tx); data.nonTexturePropertyBlender.TintTextureWithTextureCombiner(tx, data.distinctMaterialTextures[texSetIdx], prop); } texToPack[texSetIdx] = tx; } if (textureEditorMethods != null) { textureEditorMethods.CheckBuildSettings(estArea); } if (Math.Sqrt(estArea) > 3500f) { if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("The maximum possible atlas size is 4096. Textures may be shrunk"); } } atlas = new Texture2D(1, 1, TextureFormat.ARGB32, true); if (progressInfo != null) { progressInfo("Packing texture atlas " + prop.name, .25f); } if (propIdx == 0) { if (progressInfo != null) { progressInfo("Estimated min size of atlases: " + Math.Sqrt(estArea).ToString("F0"), .1f); } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Estimated atlas minimum size:" + Math.Sqrt(estArea).ToString("F0")); } int maxAtlasSize = 4096; uvRects = atlas.PackTextures(texToPack, data._atlasPadding, maxAtlasSize, false); if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("After pack textures atlas size " + atlas.width + " " + atlas.height); } atlasSizeX = atlas.width; atlasSizeY = atlas.height; atlas.Apply(); } else { if (progressInfo != null) { progressInfo("Copying Textures Into: " + prop.name, .1f); } atlas = _copyTexturesIntoAtlas(texToPack, data._atlasPadding, uvRects, atlasSizeX, atlasSizeY, combiner); } } atlases[propIdx] = atlas; //---------------------- if (data._saveAtlasesAsAssets && textureEditorMethods != null) { textureEditorMethods.SaveAtlasToAssetDatabase(atlases[propIdx], prop, propIdx, data.resultMaterial); } data.resultMaterial.SetTextureOffset(prop.name, Vector2.zero); data.resultMaterial.SetTextureScale(prop.name, Vector2.one); combiner._destroyTemporaryTextures(prop.name); GC.Collect(); } packedAtlasRects.rects = uvRects; yield break; }
//texPropertyNames is the list of texture properties in the resultMaterial //allowedMaterialsFilter is a list of materials. Objects without any of these materials will be ignored. // this is used by the multiple materials filter //textureEditorMethods encapsulates editor only functionality such as saving assets and tracking texture assets whos format was changed. Is null if using at runtime. IEnumerator __CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, MB3_TextureCombinerPipeline.TexturePipelineData data, MB2_EditorMethodsInterface textureEditorMethods) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("__CombineTexturesIntoAtlases texture properties in shader:" + data.texPropertyNames.Count + " objsToMesh:" + data.allObjsToMesh.Count + " _fixOutOfBoundsUVs:" + data._fixOutOfBoundsUVs); } if (progressInfo != null) { progressInfo("Collecting textures ", .01f); } MB3_TextureCombinerPipeline pipeline = new MB3_TextureCombinerPipeline(); /* * each atlas (maintex, bump, spec etc...) will have distinctMaterialTextures.Count images in it. * each distinctMaterialTextures record is a set of textures, one for each atlas. And a list of materials * that use that distinct set of textures. */ List <GameObject> usedObjsToMesh = new List <GameObject>(); yield return(pipeline.__Step1_CollectDistinctMatTexturesAndUsedObjects(progressInfo, result, data, this, textureEditorMethods, usedObjsToMesh, LOG_LEVEL)); if (!result.success) { yield break; } //Textures in each material (_mainTex, Bump, Spec ect...) must be same size //Calculate the best sized to use. Takes into account tiling //if only one texture in atlas re-uses original sizes yield return(pipeline.CalculateIdealSizesForTexturesInAtlasAndPadding(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL)); if (!result.success) { yield break; } //buildAndSaveAtlases StringBuilder report = pipeline.GenerateReport(data); MB_ITextureCombinerPacker texturePaker = pipeline.CreatePacker(data.OnlyOneTextureInAtlasReuseTextures(), data._packingAlgorithm); if (!texturePaker.Validate(data)) { result.success = false; yield break; } yield return(texturePaker.ConvertTexturesToReadableFormats(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL)); if (!result.success) { yield break; } AtlasPackingResult[] uvRects = texturePaker.CalculateAtlasRectangles(data, false, LOG_LEVEL); Debug.Assert(uvRects.Length == 1, "Error, there should not be more than one packing here."); yield return(pipeline.__Step3_BuildAndSaveAtlasesAndStoreResults(result, progressInfo, data, this, texturePaker, uvRects[0], textureEditorMethods, resultAtlasesAndRects, report, LOG_LEVEL)); }
public IEnumerator CreateAtlasesCoroutine(ProgressUpdateDelegate progressInfo, CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { MBVersionConcrete mbv = new MBVersionConcrete(); if (!MB3_TextureCombiner._RunCorutineWithoutPauseIsRunning && (mbv.GetMajorVersion() < 5 || (mbv.GetMajorVersion() == 5 && mbv.GetMinorVersion() < 3))) { Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher"); coroutineResult.success = false; yield break; } this.OnCombinedTexturesCoroutineAtlasesAndRects = null; if (maxTimePerFrame <= 0f) { Debug.LogError("maxTimePerFrame must be a value greater than zero"); coroutineResult.isFinished = true; yield break; } MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, null, vl)) { coroutineResult.isFinished = true; yield break; } if (_doMultiMaterial && !_ValidateResultMaterials()) { coroutineResult.isFinished = true; yield break; } else if (!_doMultiMaterial) { if (_resultMaterial == null) { Debug.LogError("Combined Material is null please create and assign a result material."); coroutineResult.isFinished = true; yield break; } Shader targShader = _resultMaterial.shader; for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m != null && m.shader != targShader) { Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not small solid color textures will be generated."); } } } } MB3_TextureCombiner combiner = CreateAndConfigureTextureCombiner(); combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; //initialize structure to store results int numResults = 1; if (_doMultiMaterial) { numResults = resultMaterials.Length; } OnCombinedTexturesCoroutineAtlasesAndRects = new MB_AtlasesAndRects[numResults]; for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { OnCombinedTexturesCoroutineAtlasesAndRects[i] = new MB_AtlasesAndRects(); } //Do the material combining. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { Material resMatToPass = null; List <Material> sourceMats = null; if (_doMultiMaterial) { sourceMats = resultMaterials[i].sourceMaterials; resMatToPass = resultMaterials[i].combinedMaterial; combiner.fixOutOfBoundsUVs = resultMaterials[i].considerMeshUVs; } else { resMatToPass = _resultMaterial; } MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult(); yield return(combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, OnCombinedTexturesCoroutineAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, editorMethods, coroutineResult2, maxTimePerFrame)); coroutineResult.success = coroutineResult2.success; if (!coroutineResult.success) { coroutineResult.isFinished = true; yield break; } } unpackMat2RectMap(textureBakeResults); //Save the results textureBakeResults.doMultiMaterial = _doMultiMaterial; //textureBakeResults.resultMaterial = _resultMaterial; if (_doMultiMaterial) { textureBakeResults.resultMaterials = resultMaterials; } else { MB_MultiMaterial[] resMats = new MB_MultiMaterial[1]; resMats[0] = new MB_MultiMaterial(); resMats[0].combinedMaterial = _resultMaterial; resMats[0].considerMeshUVs = _fixOutOfBoundsUVs; resMats[0].sourceMaterials = new List <Material>(); for (int i = 0; i < textureBakeResults.materialsAndUVRects.Length; i++) { resMats[0].sourceMaterials.Add(textureBakeResults.materialsAndUVRects[i].material); } textureBakeResults.resultMaterials = resMats; } //textureBakeResults.fixOutOfBoundsUVs = combiner.fixOutOfBoundsUVs; //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists MB3_MeshBakerCommon[] mb = GetComponentsInChildren <MB3_MeshBakerCommon>(); for (int i = 0; i < mb.Length; i++) { mb[i].textureBakeResults = textureBakeResults; } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Atlases"); } coroutineResult.isFinished = true; if (coroutineResult.success && onBuiltAtlasesSuccess != null) { onBuiltAtlasesSuccess(); } if (!coroutineResult.success && onBuiltAtlasesFail != null) { onBuiltAtlasesFail(); } }
//If we are switching from a Material that uses color properties to //using atlases don't want some properties such as _Color to be copied //from the original material because the atlas texture will be multiplied //by that color void AdjustNonTextureProperties(Material mat, List<ShaderTextureProperty> texPropertyNames, List<MB_TexSet> distinctMaterialTextures, MB2_EditorMethodsInterface editorMethods) { if (mat == null || texPropertyNames == null) return; for (int i = 0; i < texPropertyNames.Count; i++){ string nm = texPropertyNames[i].name; if (nm.Equals("_MainTex")){ if (mat.HasProperty("_Color")){ try{ mat.SetColor("_Color",distinctMaterialTextures[0].ts[i].tintColor); } catch(Exception){} } } if (nm.Equals("_BumpMap")){ if (mat.HasProperty("_BumpScale")){ try{ mat.SetFloat("_BumpScale",1f); } catch(Exception){} } } if (nm.Equals("_ParallaxMap")){ if (mat.HasProperty("_Parallax")){ try{ mat.SetFloat("_Parallax",.02f); } catch(Exception){} } } if (nm.Equals("_OcclusionMap")){ if (mat.HasProperty("_OcclusionStrength")){ try{ mat.SetFloat("_OcclusionStrength",1f); } catch(Exception){} } } if (nm.Equals("_EmissionMap")){ if (mat.HasProperty("_EmissionColorUI")){ try{ mat.SetColor("_EmissionColorUI",new Color(1f,1f,1f,1f)); } catch(Exception){} } if (mat.HasProperty("_EmissionScaleUI")){ try{ mat.SetFloat("_EmissionScaleUI",1f); } catch(Exception){} } } } if (editorMethods != null){ editorMethods.CommitChangesToAssets(); } }
/**<summary>Combines meshes and generates texture atlases. NOTE running coroutines at runtime does not work in Unity 4</summary> * <param name="progressInfo">A delegate function that will be called to report progress.</param> * <param name="textureEditorMethods">If called from the editor should be an instance of MB2_EditorMethods. If called at runtime should be null.</param> * <remarks>Combines meshes and generates texture atlases</remarks> */ public bool CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null, List <AtlasPackingResult> packingResults = null, bool onlyPackRects = false, bool splitAtlasWhenPackingIfTooBig = false) { CombineTexturesIntoAtlasesCoroutineResult result = new CombineTexturesIntoAtlasesCoroutineResult(); RunCorutineWithoutPause(_CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods, packingResults, onlyPackRects, splitAtlasWhenPackingIfTooBig), 0); if (result.success == false) { Debug.LogError("Failed to generate atlases."); } return(result.success); }
Rect[] __CreateAtlasesMBTexturePacker(ProgressUpdateDelegate progressInfo, int numAtlases, List<MB_TexSet> distinctMaterialTextures, List<ShaderTextureProperty> texPropertyNames, bool[] allTexturesAreNullAndSameColor, Material resultMaterial, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, int _padding) { Rect[] uvRects; if (distinctMaterialTextures.Count == 1){ if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Only one image per atlas. Will re-use original texture"); uvRects = new Rect[1]; uvRects[0] = new Rect(0f,0f,1f,1f); for (int i = 0; i < numAtlases; i++){ MeshBakerMaterialTexture dmt = distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.t; resultMaterial.SetTexture(texPropertyNames[i].name,atlases[i]); resultMaterial.SetTextureScale(texPropertyNames[i].name,dmt.scale); resultMaterial.SetTextureOffset(texPropertyNames[i].name,dmt.offset); } } else { List<Vector2> imageSizes = new List<Vector2>(); for (int i = 0; i < distinctMaterialTextures.Count; i++){ imageSizes.Add(new Vector2(distinctMaterialTextures[i].idealWidth, distinctMaterialTextures[i].idealHeight)); } MB2_TexturePacker tp = new MB2_TexturePacker(); tp.doPowerOfTwoTextures = _meshBakerTexturePackerForcePowerOfTwo; int atlasSizeX = 1; int atlasSizeY = 1; //todo add sanity warnings for huge atlasesr int atlasMaxDimension = _maxAtlasSize; //if (textureEditorMethods != null) atlasMaxDimension = textureEditorMethods.GetMaximumAtlasDimension(); uvRects = tp.GetRects(imageSizes,atlasMaxDimension,_padding,out atlasSizeX, out atlasSizeY); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY + " (Max atlas size for platform: " + atlasMaxDimension + ")"); for (int i = 0; i < numAtlases; i++){ Texture2D atlas = null; if (allTexturesAreNullAndSameColor[i]){ atlas = null; if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Not creating atlas for " + texPropertyNames[i].name + " because textures are null and default value parameters are the same."); } else { GC.Collect(); if (progressInfo != null) progressInfo("Creating Atlas '" + texPropertyNames[i].name + "'", .01f); //use a jagged array because it is much more efficient in memory Color[][] atlasPixels = new Color[atlasSizeY][]; for (int j = 0; j < atlasPixels.Length; j++){ atlasPixels[j] = new Color[atlasSizeX]; } bool isNormalMap = false; if (texPropertyNames[i].isNormalMap) isNormalMap = true; // for (int j = 0; j < atlasPixels.Length; j++) { // for (int k = 0; k < atlasSizeX; k++){ // atlasPixels[j][k] = GetColorIfNoTexture( // if (isNormalMap){ // atlasPixels[j][k] = new Color(.5f,.5f,1f); //neutral bluish for normal maps // } else { // atlasPixels[j][k] = Color.clear; // } // } // } for (int j = 0; j < distinctMaterialTextures.Count; j++){ if (LOG_LEVEL >= MB2_LogLevel.trace) MB2_Log.Trace("Adding texture {0} to atlas {1}", distinctMaterialTextures[j].ts[i].t == null ? "null" : distinctMaterialTextures[j].ts[i].t.ToString(),texPropertyNames[i]); Rect r = uvRects[j]; Texture2D t = distinctMaterialTextures[j].ts[i].t; int x = Mathf.RoundToInt(r.x * atlasSizeX); int y = Mathf.RoundToInt(r.y * atlasSizeY); int ww = Mathf.RoundToInt(r.width * atlasSizeX); int hh = Mathf.RoundToInt(r.height * atlasSizeY); if (ww == 0 || hh == 0) Debug.LogError("Image in atlas has no height or width"); if (textureEditorMethods != null) textureEditorMethods.SetReadWriteFlag(t, true, true); if (progressInfo != null) progressInfo("Copying to atlas: '" + distinctMaterialTextures[j].ts[i].t + "'", .02f); CopyScaledAndTiledToAtlas(distinctMaterialTextures[j].ts[i],x,y,ww,hh,_fixOutOfBoundsUVs,_maxTilingBakeSize,atlasPixels,atlasSizeX,isNormalMap,progressInfo); } if (progressInfo != null) progressInfo("Applying changes to atlas: '" + texPropertyNames[i].name + "'", .03f); atlas = new Texture2D(atlasSizeX, atlasSizeY,TextureFormat.ARGB32, true); for (int j = 0; j < atlasPixels.Length; j++){ atlas.SetPixels(0,j,atlasSizeX,1,atlasPixels[j]); } atlas.Apply(); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Saving atlas " + texPropertyNames[i].name + " w=" + atlas.width + " h=" + atlas.height); } atlases[i] = atlas; if (progressInfo != null) progressInfo("Saving atlas: '" + texPropertyNames[i].name + "'", .04f); if (_saveAtlasesAsAssets && textureEditorMethods != null){ textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], texPropertyNames[i], i, resultMaterial); } else { resultMaterial.SetTexture(texPropertyNames[i].name, atlases[i]); } resultMaterial.SetTextureOffset(texPropertyNames[i].name, Vector2.zero); resultMaterial.SetTextureScale(texPropertyNames[i].name,Vector2.one); _destroyTemporaryTextures(); // need to save atlases before doing this } } return uvRects; }
IEnumerator _CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods, List <AtlasPackingResult> atlasPackingResult, bool onlyPackRects, bool splitAtlasWhenPackingIfTooBig) { System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); try { _temporaryTextures.Clear(); MeshBakerMaterialTexture.readyToBuildAtlases = false; if (textureEditorMethods != null) { textureEditorMethods.Clear(); textureEditorMethods.OnPreTextureBake(); } if (objsToMesh == null || objsToMesh.Count == 0) { Debug.LogError("No meshes to combine. Please assign some meshes to combine."); result.success = false; yield break; } if (_atlasPadding < 0) { Debug.LogError("Atlas padding must be zero or greater."); result.success = false; yield break; } if (_maxTilingBakeSize < 2 || _maxTilingBakeSize > 4096) { Debug.LogError("Invalid value for max tiling bake size."); result.success = false; yield break; } for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m == null) { Debug.LogError("Game object " + objsToMesh[i] + " has a null material"); result.success = false; yield break; } } } if (progressInfo != null) { progressInfo("Collecting textures for " + objsToMesh.Count + " meshes.", .01f); } MB3_TextureCombinerPipeline.TexturePipelineData data = LoadPipelineData(resultMaterial, new List <ShaderTextureProperty>(), objsToMesh, allowedMaterialsFilter, new List <MB_TexSet>()); if (!MB3_TextureCombinerPipeline._CollectPropertyNames(data, LOG_LEVEL)) { result.success = false; yield break; } if (_fixOutOfBoundsUVs && (_packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal || _packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical)) { if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.LogWarning("'Consider Mesh UVs' is enabled but packing algorithm is MeshBakerTexturePacker_Horizontal or MeshBakerTexturePacker_Vertical. It is recommended to use these packers without using 'Consider Mesh UVs'"); } } data.nonTexturePropertyBlender.LoadTextureBlendersIfNeeded(data.resultMaterial); if (onlyPackRects) { yield return(__RunTexturePackerOnly(result, data, splitAtlasWhenPackingIfTooBig, textureEditorMethods, atlasPackingResult)); } else { yield return(__CombineTexturesIntoAtlases(progressInfo, result, resultAtlasesAndRects, data, splitAtlasWhenPackingIfTooBig, textureEditorMethods)); } /* * } catch (MissingReferenceException mrex){ * Debug.LogError("Creating atlases failed a MissingReferenceException was thrown. This is normally only happens when trying to create very large atlases and Unity is running out of Memory. Try changing the 'Texture Packer' to a different option, it may work with an alternate packer. This error is sometimes intermittant. Try baking again."); * Debug.LogError(mrex); * } catch (Exception ex){ * Debug.LogError(ex);*/ } finally { _destroyAllTemporaryTextures(); _restoreProceduralMaterials(); if (textureEditorMethods != null) { textureEditorMethods.RestoreReadFlagsAndFormats(progressInfo); textureEditorMethods.OnPostTextureBake(); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("===== Done creating atlases for " + resultMaterial + " Total time to create atlases " + sw.Elapsed.ToString()); } } //result.success = success; }
IEnumerator __RunTexturePackerOnly(CombineTexturesIntoAtlasesCoroutineResult result, MB3_TextureCombinerPipeline.TexturePipelineData data, bool splitAtlasWhenPackingIfTooBig, MB2_EditorMethodsInterface textureEditorMethods, List <AtlasPackingResult> packingResult) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("__RunTexturePacker texture properties in shader:" + data.texPropertyNames.Count + " objsToMesh:" + data.allObjsToMesh.Count + " _fixOutOfBoundsUVs:" + data._fixOutOfBoundsUVs); } List <GameObject> usedObjsToMesh = new List <GameObject>(); yield return(MB3_TextureCombinerPipeline.__Step1_CollectDistinctMatTexturesAndUsedObjects(null, result, data, this, textureEditorMethods, usedObjsToMesh, LOG_LEVEL)); if (!result.success) { yield break; } data.allTexturesAreNullAndSameColor = new MB3_TextureCombinerPipeline.CreateAtlasForProperty[data.texPropertyNames.Count]; yield return(MB3_TextureCombinerPipeline.CalculateIdealSizesForTexturesInAtlasAndPadding(null, result, data, this, textureEditorMethods, LOG_LEVEL)); if (!result.success) { yield break; } MB_ITextureCombinerPacker texturePaker = MB3_TextureCombinerPipeline.CreatePacker(data.OnlyOneTextureInAtlasReuseTextures(), data._packingAlgorithm); // run the texture packer only AtlasPackingResult[] aprs = MB3_TextureCombinerPipeline.RunTexturePackerOnly(data, splitAtlasWhenPackingIfTooBig, texturePaker, LOG_LEVEL); for (int i = 0; i < aprs.Length; i++) { packingResult.Add(aprs[i]); } }
public IEnumerator CreateAtlasesCoroutine(ProgressUpdateDelegate progressInfo, CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { MBVersionConcrete mbv = new MBVersionConcrete(); if (!MB3_TextureCombiner._RunCorutineWithoutPauseIsRunning && (mbv.GetMajorVersion() < 5 || (mbv.GetMajorVersion() == 5 && mbv.GetMinorVersion() < 3))) { Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher"); yield return(null); } this.OnCombinedTexturesCoroutineAtlasesAndRects = null; //if (!Application.isPlaying) //{ // Debug.LogError("CombineTexturesIntoAtlasesCoroutine should only be called when the game is running. Use CombineTexturesIntoAtlases in the editor."); // _CreateAtlasesCoroutineIsFinished = true; // yield break; //} if (maxTimePerFrame <= 0f) { Debug.LogError("maxTimePerFrame must be a value greater than zero"); coroutineResult.isFinished = true; yield break; } MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, null, vl)) { coroutineResult.isFinished = true; yield break; } if (_doMultiMaterial && !_ValidateResultMaterials()) { coroutineResult.isFinished = true; yield break; } else if (!_doMultiMaterial) { if (_resultMaterial == null) { Debug.LogError("Combined Material is null please create and assign a result material."); coroutineResult.isFinished = true; yield break; } Shader targShader = _resultMaterial.shader; for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m != null && m.shader != targShader) { Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not small solid color textures will be generated."); } } } } for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m == null) { Debug.LogError("Game object " + objsToMesh[i] + " has a null material. Can't build atlases"); coroutineResult.isFinished = true; yield break; } } } MB3_TextureCombiner combiner = new MB3_TextureCombiner(); combiner.LOG_LEVEL = LOG_LEVEL; combiner.atlasPadding = _atlasPadding; combiner.maxAtlasSize = _maxAtlasSize; combiner.customShaderPropNames = _customShaderProperties; combiner.fixOutOfBoundsUVs = _fixOutOfBoundsUVs; combiner.maxTilingBakeSize = _maxTilingBakeSize; combiner.packingAlgorithm = _packingAlgorithm; combiner.meshBakerTexturePackerForcePowerOfTwo = _meshBakerTexturePackerForcePowerOfTwo; combiner.resizePowerOfTwoTextures = _resizePowerOfTwoTextures; combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; combiner.considerNonTextureProperties = _considerNonTextureProperties; //initialize structure to store results int numResults = 1; if (_doMultiMaterial) { numResults = resultMaterials.Length; } OnCombinedTexturesCoroutineAtlasesAndRects = new MB_AtlasesAndRects[numResults]; for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { OnCombinedTexturesCoroutineAtlasesAndRects[i] = new MB_AtlasesAndRects(); } //Do the material combining. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { Material resMatToPass = null; List <Material> sourceMats = null; if (_doMultiMaterial) { sourceMats = resultMaterials[i].sourceMaterials; resMatToPass = resultMaterials[i].combinedMaterial; } else { resMatToPass = _resultMaterial; } Debug.Log(string.Format("Creating atlases for result material {0} using shader {1}", resMatToPass, resMatToPass.shader)); MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult(); yield return(combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, OnCombinedTexturesCoroutineAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, editorMethods, coroutineResult2, maxTimePerFrame)); coroutineResult.success = coroutineResult2.success; if (!coroutineResult.success) { coroutineResult.isFinished = true; yield break; } } //Save the results textureBakeResults.combinedMaterialInfo = OnCombinedTexturesCoroutineAtlasesAndRects; textureBakeResults.doMultiMaterial = _doMultiMaterial; textureBakeResults.resultMaterial = _resultMaterial; textureBakeResults.resultMaterials = resultMaterials; textureBakeResults.fixOutOfBoundsUVs = combiner.fixOutOfBoundsUVs; unpackMat2RectMap(textureBakeResults); //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists MB3_MeshBakerCommon[] mb = GetComponentsInChildren <MB3_MeshBakerCommon>(); for (int i = 0; i < mb.Length; i++) { mb[i].textureBakeResults = textureBakeResults; } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Atlases"); } coroutineResult.isFinished = true; if (coroutineResult.success && onBuiltAtlasesSuccess != null) { onBuiltAtlasesSuccess(); } if (!coroutineResult.success && onBuiltAtlasesFail != null) { onBuiltAtlasesFail(); } }
public static bool DoCombinedValidate(MB3_MeshBakerRoot mom, MB_ObjsToCombineTypes objToCombineType, MB2_EditorMethodsInterface editorMethods, MB2_ValidationLevel validationLevel){ if (mom.textureBakeResults == null){ Debug.LogError("Need to set Material Bake Result on " + mom); return false; } if (mom is MB3_MeshBakerCommon){ MB3_MeshBakerCommon momMB = (MB3_MeshBakerCommon) mom; MB3_TextureBaker tb = momMB.GetTextureBaker(); if (tb != null && tb.textureBakeResults != mom.textureBakeResults){ Debug.LogWarning("Material Bake Result on this component is not the same as the Material Bake Result on the MB3_TextureBaker."); } } Dictionary<int,MB_Utility.MeshAnalysisResult> meshAnalysisResultCache = null; if (validationLevel == MB2_ValidationLevel.robust){ meshAnalysisResultCache = new Dictionary<int, MB_Utility.MeshAnalysisResult>(); } List<GameObject> objsToMesh = mom.GetObjectsToCombine(); for (int i = 0; i < objsToMesh.Count; i++){ GameObject go = objsToMesh[i]; if (go == null){ Debug.LogError("The list of objects to combine contains a null at position." + i + " Select and use [shift] delete to remove"); return false; } for (int j = i + 1; j < objsToMesh.Count; j++){ if (objsToMesh[i] == objsToMesh[j]){ Debug.LogError("The list of objects to combine contains duplicates at " + i + " and " + j); return false; } } if (MB_Utility.GetGOMaterials(go) == null){ Debug.LogError("Object " + go + " in the list of objects to be combined does not have a material"); return false; } Mesh m = MB_Utility.GetMesh(go); if (m == null){ Debug.LogError("Object " + go + " in the list of objects to be combined does not have a mesh"); return false; } if (m != null){ //This check can be very expensive and it only warns so only do this if we are in the editor. if (!Application.isEditor && Application.isPlaying && mom.textureBakeResults.doMultiMaterial && validationLevel >= MB2_ValidationLevel.robust){ MB_Utility.MeshAnalysisResult mar; if (!meshAnalysisResultCache.TryGetValue(m.GetInstanceID(),out mar)){ MB_Utility.doSubmeshesShareVertsOrTris(m,ref mar); meshAnalysisResultCache.Add (m.GetInstanceID(),mar); } if (mar.hasOverlappingSubmeshVerts){ Debug.LogWarning("Object " + objsToMesh[i] + " in the list of objects to combine has overlapping submeshes (submeshes share vertices). If the UVs associated with the shared vertices are important then this bake may not work. If you are using multiple materials then this object can only be combined with objects that use the exact same set of textures (each atlas contains one texture). There may be other undesirable side affects as well. Mesh Master, available in the asset store can fix overlapping submeshes."); } } } } List<GameObject> objs = objsToMesh; if (mom is MB3_MeshBaker) { objs = mom.GetObjectsToCombine(); //if (((MB3_MeshBaker)mom).useObjsToMeshFromTexBaker && tb != null) objs = tb.GetObjectsToCombine(); if (objs == null || objs.Count == 0) { Debug.LogError("No meshes to combine. Please assign some meshes to combine."); return false; } if (mom is MB3_MeshBaker && ((MB3_MeshBaker)mom).meshCombiner.renderType == MB_RenderType.skinnedMeshRenderer){ if (!editorMethods.ValidateSkinnedMeshes(objs)) { return false; } } } if (editorMethods != null){ editorMethods.CheckPrefabTypes(objToCombineType, objsToMesh); } return true; }
/// <summary> /// Creates the atlases. /// </summary> /// <returns> /// The atlases. /// </returns> /// <param name='progressInfo'> /// Progress info is a delegate function that displays a progress dialog. Can be null /// </param> /// <param name='saveAtlasesAsAssets'> /// if true atlases are saved as assets in the project folder. Othersise they are instances in memory /// </param> /// <param name='editorMethods'> /// Texture format tracker. Contains editor functionality such as save assets. Can be null. /// </param> public MB_AtlasesAndRects[] CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null) { MB_AtlasesAndRects[] mAndAs = null; try { //mAndAs = _CreateAtlases(progressInfo, saveAtlasesAsAssets, editorMethods); _coroutineResult = new CreateAtlasesCoroutineResult(); MB3_TextureCombiner.RunCorutineWithoutPause(CreateAtlasesCoroutine(progressInfo, _coroutineResult, saveAtlasesAsAssets, editorMethods, 1000f), 0); if (_coroutineResult.success && textureBakeResults != null) { mAndAs = this.OnCombinedTexturesCoroutineAtlasesAndRects; } } catch (Exception e) { Debug.LogError(e); } finally { if (saveAtlasesAsAssets) { //Atlases were saved to project so we don't need these ones if (mAndAs != null) { for (int j = 0; j < mAndAs.Length; j++) { MB_AtlasesAndRects mAndA = mAndAs[j]; if (mAndA != null && mAndA.atlases != null) { for (int i = 0; i < mAndA.atlases.Length; i++) { if (mAndA.atlases[i] != null) { if (editorMethods != null) { editorMethods.Destroy(mAndA.atlases[i]); } else { MB_Utility.Destroy(mAndA.atlases[i]); } } } } } } } } return(mAndAs); }
public IEnumerator CreateAtlases(ProgressUpdateDelegate progressInfo, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Rect[] uvRects = packedAtlasRects.rects; if (data.distinctMaterialTextures.Count == 1 && data._fixOutOfBoundsUVs == false) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Only one image per atlas. Will re-use original texture"); } uvRects = new Rect[1]; uvRects[0] = new Rect(0f, 0f, 1f, 1f); for (int i = 0; i < data.numAtlases; i++) { MeshBakerMaterialTexture dmt = data.distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.GetTexture2D(); data.resultMaterial.SetTexture(data.texPropertyNames[i].name, atlases[i]); data.resultMaterial.SetTextureScale(data.texPropertyNames[i].name, dmt.matTilingRect.size); data.resultMaterial.SetTextureOffset(data.texPropertyNames[i].name, dmt.matTilingRect.min); } } else { long estArea = 0; int atlasSizeX = 1; int atlasSizeY = 1; uvRects = null; for (int i = 0; i < data.numAtlases; i++) { //i is an atlas "MainTex", "BumpMap" etc... //----------------------- Texture2D atlas = null; if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(i, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.LogWarning("Beginning loop " + i + " num temporary textures " + combiner._temporaryTextures.Count); } for (int j = 0; j < data.distinctMaterialTextures.Count; j++) { //j is a distinct set of textures one for each of "MainTex", "BumpMap" etc... MB_TexSet txs = data.distinctMaterialTextures[j]; int tWidth = txs.idealWidth; int tHeight = txs.idealHeight; Texture2D tx = txs.ts[i].GetTexture2D(); if (tx == null) { tx = txs.ts[i].t = combiner._createTemporaryTexture(tWidth, tHeight, TextureFormat.ARGB32, true); if (data._considerNonTextureProperties && data.nonTexturePropertyBlender != null) { Color col = data.nonTexturePropertyBlender.GetColorIfNoTexture(txs.matsAndGOs.mats[0].mat, data.texPropertyNames[i]); if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("Setting texture to solid color " + col); } MB_Utility.setSolidColor(tx, col); } else { Color col = MB3_TextureCombinerNonTextureProperties.GetColorIfNoTexture(data.texPropertyNames[i]); MB_Utility.setSolidColor(tx, col); } } if (progressInfo != null) { progressInfo("Adjusting for scale and offset " + tx, .01f); } if (textureEditorMethods != null) { textureEditorMethods.SetReadWriteFlag(tx, true, true); } tx = GetAdjustedForScaleAndOffset2(txs.ts[i], txs.obUVoffset, txs.obUVscale, data, combiner, LOG_LEVEL); //create a resized copy if necessary if (tx.width != tWidth || tx.height != tHeight) { if (progressInfo != null) { progressInfo("Resizing texture '" + tx + "'", .01f); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.LogWarning("Copying and resizing texture " + data.texPropertyNames[i].name + " from " + tx.width + "x" + tx.height + " to " + tWidth + "x" + tHeight); } tx = combiner._resizeTexture((Texture2D)tx, tWidth, tHeight); } txs.ts[i].t = tx; } Texture2D[] texToPack = new Texture2D[data.distinctMaterialTextures.Count]; for (int j = 0; j < data.distinctMaterialTextures.Count; j++) { Texture2D tx = data.distinctMaterialTextures[j].ts[i].GetTexture2D(); estArea += tx.width * tx.height; if (data._considerNonTextureProperties) { //combine the tintColor with the texture tx = combiner._createTextureCopy(tx); data.nonTexturePropertyBlender.TintTextureWithTextureCombiner(tx, data.distinctMaterialTextures[j], data.texPropertyNames[i]); } texToPack[j] = tx; } if (textureEditorMethods != null) { textureEditorMethods.CheckBuildSettings(estArea); } if (Math.Sqrt(estArea) > 3500f) { if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("The maximum possible atlas size is 4096. Textures may be shrunk"); } } atlas = new Texture2D(1, 1, TextureFormat.ARGB32, true); if (progressInfo != null) { progressInfo("Packing texture atlas " + data.texPropertyNames[i].name, .25f); } if (i == 0) { if (progressInfo != null) { progressInfo("Estimated min size of atlases: " + Math.Sqrt(estArea).ToString("F0"), .1f); } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Estimated atlas minimum size:" + Math.Sqrt(estArea).ToString("F0")); } if (data.distinctMaterialTextures.Count == 1 && data._fixOutOfBoundsUVs == false) { //don't want to force power of 2 so tiling will still work uvRects = new Rect[1] { new Rect(0f, 0f, 1f, 1f) }; atlas = _copyTexturesIntoAtlas(texToPack, data._atlasPadding, uvRects, texToPack[0].width, texToPack[0].height, combiner); } else { int maxAtlasSize = 4096; uvRects = atlas.PackTextures(texToPack, data._atlasPadding, maxAtlasSize, false); } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("After pack textures atlas size " + atlas.width + " " + atlas.height); } atlasSizeX = atlas.width; atlasSizeY = atlas.height; atlas.Apply(); } else { if (progressInfo != null) { progressInfo("Copying Textures Into: " + data.texPropertyNames[i].name, .1f); } atlas = _copyTexturesIntoAtlas(texToPack, data._atlasPadding, uvRects, atlasSizeX, atlasSizeY, combiner); } } atlases[i] = atlas; //---------------------- if (data._saveAtlasesAsAssets && textureEditorMethods != null) { textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], data.texPropertyNames[i], i, data.resultMaterial); } data.resultMaterial.SetTextureOffset(data.texPropertyNames[i].name, Vector2.zero); data.resultMaterial.SetTextureScale(data.texPropertyNames[i].name, Vector2.one); combiner._destroyTemporaryTextures(); // need to save atlases before doing this GC.Collect(); } } packedAtlasRects.rects = uvRects; yield break; }
//float _maxTimePerFrameForCoroutine; public IEnumerator CombineTexturesIntoAtlasesCoroutine(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List <GameObject> objsToMesh, List <Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods = null, CombineTexturesIntoAtlasesCoroutineResult coroutineResult = null, float maxTimePerFrame = .01f, List <AtlasPackingResult> packingResults = null, bool onlyPackRects = false, bool splitAtlasWhenPackingIfTooBig = false) { if (!_RunCorutineWithoutPauseIsRunning && (MBVersion.GetMajorVersion() < 5 || (MBVersion.GetMajorVersion() == 5 && MBVersion.GetMinorVersion() < 3))) { Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher"); yield return(null); } coroutineResult.success = true; coroutineResult.isFinished = false; if (maxTimePerFrame <= 0f) { Debug.LogError("maxTimePerFrame must be a value greater than zero"); coroutineResult.isFinished = true; yield break; } //_maxTimePerFrameForCoroutine = maxTimePerFrame; yield return(_CombineTexturesIntoAtlases(progressInfo, coroutineResult, resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods, packingResults, onlyPackRects, splitAtlasWhenPackingIfTooBig)); coroutineResult.isFinished = true; yield break; }
public IEnumerator CreateAtlases(ProgressUpdateDelegate progressInfo, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { Rect[] uvRects = packedAtlasRects.rects; if (uvRects.Length == 1) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Only one image per atlas. Will re-use original texture"); } for (int i = 0; i < data.numAtlases; i++) { MeshBakerMaterialTexture dmt = data.distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.GetTexture2D(); data.resultMaterial.SetTexture(data.texPropertyNames[i].name, atlases[i]); data.resultMaterial.SetTextureScale(data.texPropertyNames[i].name, dmt.matTilingRect.size); data.resultMaterial.SetTextureOffset(data.texPropertyNames[i].name, dmt.matTilingRect.min); } } else { int atlasSizeX = packedAtlasRects.atlasX; int atlasSizeY = packedAtlasRects.atlasY; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY); } for (int propIdx = 0; propIdx < data.numAtlases; propIdx++) { Texture2D atlas = null; if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(propIdx, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("=== Not creating atlas for " + data.texPropertyNames[propIdx].name + " because textures are null and default value parameters are the same."); } } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("=== Creating atlas for " + data.texPropertyNames[propIdx].name); } GC.Collect(); //use a jagged array because it is much more efficient in memory Color[][] atlasPixels = new Color[atlasSizeY][]; for (int j = 0; j < atlasPixels.Length; j++) { atlasPixels[j] = new Color[atlasSizeX]; } bool isNormalMap = false; if (data.texPropertyNames[propIdx].isNormalMap) { isNormalMap = true; } for (int texSetIdx = 0; texSetIdx < data.distinctMaterialTextures.Count; texSetIdx++) { string s = "Creating Atlas '" + data.texPropertyNames[propIdx].name + "' texture " + data.distinctMaterialTextures[texSetIdx]; if (progressInfo != null) { progressInfo(s, .01f); } MB_TexSet texSet = data.distinctMaterialTextures[texSetIdx]; if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(string.Format("Adding texture {0} to atlas {1}", texSet.ts[propIdx].GetTexture2D() == null ? "null" : texSet.ts[propIdx].GetTexName(), data.texPropertyNames[propIdx])); } Rect r = uvRects[texSetIdx]; Texture2D t = texSet.ts[propIdx].GetTexture2D(); int x = Mathf.RoundToInt(r.x * atlasSizeX); int y = Mathf.RoundToInt(r.y * atlasSizeY); int ww = Mathf.RoundToInt(r.width * atlasSizeX); int hh = Mathf.RoundToInt(r.height * atlasSizeY); if (ww == 0 || hh == 0) { Debug.LogError("Image in atlas has no height or width " + r); } if (progressInfo != null) { progressInfo(s + " set ReadWrite flag", .01f); } if (textureEditorMethods != null) { textureEditorMethods.SetReadWriteFlag(t, true, true); } if (progressInfo != null) { progressInfo(s + "Copying to atlas: '" + texSet.ts[propIdx].GetTexName() + "'", .02f); } DRect samplingRect = texSet.ts[propIdx].encapsulatingSamplingRect; yield return(CopyScaledAndTiledToAtlas(texSet.ts[propIdx], texSet, data.texPropertyNames[propIdx], samplingRect, x, y, ww, hh, packedAtlasRects.padding[texSetIdx], atlasPixels, isNormalMap, data, combiner, progressInfo, LOG_LEVEL)); // Debug.Log("after copyScaledAndTiledAtlas"); } yield return(data.numAtlases); if (progressInfo != null) { progressInfo("Applying changes to atlas: '" + data.texPropertyNames[propIdx].name + "'", .03f); } atlas = new Texture2D(atlasSizeX, atlasSizeY, TextureFormat.ARGB32, true); for (int j = 0; j < atlasPixels.Length; j++) { atlas.SetPixels(0, j, atlasSizeX, 1, atlasPixels[j]); } atlas.Apply(); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Saving atlas " + data.texPropertyNames[propIdx].name + " w=" + atlas.width + " h=" + atlas.height); } } atlases[propIdx] = atlas; if (progressInfo != null) { progressInfo("Saving atlas: '" + data.texPropertyNames[propIdx].name + "'", .04f); } System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); if (data._saveAtlasesAsAssets && textureEditorMethods != null) { textureEditorMethods.SaveAtlasToAssetDatabase(atlases[propIdx], data.texPropertyNames[propIdx], propIdx, data.resultMaterial); } else { data.resultMaterial.SetTexture(data.texPropertyNames[propIdx].name, atlases[propIdx]); } data.resultMaterial.SetTextureOffset(data.texPropertyNames[propIdx].name, Vector2.zero); data.resultMaterial.SetTextureScale(data.texPropertyNames[propIdx].name, Vector2.one); combiner._destroyTemporaryTextures(); // need to save atlases before doing this } } //data.rectsInAtlas = uvRects; // Debug.Log("finished!"); yield break; }
//texPropertyNames is the list of texture properties in the resultMaterial //allowedMaterialsFilter is a list of materials. Objects without any of these materials will be ignored. // this is used by the multiple materials filter //textureEditorMethods encapsulates editor only functionality such as saving assets and tracking texture assets whos format was changed. Is null if using at runtime. IEnumerator __CombineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, CombineTexturesIntoAtlasesCoroutineResult result, MB_AtlasesAndRects resultAtlasesAndRects, MB3_TextureCombinerPipeline.TexturePipelineData data, bool splitAtlasWhenPackingIfTooBig, MB2_EditorMethodsInterface textureEditorMethods) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("__CombineTexturesIntoAtlases texture properties in shader:" + data.texPropertyNames.Count + " objsToMesh:" + data.allObjsToMesh.Count + " _fixOutOfBoundsUVs:" + data._fixOutOfBoundsUVs); } if (progressInfo != null) { progressInfo("Collecting textures ", .01f); } /* * each atlas (maintex, bump, spec etc...) will have distinctMaterialTextures.Count images in it. * each distinctMaterialTextures record is a set of textures, one for each atlas. And a list of materials * that use that distinct set of textures. */ List <GameObject> usedObjsToMesh = new List <GameObject>(); yield return(MB3_TextureCombinerPipeline.__Step1_CollectDistinctMatTexturesAndUsedObjects(progressInfo, result, data, this, textureEditorMethods, usedObjsToMesh, LOG_LEVEL)); if (!result.success) { yield break; } if (MB3_MeshCombiner.EVAL_VERSION) { bool usesAllowedShaders = true; for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { for (int j = 0; j < data.distinctMaterialTextures[i].matsAndGOs.mats.Count; j++) { if (!data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name.EndsWith("Diffuse") && !data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name.EndsWith("Bumped Diffuse")) { Debug.LogError("The free version of Mesh Baker only works with Diffuse and Bumped Diffuse Shaders. The full version can be used with any shader. Material " + data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.name + " uses shader " + data.distinctMaterialTextures[i].matsAndGOs.mats[j].mat.shader.name); usesAllowedShaders = false; } } } if (!usesAllowedShaders) { result.success = false; yield break; } } //Textures in each material (_mainTex, Bump, Spec ect...) must be same size //Calculate the best sized to use. Takes into account tiling //if only one texture in atlas re-uses original sizes yield return(MB3_TextureCombinerPipeline.CalculateIdealSizesForTexturesInAtlasAndPadding(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL)); if (!result.success) { yield break; } //buildAndSaveAtlases StringBuilder report = MB3_TextureCombinerPipeline.GenerateReport(data); MB_ITextureCombinerPacker texturePaker = MB3_TextureCombinerPipeline.CreatePacker(data.OnlyOneTextureInAtlasReuseTextures(), data._packingAlgorithm); yield return(texturePaker.ConvertTexturesToReadableFormats(progressInfo, result, data, this, textureEditorMethods, LOG_LEVEL)); if (!result.success) { yield break; } AtlasPackingResult[] uvRects = texturePaker.CalculateAtlasRectangles(data, splitAtlasWhenPackingIfTooBig, LOG_LEVEL); yield return(MB3_TextureCombinerPipeline.__Step3_BuildAndSaveAtlasesAndStoreResults(result, progressInfo, data, this, texturePaker, uvRects[0], textureEditorMethods, resultAtlasesAndRects, report, LOG_LEVEL)); }
public IEnumerator CreateAtlasesCoroutine(ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { yield return(_CreateAtlasesCoroutine(progressInfo, coroutineResult, saveAtlasesAsAssets, editorMethods, maxTimePerFrame)); if (coroutineResult.success && onBuiltAtlasesSuccess != null) { onBuiltAtlasesSuccess(); } if (!coroutineResult.success && onBuiltAtlasesFail != null) { onBuiltAtlasesFail(); } }
bool _combineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, MB2_EditorMethodsInterface textureEditorMethods){ bool success = false; try{ _temporaryTextures.Clear(); if (textureEditorMethods != null) textureEditorMethods.Clear(); if (objsToMesh == null || objsToMesh.Count == 0){ Debug.LogError("No meshes to combine. Please assign some meshes to combine."); return false; } if (atlasPadding < 0){ Debug.LogError("Atlas padding must be zero or greater."); return false; } if (maxTilingBakeSize < 2 || maxTilingBakeSize > 4096){ Debug.LogError("Invalid value for max tiling bake size."); return false; } if (progressInfo != null) progressInfo("Collecting textures for " + objsToMesh.Count + " meshes.", .01f); List<string> texPropertyNames = new List<string>(); if (!_collectPropertyNames(resultMaterial, texPropertyNames)){ return false; } success = __combineTexturesIntoAtlases(progressInfo,resultAtlasesAndRects, resultMaterial, texPropertyNames, objsToMesh,allowedMaterialsFilter, textureEditorMethods); } catch (MissingReferenceException mrex){ Debug.LogError("Creating atlases failed a MissingReferenceException was thrown. This is normally only happens when trying to create very large atlases and Unity is running out of Memory. Try changing the 'Texture Packer' to a different option, it may work with an alternate packer. This error is sometimes intermittant. Try baking again."); Debug.LogError(mrex); } catch (Exception ex){ Debug.LogError(ex); } finally { _destroyTemporaryTextures(); if (textureEditorMethods != null) textureEditorMethods.SetReadFlags(progressInfo); } return success; }
private IEnumerator _CreateAtlasesCoroutineAtlases(MB3_TextureCombiner combiner, ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { int numResults = 1; if (_doMultiMaterial) { numResults = resultMaterials.Length; } OnCombinedTexturesCoroutineAtlasesAndRects = new MB_AtlasesAndRects[numResults]; for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { OnCombinedTexturesCoroutineAtlasesAndRects[i] = new MB_AtlasesAndRects(); } //Do the material combining. for (int i = 0; i < OnCombinedTexturesCoroutineAtlasesAndRects.Length; i++) { Material resMatToPass = null; List <Material> sourceMats = null; if (_doMultiMaterial) { sourceMats = resultMaterials[i].sourceMaterials; resMatToPass = resultMaterials[i].combinedMaterial; combiner.fixOutOfBoundsUVs = resultMaterials[i].considerMeshUVs; } else { resMatToPass = _resultMaterial; } MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult coroutineResult2 = new MB3_TextureCombiner.CombineTexturesIntoAtlasesCoroutineResult(); yield return(combiner.CombineTexturesIntoAtlasesCoroutine(progressInfo, OnCombinedTexturesCoroutineAtlasesAndRects[i], resMatToPass, objsToMesh, sourceMats, editorMethods, coroutineResult2, maxTimePerFrame, onlyPackRects: false, splitAtlasWhenPackingIfTooBig: false)); coroutineResult.success = coroutineResult2.success; if (!coroutineResult.success) { coroutineResult.isFinished = true; yield break; } } unpackMat2RectMap(OnCombinedTexturesCoroutineAtlasesAndRects); if (coroutineResult.success && editorMethods != null) { editorMethods.GetMaterialPrimaryKeysIfAddressables(textureBakeResults); } //Save the results textureBakeResults.resultType = MB2_TextureBakeResults.ResultType.atlas; textureBakeResults.resultMaterialsTexArray = new MB_MultiMaterialTexArray[0]; textureBakeResults.doMultiMaterial = _doMultiMaterial; if (_doMultiMaterial) { textureBakeResults.resultMaterials = resultMaterials; } else { MB_MultiMaterial[] resMats = new MB_MultiMaterial[1]; resMats[0] = new MB_MultiMaterial(); resMats[0].combinedMaterial = _resultMaterial; resMats[0].considerMeshUVs = _fixOutOfBoundsUVs; resMats[0].sourceMaterials = new List <Material>(); for (int i = 0; i < textureBakeResults.materialsAndUVRects.Length; i++) { resMats[0].sourceMaterials.Add(textureBakeResults.materialsAndUVRects[i].material); } textureBakeResults.resultMaterials = resMats; } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Atlases"); } }
//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; }
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"); } } }
Rect[] __CreateAtlasesMBTexturePacker(ProgressUpdateDelegate progressInfo, int numAtlases, List<MB_TexSet> distinctMaterialTextures, List<string> texPropertyNames, Material resultMaterial, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, int _padding){ Rect[] uvRects; if (distinctMaterialTextures.Count == 1){ if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Only one image per atlas. Will re-use original texture"); uvRects = new Rect[1]; uvRects[0] = new Rect(0f,0f,1f,1f); for (int i = 0; i < numAtlases; i++){ MeshBakerMaterialTexture dmt = distinctMaterialTextures[0].ts[i]; atlases[i] = dmt.t; resultMaterial.SetTexture(texPropertyNames[i],atlases[i]); resultMaterial.SetTextureScale(texPropertyNames[i],dmt.scale); resultMaterial.SetTextureOffset(texPropertyNames[i],dmt.offset); } } else { List<Vector2> imageSizes = new List<Vector2>(); for (int i = 0; i < distinctMaterialTextures.Count; i++){ imageSizes.Add(new Vector2(distinctMaterialTextures[i].idealWidth, distinctMaterialTextures[i].idealHeight)); } MB2_TexturePacker tp = new MB2_TexturePacker(); int atlasSizeX = 1; int atlasSizeY = 1; //todo add sanity warnings for huge atlasesr int atlasMaxDimension = 4096; if (textureEditorMethods != null) atlasMaxDimension = textureEditorMethods.GetMaximumAtlasDimension(); uvRects = tp.GetRects(imageSizes,atlasMaxDimension,_padding,out atlasSizeX, out atlasSizeY); if (LOG_LEVEL >= MB2_LogLevel.info) Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY + " (Max atlas size for platform: " + atlasMaxDimension + ")"); for (int i = 0; i < numAtlases; i++){ GC.Collect(); if (progressInfo != null) progressInfo("Creating Atlas '" + texPropertyNames[i] + "'", .01f); Color[] atlasPixels = new Color[atlasSizeX * atlasSizeY];//atlas.GetPixels(); for (int j = 0; j < atlasPixels.Length; j++) atlasPixels[j] = Color.clear; for (int j = 0; j < distinctMaterialTextures.Count; j++){ if (LOG_LEVEL >= MB2_LogLevel.trace) MB2_Log.Trace("Adding texture {0} to atlas {1}", distinctMaterialTextures[j].ts[i].t == null ? "null" : distinctMaterialTextures[j].ts[i].t.ToString(),texPropertyNames[i]); Rect r = uvRects[j]; Texture2D t = distinctMaterialTextures[j].ts[i].t; int x = Mathf.RoundToInt(r.x * atlasSizeX); int y = Mathf.RoundToInt(r.y * atlasSizeY); int ww = Mathf.RoundToInt(r.width * atlasSizeX); int hh = Mathf.RoundToInt(r.height * atlasSizeY); if (ww == 0 || hh == 0) Debug.LogError("Image in atlas has no height or width"); if (textureEditorMethods != null) textureEditorMethods.SetReadWriteFlag(t, true, true); if (progressInfo != null) progressInfo("Copying to atlas: '" + distinctMaterialTextures[j].ts[i].t + "'", .02f); CopyScaledAndTiledToAtlas(distinctMaterialTextures[j].ts[i],x,y,ww,hh,fixOutOfBoundsUVs,maxTilingBakeSize,atlasPixels,atlasSizeX,progressInfo); } if (progressInfo != null) progressInfo("Applying changes to atlas: '" + texPropertyNames[i] + "'", .03f); Texture2D atlas = new Texture2D(atlasSizeX, atlasSizeY,TextureFormat.ARGB32, true); atlas.SetPixels(atlasPixels); atlas.Apply(); if (LOG_LEVEL >= MB2_LogLevel.debug) Debug.Log("Saving atlas " + texPropertyNames[i] + " w=" + atlas.width + " h=" + atlas.height); atlases[i] = atlas; if (progressInfo != null) progressInfo("Saving atlas: '" + texPropertyNames[i] + "'", .04f); if (saveAtlasesAsAssets && textureEditorMethods != null){ textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], texPropertyNames[i], i, resultMaterial); } else { resultMaterial.SetTexture(texPropertyNames[i], atlases[i]); } resultMaterial.SetTextureOffset(texPropertyNames[i], Vector2.zero); resultMaterial.SetTextureScale(texPropertyNames[i],Vector2.one); _destroyTemporaryTextures(); // need to save atlases before doing this } } return uvRects; }
private IEnumerator _CreateAtlasesCoroutine(ProgressUpdateDelegate progressInfo, MB3_TextureCombiner.CreateAtlasesCoroutineResult coroutineResult, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null, float maxTimePerFrame = .01f) { MBVersionConcrete mbv = new MBVersionConcrete(); if (!MB3_TextureCombiner._RunCorutineWithoutPauseIsRunning && (mbv.GetMajorVersion() < 5 || (mbv.GetMajorVersion() == 5 && mbv.GetMinorVersion() < 3))) { Debug.LogError("Running the texture combiner as a coroutine only works in Unity 5.3 and higher"); coroutineResult.success = false; yield break; } this.OnCombinedTexturesCoroutineAtlasesAndRects = null; if (maxTimePerFrame <= 0f) { Debug.LogError("maxTimePerFrame must be a value greater than zero"); coroutineResult.isFinished = true; yield break; } MB2_ValidationLevel vl = Application.isPlaying ? MB2_ValidationLevel.quick : MB2_ValidationLevel.robust; if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, null, vl)) { coroutineResult.isFinished = true; yield break; } if (_doMultiMaterial && !_ValidateResultMaterials()) { coroutineResult.isFinished = true; yield break; } else if (resultType == MB2_TextureBakeResults.ResultType.textureArray) { //TODO validate texture arrays. } else if (!_doMultiMaterial) { if (_resultMaterial == null) { Debug.LogError("Combined Material is null please create and assign a result material."); coroutineResult.isFinished = true; yield break; } Shader targShader = _resultMaterial.shader; for (int i = 0; i < objsToMesh.Count; i++) { Material[] ms = MB_Utility.GetGOMaterials(objsToMesh[i]); for (int j = 0; j < ms.Length; j++) { Material m = ms[j]; if (m != null && m.shader != targShader) { Debug.LogWarning("Game object " + objsToMesh[i] + " does not use shader " + targShader + " it may not have the required textures. If not small solid color textures will be generated."); } } } } MB3_TextureCombiner combiner = CreateAndConfigureTextureCombiner(); combiner.saveAtlasesAsAssets = saveAtlasesAsAssets; OnCombinedTexturesCoroutineAtlasesAndRects = null; if (resultType == MB2_TextureBakeResults.ResultType.textureArray) { yield return(_CreateAtlasesCoroutineTextureArray(combiner, progressInfo, coroutineResult, saveAtlasesAsAssets, editorMethods, maxTimePerFrame)); if (!coroutineResult.success) { yield break; } } else { yield return(_CreateAtlasesCoroutineAtlases(combiner, progressInfo, coroutineResult, saveAtlasesAsAssets, editorMethods, maxTimePerFrame)); if (!coroutineResult.success) { yield break; } } //set the texture bake resultAtlasesAndRects on the Mesh Baker component if it exists MB3_MeshBakerCommon[] mb = GetComponentsInChildren <MB3_MeshBakerCommon>(); for (int i = 0; i < mb.Length; i++) { mb[i].textureBakeResults = textureBakeResults; } coroutineResult.isFinished = true; }
/**<summary>Combines meshes and generates texture atlases.</summary> * <param name="createTextureAtlases">Whether or not texture atlases should be created. If not uvs will not be adjusted.</param> * <param name="progressInfo">A delegate function that will be called to report progress.</param> * <param name="textureEditorMethods">If called from the editor should be an instance of MB2_EditorMethods. If called at runtime should be null.</param> * <remarks>Combines meshes and generates texture atlases</remarks> */ public bool combineTexturesIntoAtlases(ProgressUpdateDelegate progressInfo, MB_AtlasesAndRects resultAtlasesAndRects, Material resultMaterial, List<GameObject> objsToMesh, List<Material> allowedMaterialsFilter, int atlasPadding, List<string> customShaderPropNames, bool resizePowerOfTwoTextures, bool fixOutOfBoundsUVs, int maxTilingBakeSize, bool saveAtlasesAsAssets = false, MB2_PackingAlgorithmEnum packingAlgorithm = MB2_PackingAlgorithmEnum.UnitysPackTextures, MB2_EditorMethodsInterface textureEditorMethods = null){ this.atlasPadding = atlasPadding; this.resizePowerOfTwoTextures = resizePowerOfTwoTextures; this.fixOutOfBoundsUVs = fixOutOfBoundsUVs; this.maxTilingBakeSize = maxTilingBakeSize; this.saveAtlasesAsAssets = saveAtlasesAsAssets; this.packingAlgorithm = packingAlgorithm; this.customShaderPropNames = customShaderPropNames; return _combineTexturesIntoAtlases(progressInfo,resultAtlasesAndRects, resultMaterial, objsToMesh, allowedMaterialsFilter, textureEditorMethods); }
public override void DestroyMeshEditor(MB2_EditorMethodsInterface editorMethods){ _update_MB2_MeshCombiner(); meshCombiner.DestroyMeshEditor(editorMethods); }