/// <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); CreateAtlasesCoroutineResult result = new CreateAtlasesCoroutineResult(); MB3_TextureCombiner.RunCorutineWithoutPause(CreateAtlasesCoroutine(progressInfo, result, saveAtlasesAsAssets, editorMethods, 1000f), 0); if (result.success) { mAndAs = textureBakeResults.combinedMaterialInfo; } } 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 Texture2D OnRenderAtlas(MB3_TextureCombiner combiner) { fastRenderer = new MB_TextureCombinerRenderTexture(); _doRenderAtlas = true; TextureBlender blender = null; if (resultMaterialTextureBlender != null) { blender = resultMaterialTextureBlender.GetTextureBlender(); } Texture2D atlas = fastRenderer.DoRenderAtlas(this.gameObject, width, height, padding, rects, textureSets, indexOfTexSetToRender, texPropertyName, blender, isNormalMap, fixOutOfBoundsUVs, considerNonTextureProperties, combiner, LOG_LEVEL); _doRenderAtlas = false; return(atlas); }
public MB3_TextureCombiner CreateAndConfigureTextureCombiner() { 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.considerNonTextureProperties = _considerNonTextureProperties; return(combiner); }
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; }
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"); } } }
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"); } }
public Texture2D DoRenderAtlas(GameObject gameObject, int width, int height, int padding, Rect[] rss, List <MB3_TextureCombiner.MB_TexSet> textureSetss, int indexOfTexSetToRenders, ShaderTextureProperty texPropertyname, TextureBlender resultMaterialTextureBlender, bool isNormalMap, bool fixOutOfBoundsUVs, bool considerNonTextureProperties, MB3_TextureCombiner texCombiner, MB2_LogLevel LOG_LEV) { LOG_LEVEL = LOG_LEV; textureSets = textureSetss; indexOfTexSetToRender = indexOfTexSetToRenders; _texPropertyName = texPropertyname; _padding = padding; _isNormalMap = isNormalMap; _fixOutOfBoundsUVs = fixOutOfBoundsUVs; //_considerNonTextureProperties = considerNonTextureProperties; _resultMaterialTextureBlender = resultMaterialTextureBlender; combiner = texCombiner; rs = rss; Shader s; if (_isNormalMap) { s = Shader.Find("MeshBaker/NormalMapShader"); } else { s = Shader.Find("MeshBaker/AlbedoShader"); } if (s == null) { Debug.LogError("Could not find shader for RenderTexture. Try reimporting mesh baker"); return(null); } mat = new Material(s); _destinationTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32); _destinationTexture.filterMode = FilterMode.Point; myCamera = gameObject.GetComponent <Camera>(); myCamera.orthographic = true; myCamera.orthographicSize = height >> 1; myCamera.aspect = width / height; myCamera.targetTexture = _destinationTexture; myCamera.clearFlags = CameraClearFlags.Color; Transform camTransform = myCamera.GetComponent <Transform>(); camTransform.localPosition = new Vector3(width / 2.0f, height / 2f, 3); camTransform.localRotation = Quaternion.Euler(0, 180, 180); _doRenderAtlas = true; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(string.Format("Begin Camera.Render destTex w={0} h={1} camPos={2}", width, height, camTransform.localPosition)); } //This triggers the OnRenderObject callback myCamera.Render(); _doRenderAtlas = false; MB_Utility.Destroy(mat); MB_Utility.Destroy(_destinationTexture); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Finished Camera.Render "); } Texture2D tempTex = targTex; targTex = null; return(tempTex); }
private void CopyScaledAndTiledToAtlas(MB3_TextureCombiner.MB_TexSet texSet, MB3_TextureCombiner.MeshBakerMaterialTexture source, Vector2 obUVoffset, Vector2 obUVscale, Rect rec, ShaderTextureProperty texturePropertyName, TextureBlender resultMatTexBlender) { Rect r = rec; if (resultMatTexBlender != null) { myCamera.backgroundColor = resultMatTexBlender.GetColorIfNoTexture(texSet.mats[0].mat, texturePropertyName); } else { myCamera.backgroundColor = MB3_TextureCombiner.GetColorIfNoTexture(texturePropertyName); } if (source.t == null) { source.t = combiner._createTemporaryTexture(16, 16, TextureFormat.ARGB32, true); } r.y = 1f - (r.y + r.height); // DrawTexture uses topLeft 0,0, Texture2D uses bottomLeft 0,0 r.x *= _destinationTexture.width; r.y *= _destinationTexture.height; r.width *= _destinationTexture.width; r.height *= _destinationTexture.height; Rect rPadded = r; rPadded.x -= _padding; rPadded.y -= _padding; rPadded.width += _padding * 2; rPadded.height += _padding * 2; Rect srcPrTex = source.matTilingRect.GetRect(); Rect targPr = new Rect(); if (_fixOutOfBoundsUVs) { Rect ruv = new Rect(obUVoffset.x, obUVoffset.y, obUVscale.x, obUVscale.y); srcPrTex = MB3_UVTransformUtility.CombineTransforms(ref srcPrTex, ref ruv); if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("Fixing out of bounds UVs for tex " + source.t); } } Texture2D tex = source.t; /* * if (_considerNonTextureProperties && resultMatTexBlender != null) * { * if (LOG_LEVEL >= MB2_LogLevel.trace) Debug.Log(string.Format("Blending texture {0} mat {1} with non-texture properties using TextureBlender {2}", tex.name, texSet.mats[0].mat, resultMatTexBlender)); * * resultMatTexBlender.OnBeforeTintTexture(texSet.mats[0].mat, texturePropertyName.name); * //combine the tintColor with the texture * tex = combiner._createTextureCopy(tex); * for (int i = 0; i < tex.height; i++) * { * Color[] cs = tex.GetPixels(0, i, tex.width, 1); * for (int j = 0; j < cs.Length; j++) * { * cs[j] = resultMatTexBlender.OnBlendTexturePixel(texturePropertyName.name, cs[j]); * } * tex.SetPixels(0, i, tex.width, 1, cs); * } * tex.Apply(); * } */ //main texture TextureWrapMode oldTexWrapMode = tex.wrapMode; if (srcPrTex.width == 1f && srcPrTex.height == 1f && srcPrTex.x == 0f && srcPrTex.y == 0f) { //fixes bug where there is a dark line at the edge of the texture tex.wrapMode = TextureWrapMode.Clamp; } else { tex.wrapMode = TextureWrapMode.Repeat; } if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("DrawTexture tex=" + tex.name + " destRect=" + r + " srcRect=" + srcPrTex + " Mat=" + mat); } //fill the padding first Rect srcPr = new Rect(); //top margin srcPr.x = srcPrTex.x; srcPr.y = srcPrTex.y + 1 - 1f / tex.height; srcPr.width = srcPrTex.width; srcPr.height = 1f / tex.height; targPr.x = r.x; targPr.y = rPadded.y; targPr.width = r.width; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //bot margin srcPr.x = srcPrTex.x; srcPr.y = srcPrTex.y; srcPr.width = srcPrTex.width; srcPr.height = 1f / tex.height; targPr.x = r.x; targPr.y = r.y + r.height; targPr.width = r.width; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //left margin srcPr.x = srcPrTex.x; srcPr.y = srcPrTex.y; srcPr.width = 1f / tex.width; srcPr.height = srcPrTex.height; targPr.x = rPadded.x; targPr.y = r.y; targPr.width = _padding; targPr.height = r.height; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //right margin srcPr.x = srcPrTex.x + 1f - 1f / tex.width; srcPr.y = srcPrTex.y; srcPr.width = 1f / tex.width; srcPr.height = srcPrTex.height; targPr.x = r.x + r.width; targPr.y = r.y; targPr.width = _padding; targPr.height = r.height; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //top left corner srcPr.x = srcPrTex.x; srcPr.y = srcPrTex.y + 1 - 1f / tex.height; srcPr.width = 1f / tex.width; srcPr.height = 1f / tex.height; targPr.x = rPadded.x; targPr.y = rPadded.y; targPr.width = _padding; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //top right corner srcPr.x = srcPrTex.x + 1f - 1f / tex.width; srcPr.y = srcPrTex.y + 1 - 1f / tex.height; srcPr.width = 1f / tex.width; srcPr.height = 1f / tex.height; targPr.x = r.x + r.width; targPr.y = rPadded.y; targPr.width = _padding; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //bot left corner srcPr.x = srcPrTex.x; srcPr.y = srcPrTex.y; srcPr.width = 1f / tex.width; srcPr.height = 1f / tex.height; targPr.x = rPadded.x; targPr.y = r.y + r.height; targPr.width = _padding; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //bot right corner srcPr.x = srcPrTex.x + 1f - 1f / tex.width; srcPr.y = srcPrTex.y; srcPr.width = 1f / tex.width; srcPr.height = 1f / tex.height; targPr.x = r.x + r.width; targPr.y = r.y + r.height; targPr.width = _padding; targPr.height = _padding; Graphics.DrawTexture(targPr, tex, srcPr, 0, 0, 0, 0, mat); //now the texture Graphics.DrawTexture(r, tex, srcPrTex, 0, 0, 0, 0, mat); tex.wrapMode = oldTexWrapMode; }
public static string ReportTextureSizesAndFormats(MB3_TextureBaker mom) { if (mom.resultType != MB2_TextureBakeResults.ResultType.textureArray) { Debug.LogError("Result Type must be Texture Array."); return(""); } for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) { MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; if (resMatTexArray.combinedMaterial == null) { Debug.LogError("Result Material " + resMatIdx + " is null"); return(""); } } System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Visit each result material for (int resMatIdx = 0; resMatIdx < mom.resultMaterialsTexArray.Length; resMatIdx++) { MB_MultiMaterialTexArray resMatTexArray = mom.resultMaterialsTexArray[resMatIdx]; // Do an atlas pack in order to collect all the textures needed by the result material // And group these by material texture property. MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); combiner.saveAtlasesAsAssets = false; combiner.fixOutOfBoundsUVs = false; List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempMat = new Material(resMatTexArray.combinedMaterial.shader); List <Material> allSourceMaterials = new List <Material>(); for (int sliceIdx = 0; sliceIdx < resMatTexArray.slices.Count; sliceIdx++) { List <Material> srcMats = new List <Material>(); resMatTexArray.slices[sliceIdx].GetAllUsedMaterials(srcMats); for (int srcMatIdx = 0; srcMatIdx < srcMats.Count; srcMatIdx++) { if (srcMats[srcMatIdx] != null && !allSourceMaterials.Contains(srcMats[srcMatIdx])) { allSourceMaterials.Add(srcMats[srcMatIdx]); } } } MB_AtlasesAndRects atlasesAndRects = new MB_AtlasesAndRects(); combiner.CombineTexturesIntoAtlases(null, atlasesAndRects, tempMat, mom.GetObjectsToCombine(), allSourceMaterials, null, packingResults, onlyPackRects: true, splitAtlasWhenPackingIfTooBig: false); // Now vist the packing results and collect all the textures Debug.Assert(packingResults.Count == 1); for (int texPropIdx = 0; texPropIdx < atlasesAndRects.texPropertyNames.Length; texPropIdx++) { string propertyName = atlasesAndRects.texPropertyNames[texPropIdx]; sb.AppendLine(String.Format("Prop: {0}", propertyName)); List <MB_MaterialAndUVRect> matsData = (List <MB_MaterialAndUVRect>)packingResults[0].data; HashSet <Material> visitedMats = new HashSet <Material>(); for (int matAndGoIdx = 0; matAndGoIdx < matsData.Count; matAndGoIdx++) { Material mat = matsData[matAndGoIdx].material; if (visitedMats.Contains(mat)) { continue; } visitedMats.Add(mat); if (mat.HasProperty(propertyName)) { Texture tex = mat.GetTexture(propertyName); if (tex != null) { string texFormatString = "UnknownFormat"; string texWrapMode = "UnknownClampMode"; if (tex is Texture2D) { texFormatString = ((Texture2D)tex).format.ToString(); texWrapMode = ((Texture2D)tex).wrapMode.ToString(); } sb.AppendLine(String.Format(" {0} x {1} format:{2} wrapMode:{3} {4}", tex.width.ToString().PadLeft(6, ' '), tex.height.ToString().PadRight(6, ' '), texFormatString.PadRight(20, ' '), texWrapMode.PadRight(12, ' '), tex.name)); } } } } } return(sb.ToString()); }
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); } if (!DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, editorMethods)) { 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.customShaderPropNames = _customShaderPropNames; combiner.fixOutOfBoundsUVs = _fixOutOfBoundsUVs; combiner.maxTilingBakeSize = _maxTilingBakeSize; combiner.packingAlgorithm = _packingAlgorithm; 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 = GetComponent <MB3_MeshBakerCommon>(); if (mb != null) { mb.textureBakeResults = textureBakeResults; } if (LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Atlases"); } return(resultAtlasesAndRects); }
public Texture2D DoRenderAtlas(GameObject gameObject, int width, int height, int padding, Rect[] rss, List <MB3_TextureCombiner.MB_TexSet> textureSetss, int indexOfTexSetToRenders, bool isNormalMap, bool fixOutOfBoundsUVs, MB3_TextureCombiner texCombiner, MB2_LogLevel LOG_LEV) { this.LOG_LEVEL = LOG_LEV; this.textureSets = textureSetss; this.indexOfTexSetToRender = indexOfTexSetToRenders; this._padding = padding; this._isNormalMap = isNormalMap; this._fixOutOfBoundsUVs = fixOutOfBoundsUVs; this.combiner = texCombiner; this.rs = rss; Shader shader; if (this._isNormalMap) { shader = Shader.Find("MeshBaker/NormalMapShader"); } else { shader = Shader.Find("MeshBaker/AlbedoShader"); } if (shader == null) { UnityEngine.Debug.LogError("Could not find shader for RenderTexture. Try reimporting mesh baker"); return(null); } this.mat = new Material(shader); this._destinationTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32); this._destinationTexture.filterMode = FilterMode.Point; this.myCamera = gameObject.GetComponent <Camera>(); this.myCamera.orthographic = true; this.myCamera.orthographicSize = (float)(height >> 1); this.myCamera.aspect = (float)(width / height); this.myCamera.targetTexture = this._destinationTexture; this.myCamera.clearFlags = CameraClearFlags.Color; Transform component = this.myCamera.GetComponent <Transform>(); component.localPosition = new Vector3((float)width / 2f, (float)height / 2f, 3f); component.localRotation = Quaternion.Euler(0f, 180f, 180f); this._doRenderAtlas = true; if (this.LOG_LEVEL >= MB2_LogLevel.debug) { UnityEngine.Debug.Log(string.Format("Begin Camera.Render destTex w={0} h={1} camPos={2}", width, height, component.localPosition)); } this.myCamera.Render(); this._doRenderAtlas = false; MB_Utility.Destroy(this.mat); MB_Utility.Destroy(this._destinationTexture); if (this.LOG_LEVEL >= MB2_LogLevel.debug) { UnityEngine.Debug.Log("Finished Camera.Render "); } Texture2D result = this.targTex; this.targTex = null; return(result); }
//posibilities // using fixOutOfBoundsUVs or not // public static void ConfigureMutiMaterialsFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterials, SerializedObject textureBaker) { if (mom.GetObjectsToCombine().Count == 0) { Debug.LogError("You need to add some objects to combine before building the multi material list."); return; } if (resultMaterials.arraySize > 0) { Debug.LogError("You already have some source to combined material mappings configured. You must remove these before doing this operation."); return; } if (mom.textureBakeResults == null) { Debug.LogError("Texture Bake Result asset must be set before using this operation."); return; } Dictionary <MultiMatSubmeshInfo, List <List <Material> > > shader2Material_map = new Dictionary <MultiMatSubmeshInfo, List <List <Material> > >(); Dictionary <Material, Mesh> obUVobject2mesh_map = new Dictionary <Material, Mesh>(); //validate that the objects to be combined are valid for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) { GameObject go = mom.GetObjectsToCombine()[i]; if (go == null) { Debug.LogError("Null object in list of objects to combine at position " + i); return; } Renderer r = go.GetComponent <Renderer>(); if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) { Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); return; } if (r.sharedMaterial == null) { Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); return; } } //first pass put any meshes with obUVs on their own submesh if not fixing OB uvs if (mom.doMultiMaterialSplitAtlasesIfOBUVs) { for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) { GameObject go = mom.GetObjectsToCombine()[i]; Mesh m = MB_Utility.GetMesh(go); MB_Utility.MeshAnalysisResult dummyMar = new MB_Utility.MeshAnalysisResult(); Renderer r = go.GetComponent <Renderer>(); for (int j = 0; j < r.sharedMaterials.Length; j++) { if (MB_Utility.hasOutOfBoundsUVs(m, ref dummyMar, j)) { if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j])) { Debug.LogWarning("Object " + go + " submesh " + j + " uses UVs outside the range 0,0..1,1 to generate tiling. This object has been mapped to its own submesh in the combined mesh. It can share a submesh with other objects that use different materials if you use the fix out of bounds UVs feature which will bake the tiling"); obUVobject2mesh_map.Add(r.sharedMaterials[j], m); } } } } } //second pass put other materials without OB uvs in a shader to material map for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) { Renderer r = mom.GetObjectsToCombine()[i].GetComponent <Renderer>(); for (int j = 0; j < r.sharedMaterials.Length; j++) { if (!obUVobject2mesh_map.ContainsKey(r.sharedMaterials[j])) { //if not already added if (r.sharedMaterials[j] == null) { continue; } List <List <Material> > binsOfMatsThatUseShader = null; MultiMatSubmeshInfo newKey = new MultiMatSubmeshInfo(r.sharedMaterials[j].shader, r.sharedMaterials[j]); if (!shader2Material_map.TryGetValue(newKey, out binsOfMatsThatUseShader)) { binsOfMatsThatUseShader = new List <List <Material> >(); binsOfMatsThatUseShader.Add(new List <Material>()); shader2Material_map.Add(newKey, binsOfMatsThatUseShader); } if (!binsOfMatsThatUseShader[0].Contains(r.sharedMaterials[j])) { binsOfMatsThatUseShader[0].Add(r.sharedMaterials[j]); } } } } int numResMats = shader2Material_map.Count; //third pass for each shader grouping check how big the atlas would be and group into bins that would fit in an atlas if (mom.doMultiMaterialSplitAtlasesIfTooBig) { if (mom.packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures) { Debug.LogWarning("Unity texture packer does not support splitting atlases if too big. Atlases will not be split."); } else { numResMats = 0; foreach (MultiMatSubmeshInfo sh in shader2Material_map.Keys) { List <List <Material> > binsOfMatsThatUseShader = shader2Material_map[sh]; List <Material> allMatsThatUserShader = binsOfMatsThatUseShader[0];//at this point everything is in the same list binsOfMatsThatUseShader.RemoveAt(0); MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); combiner.saveAtlasesAsAssets = false; if (allMatsThatUserShader.Count > 1) { combiner.fixOutOfBoundsUVs = mom.fixOutOfBoundsUVs; } else { combiner.fixOutOfBoundsUVs = false; } // Do the texture pack List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempMat = new Material(sh.shader); combiner.CombineTexturesIntoAtlases(null, null, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults, true, true); for (int i = 0; i < packingResults.Count; i++) { List <MatsAndGOs> matsData = (List <MatsAndGOs>)packingResults[i].data; List <Material> mats = new List <Material>(); for (int j = 0; j < matsData.Count; j++) { for (int kk = 0; kk < matsData[j].mats.Count; kk++) { if (!mats.Contains(matsData[j].mats[kk].mat)) { mats.Add(matsData[j].mats[kk].mat); } } } binsOfMatsThatUseShader.Add(mats); } numResMats += binsOfMatsThatUseShader.Count; } } } //build the result materials if (shader2Material_map.Count == 0 && obUVobject2mesh_map.Count == 0) { Debug.LogError("Found no materials in list of objects to combine"); } mom.resultMaterials = new MB_MultiMaterial[numResMats + obUVobject2mesh_map.Count]; string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); string baseName = Path.GetFileNameWithoutExtension(pth); string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); int k = 0; foreach (MultiMatSubmeshInfo sh in shader2Material_map.Keys) { foreach (List <Material> matsThatUse in shader2Material_map[sh]) { MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial(); mm.sourceMaterials = matsThatUse; if (mm.sourceMaterials.Count == 1) { mm.considerMeshUVs = false; } else { mm.considerMeshUVs = mom.fixOutOfBoundsUVs; } string matName = folderPath + baseName + "-mat" + k + ".mat"; Material newMat = new Material(Shader.Find("Diffuse")); if (matsThatUse.Count > 0 && matsThatUse[0] != null) { MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, matsThatUse[0]); } AssetDatabase.CreateAsset(newMat, matName); mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); k++; } } foreach (Material m in obUVobject2mesh_map.Keys) { MB_MultiMaterial mm = mom.resultMaterials[k] = new MB_MultiMaterial(); mm.sourceMaterials = new List <Material>(); mm.sourceMaterials.Add(m); mm.considerMeshUVs = false; string matName = folderPath + baseName + "-mat" + k + ".mat"; Material newMat = new Material(Shader.Find("Diffuse")); MB3_TextureBaker.ConfigureNewMaterialToMatchOld(newMat, m); AssetDatabase.CreateAsset(newMat, matName); mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); k++; } MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); }
private MB_AtlasesAndRects[] _CreateAtlases(ProgressUpdateDelegate progressInfo, bool saveAtlasesAsAssets = false, MB2_EditorMethodsInterface editorMethods = null) { 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 validationLevel = (!Application.isPlaying) ? MB2_ValidationLevel.robust : MB2_ValidationLevel.quick; if (!MB3_MeshBakerRoot.DoCombinedValidate(this, MB_ObjsToCombineTypes.dontCare, editorMethods, validationLevel)) { return(null); } if (this._doMultiMaterial && !this._ValidateResultMaterials()) { return(null); } if (!this._doMultiMaterial) { if (this._resultMaterial == null) { Debug.LogError("Combined Material is null please create and assign a result material."); return(null); } Shader shader = this._resultMaterial.shader; for (int i = 0; i < this.objsToMesh.Count; i++) { foreach (Material material in MB_Utility.GetGOMaterials(this.objsToMesh[i])) { if (material != null && material.shader != shader) { Debug.LogWarning(string.Concat(new object[] { "Game object ", this.objsToMesh[i], " does not use shader ", shader, " it may not have the required textures. If not 2x2 clear textures will be generated." })); } } } } for (int k = 0; k < this.objsToMesh.Count; k++) { foreach (Material x in MB_Utility.GetGOMaterials(this.objsToMesh[k])) { if (x == null) { Debug.LogError("Game object " + this.objsToMesh[k] + " has a null material. Can't build atlases"); return(null); } } } MB3_TextureCombiner mb3_TextureCombiner = new MB3_TextureCombiner(); mb3_TextureCombiner.LOG_LEVEL = this.LOG_LEVEL; mb3_TextureCombiner.atlasPadding = this._atlasPadding; mb3_TextureCombiner.maxAtlasSize = this._maxAtlasSize; mb3_TextureCombiner.customShaderPropNames = this._customShaderProperties; mb3_TextureCombiner.fixOutOfBoundsUVs = this._fixOutOfBoundsUVs; mb3_TextureCombiner.maxTilingBakeSize = this._maxTilingBakeSize; mb3_TextureCombiner.packingAlgorithm = this._packingAlgorithm; mb3_TextureCombiner.meshBakerTexturePackerForcePowerOfTwo = this._meshBakerTexturePackerForcePowerOfTwo; mb3_TextureCombiner.resizePowerOfTwoTextures = this._resizePowerOfTwoTextures; mb3_TextureCombiner.saveAtlasesAsAssets = saveAtlasesAsAssets; if (!Application.isPlaying) { Material[] array; if (this._doMultiMaterial) { array = new Material[this.resultMaterials.Length]; for (int m = 0; m < array.Length; m++) { array[m] = this.resultMaterials[m].combinedMaterial; } } else { array = new Material[] { this._resultMaterial }; } mb3_TextureCombiner.SuggestTreatment(this.objsToMesh, array, mb3_TextureCombiner.customShaderPropNames); } int num = 1; if (this._doMultiMaterial) { num = this.resultMaterials.Length; } MB_AtlasesAndRects[] array2 = new MB_AtlasesAndRects[num]; for (int n = 0; n < array2.Length; n++) { array2[n] = new MB_AtlasesAndRects(); } for (int num2 = 0; num2 < array2.Length; num2++) { List <Material> allowedMaterialsFilter = null; Material material2; if (this._doMultiMaterial) { allowedMaterialsFilter = this.resultMaterials[num2].sourceMaterials; material2 = this.resultMaterials[num2].combinedMaterial; } else { material2 = this._resultMaterial; } Debug.Log("Creating atlases for result material " + material2); if (!mb3_TextureCombiner.CombineTexturesIntoAtlases(progressInfo, array2[num2], material2, this.objsToMesh, allowedMaterialsFilter, editorMethods)) { return(null); } } this.textureBakeResults.combinedMaterialInfo = array2; this.textureBakeResults.doMultiMaterial = this._doMultiMaterial; this.textureBakeResults.resultMaterial = this._resultMaterial; this.textureBakeResults.resultMaterials = this.resultMaterials; this.textureBakeResults.fixOutOfBoundsUVs = mb3_TextureCombiner.fixOutOfBoundsUVs; this.unpackMat2RectMap(this.textureBakeResults); MB3_MeshBakerCommon[] componentsInChildren = base.GetComponentsInChildren <MB3_MeshBakerCommon>(); for (int num3 = 0; num3 < componentsInChildren.Length; num3++) { componentsInChildren[num3].textureBakeResults = this.textureBakeResults; } if (this.LOG_LEVEL >= MB2_LogLevel.info) { Debug.Log("Created Atlases"); } return(array2); }
public static void ConfigureTextureArraysFromObjsToCombine(MB3_TextureBaker mom, SerializedProperty resultMaterialsTexArrays, SerializedObject textureBaker) { if (mom.GetObjectsToCombine().Count == 0) { Debug.LogError("You need to add some objects to combine before building the texture array result materials."); return; } if (resultMaterialsTexArrays.arraySize > 0) { Debug.LogError("You already have some texture array result materials configured. You must remove these before doing this operation."); return; } if (mom.textureBakeResults == null) { Debug.LogError("Texture Bake Result asset must be set before using this operation."); return; } //validate that the objects to be combined are valid for (int i = 0; i < mom.GetObjectsToCombine().Count; i++) { GameObject go = mom.GetObjectsToCombine()[i]; if (go == null) { Debug.LogError("Null object in list of objects to combine at position " + i); return; } if (MB_Utility.GetMesh(go) == null) { Debug.LogError("Could not get mesh for object in list of objects to combine at position " + i); return; } Renderer r = go.GetComponent <Renderer>(); if (r == null || (!(r is MeshRenderer) && !(r is SkinnedMeshRenderer))) { Debug.LogError("GameObject at position " + i + " in list of objects to combine did not have a renderer"); return; } if (r.sharedMaterial == null) { Debug.LogError("GameObject at position " + i + " in list of objects to combine has a null material"); return; } } //Will sort into "result material" // slices Dictionary <MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List <Slice> > shader2ResultMat_map = new Dictionary <MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo, List <Slice> >(); // first pass split by shader and analyse meshes. List <GameObject> objsToCombine = mom.GetObjectsToCombine(); for (int meshIdx = 0; meshIdx < objsToCombine.Count; meshIdx++) { GameObject srcGo = objsToCombine[meshIdx]; Mesh mesh = MB_Utility.GetMesh(srcGo); Renderer r = MB_Utility.GetRenderer(srcGo); if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("1st Pass 'Split By Shader' Processing Mesh: " + mesh + " Num submeshes: " + r.sharedMaterials.Length); } for (int submeshIdx = 0; submeshIdx < r.sharedMaterials.Length; submeshIdx++) { if (r.sharedMaterials[submeshIdx] == null) { continue; } MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo newKey = new MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo(r.sharedMaterials[submeshIdx].shader, r.sharedMaterials[submeshIdx]); // Initially we fill the list of srcMaterials with garbage MB_MaterialAndUVRects. Will get proper ones when we atlas pack. MB_MaterialAndUVRect submeshMaterial = new MB_MaterialAndUVRect( r.sharedMaterials[submeshIdx], new Rect(0, 0, 0, 0), // garbage value false, //garbage value new Rect(0, 0, 0, 0), // garbage value new Rect(0, 0, 0, 0), // garbage value new Rect(0, 0, 1, 1), // garbage value MB_TextureTilingTreatment.unknown, // garbage value r.name); submeshMaterial.objectsThatUse = new List <GameObject>(); submeshMaterial.objectsThatUse.Add(r.gameObject); if (!shader2ResultMat_map.ContainsKey(newKey)) { // this is a new shader create a new result material Slice srcMaterials = new Slice { atlasRects = new List <MB_MaterialAndUVRect>(), numAtlasRects = 1, }; srcMaterials.atlasRects.Add(submeshMaterial); List <Slice> binsOfMatsThatUseShader = new List <Slice>(); binsOfMatsThatUseShader.Add(srcMaterials); if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(" Adding Source Material: " + submeshMaterial.material); } shader2ResultMat_map.Add(newKey, binsOfMatsThatUseShader); } else { // there is a result material that uses this shader. Add this source material Slice srcMaterials = shader2ResultMat_map[newKey][0]; // There should only be one list of source materials if (srcMaterials.atlasRects.Find(x => x.material == submeshMaterial.material) == null) { if (mom.LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(" Adding Source Material: " + submeshMaterial.material); } srcMaterials.atlasRects.Add(submeshMaterial); } } } } int resMatCount = 0; foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resultMat in shader2ResultMat_map.Keys) { // at this point there there should be only one slice with all the source materials resMatCount++; // For each result material, all source materials are in the first slice. // We will now split these using a texture packer. Each "atlas" generated by the packer will be a slice. { // All source materials should be in the first slice at this point. List <Slice> slices = shader2ResultMat_map[resultMat]; List <Slice> newSlices = new List <Slice>(); Slice firstSlice = slices[0]; List <Material> allMatsThatUserShader = new List <Material>(); List <GameObject> objsThatUseFirstSlice = new List <GameObject>(); for (int i = 0; i < firstSlice.atlasRects.Count; i++) { allMatsThatUserShader.Add(firstSlice.atlasRects[i].material); if (!objsThatUseFirstSlice.Contains(firstSlice.atlasRects[i].objectsThatUse[0])) { objsThatUseFirstSlice.Add(firstSlice.atlasRects[i].objectsThatUse[0]); } } MB3_TextureCombiner combiner = mom.CreateAndConfigureTextureCombiner(); combiner.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; combiner.saveAtlasesAsAssets = false; combiner.fixOutOfBoundsUVs = true; combiner.doMergeDistinctMaterialTexturesThatWouldExceedAtlasSize = true; List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempMat = new Material(resultMat.shader); if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("======== 2nd pass. Use atlas packer to split the first slice into multiple if it exceeds atlas size. "); } combiner.CombineTexturesIntoAtlases(null, null, tempMat, mom.GetObjectsToCombine(), allMatsThatUserShader, null, packingResults, onlyPackRects: true, splitAtlasWhenPackingIfTooBig: true); if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("======== Completed packing with texture packer. numPackingResults: " + packingResults.Count); } newSlices.Clear(); // The texture packing just split the atlas into multiple atlases. Each atlas will become a "slice". for (int newSliceIdx = 0; newSliceIdx < packingResults.Count; newSliceIdx++) { List <MB_MaterialAndUVRect> sourceMats = new List <MB_MaterialAndUVRect>(); List <MB_MaterialAndUVRect> packedMatRects = (List <MB_MaterialAndUVRect>)packingResults[newSliceIdx].data; HashSet <Rect> distinctAtlasRects = new HashSet <Rect>(); for (int packedMatRectIdx = 0; packedMatRectIdx < packedMatRects.Count; packedMatRectIdx++) { MB_MaterialAndUVRect muvr = packedMatRects[packedMatRectIdx]; distinctAtlasRects.Add(muvr.atlasRect); { Rect encapsulatingRect = muvr.GetEncapsulatingRect(); Vector2 sizeInAtlas_px = new Vector2( packingResults[newSliceIdx].atlasX * encapsulatingRect.width, packingResults[newSliceIdx].atlasY * encapsulatingRect.height); } sourceMats.Add(muvr); } Slice slice = new Slice() { atlasRects = sourceMats, packingResult = packingResults[newSliceIdx], numAtlasRects = distinctAtlasRects.Count, }; newSlices.Add(slice); } // Replace first slice with split version. if (mom.LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("First slice exceeded atlas size splitting it into " + newSlices.Count + " slices"); } slices.RemoveAt(0); for (int i = 0; i < newSlices.Count; i++) { slices.Insert(i, newSlices[i]); } } } // build the texture array result materials if (shader2ResultMat_map.Count == 0) { Debug.LogError("Found no materials in list of objects to combine"); } mom.resultMaterialsTexArray = new MB_MultiMaterialTexArray[shader2ResultMat_map.Count]; int k = 0; foreach (MB3_TextureBakerEditorInternal.MultiMatSubmeshInfo resMatKey in shader2ResultMat_map.Keys) { List <Slice> srcSlices = shader2ResultMat_map[resMatKey]; MB_MultiMaterialTexArray mm = mom.resultMaterialsTexArray[k] = new MB_MultiMaterialTexArray(); for (int sliceIdx = 0; sliceIdx < srcSlices.Count; sliceIdx++) { Slice slice = srcSlices[sliceIdx]; MB_TexArraySlice resSlice = new MB_TexArraySlice(); List <Material> usedMats = new List <Material>(); for (int srcMatIdx = 0; srcMatIdx < slice.atlasRects.Count; srcMatIdx++) { MB_MaterialAndUVRect matAndUVRect = slice.atlasRects[srcMatIdx]; List <GameObject> objsThatUse = matAndUVRect.objectsThatUse; for (int objsThatUseIdx = 0; objsThatUseIdx < objsThatUse.Count; objsThatUseIdx++) { GameObject obj = objsThatUse[objsThatUseIdx]; if (!resSlice.ContainsMaterialAndMesh(slice.atlasRects[srcMatIdx].material, MB_Utility.GetMesh(obj))) { resSlice.sourceMaterials.Add( new MB_TexArraySliceRendererMatPair() { renderer = obj, sourceMaterial = slice.atlasRects[srcMatIdx].material } ); } } } { // Should we use considerUVs bool doConsiderUVs = false; // If there is more than one atlas rectangle in a slice then use considerUVs if (slice.numAtlasRects > 1) { doConsiderUVs = true; } else { // There is only one source material, could be: // - lots of tiling (don't want consider UVs) // - We are extracting a small part of a large atlas (want considerUVs) if (slice.packingResult.atlasX >= mom.maxAtlasSize || slice.packingResult.atlasY >= mom.maxAtlasSize) { doConsiderUVs = false; // lots of tiling } else { doConsiderUVs = true; // extracting a small part of an atlas } } resSlice.considerMeshUVs = doConsiderUVs; } mm.slices.Add(resSlice); } // Enforce integrity. If a material appears in more than one slice then all those slices must be considerUVs=true { // collect all distinct materials HashSet <Material> distinctMats = new HashSet <Material>(); Dictionary <Material, int> mat2sliceCount = new Dictionary <Material, int>(); for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { for (int sliceMatIdx = 0; sliceMatIdx < mm.slices[sliceIdx].sourceMaterials.Count; sliceMatIdx++) { Material mat = mm.slices[sliceIdx].sourceMaterials[sliceMatIdx].sourceMaterial; distinctMats.Add(mat); mat2sliceCount[mat] = 0; } } // Count the number of slices that use each material. foreach (Material mat in distinctMats) { for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].ContainsMaterial(mat)) { mat2sliceCount[mat] = mat2sliceCount[mat] + 1; } } } // Check that considerUVs is true for any materials that appear more than once foreach (Material mat in distinctMats) { if (mat2sliceCount[mat] > 1) { for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].ContainsMaterial(mat)) { if (mom.LOG_LEVEL >= MB2_LogLevel.debug && mm.slices[sliceIdx].considerMeshUVs) { Debug.Log("There was a material " + mat + " that was used by more than one slice and considerUVs was false. sliceIdx:" + sliceIdx); } mm.slices[sliceIdx].considerMeshUVs = true; } } } } } // Cleanup. remove "Renderer"s from source materials that do not use considerUVs and delete extra { // put any slices with consider UVs first List <MB_TexArraySlice> newSlices = new List <MB_TexArraySlice>(); for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { if (mm.slices[sliceIdx].considerMeshUVs == true) { newSlices.Add(mm.slices[sliceIdx]); } } // for any slices without considerUVs, remove "renderer" and truncate for (int sliceIdx = 0; sliceIdx < mm.slices.Count; sliceIdx++) { MB_TexArraySlice slice = mm.slices[sliceIdx]; if (slice.considerMeshUVs == false) { newSlices.Add(slice); HashSet <Material> distinctMats = slice.GetDistinctMaterials(); slice.sourceMaterials.Clear(); foreach (Material mat in distinctMats) { slice.sourceMaterials.Add(new MB_TexArraySliceRendererMatPair() { sourceMaterial = mat }); } } } mm.slices = newSlices; } string pth = AssetDatabase.GetAssetPath(mom.textureBakeResults); string baseName = Path.GetFileNameWithoutExtension(pth); string folderPath = pth.Substring(0, pth.Length - baseName.Length - 6); string matName = folderPath + baseName + "-mat" + k + ".mat"; Material existingAsset = AssetDatabase.LoadAssetAtPath <Material>(matName); if (!existingAsset) { Material newMat = new Material(Shader.Find("Standard")); // Don't try to configure the material we need the user to pick a shader that has TextureArrays AssetDatabase.CreateAsset(newMat, matName); } mm.combinedMaterial = (Material)AssetDatabase.LoadAssetAtPath(matName, typeof(Material)); k++; } MBVersionEditor.UpdateIfDirtyOrScript(textureBaker); textureBaker.Update(); }
public 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 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(); } }
public static Dictionary <GameObjectFilterInfo, List <List <GameObjectFilterInfo> > > sortIntoBakeGroups3(List <GameObjectFilterInfo> gameObjects, List <GameObjectFilterInfo> objsNotAddedToBaker, IGroupByFilter[] filters, bool splitAtlasesSoMeshesFit, int atlasSize) { Dictionary <GameObjectFilterInfo, List <List <GameObjectFilterInfo> > > gs2bakeGroupMap = new Dictionary <GameObjectFilterInfo, List <List <GameObjectFilterInfo> > >(); List <GameObjectFilterInfo> gos = gameObjects; if (gos.Count < 1) { return(gs2bakeGroupMap); } gos.Sort(); List <List <GameObjectFilterInfo> > l = null; GameObjectFilterInfo key = gos[0]; for (int i = 0; i < gos.Count; i++) { GameObjectFilterInfo goaw = gos[i]; //compare with key and decide if we need a new list for (int j = 0; j < filters.Length; j++) { if (filters[j] != null && filters[j].Compare(key, goaw) != 0) { l = null; } } if (l == null) { l = new List <List <GameObjectFilterInfo> >(); l.Add(new List <GameObjectFilterInfo>()); gs2bakeGroupMap.Add(gos[i], l); key = gos[i]; } l[0].Add(gos[i]); } //now that objects have been grouped by the sort criteria we can see how many atlases are needed Dictionary <GameObjectFilterInfo, List <List <GameObjectFilterInfo> > > gs2bakeGroupMap2 = new Dictionary <GameObjectFilterInfo, List <List <GameObjectFilterInfo> > >(); if (splitAtlasesSoMeshesFit) { foreach (GameObjectFilterInfo k in gs2bakeGroupMap.Keys) { List <GameObjectFilterInfo> vs = gs2bakeGroupMap[k][0]; List <GameObject> objsInGroup = new List <GameObject>(); for (int i = 0; i < vs.Count; i++) { objsInGroup.Add(vs[i].go); } MB3_TextureCombiner tc = new MB3_TextureCombiner(); tc.maxAtlasSize = atlasSize; tc.packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; tc.LOG_LEVEL = MB2_LogLevel.warn; List <AtlasPackingResult> packingResults = new List <AtlasPackingResult>(); Material tempResMat = k.materials[0];//we don't write to the materials so can use this as the result material MB_AtlasesAndRects tempAtlasesAndRects = new MB_AtlasesAndRects(); if (tc.CombineTexturesIntoAtlases(null, tempAtlasesAndRects, tempResMat, objsInGroup, null, null, packingResults, onlyPackRects:true, splitAtlasWhenPackingIfTooBig:false)) { List <List <GameObjectFilterInfo> > atlasGroups = new List <List <GameObjectFilterInfo> >(); for (int i = 0; i < packingResults.Count; i++) { List <GameObjectFilterInfo> ngos = new List <GameObjectFilterInfo>(); List <MB_MaterialAndUVRect> matsData = (List <MB_MaterialAndUVRect>)packingResults[i].data; for (int j = 0; j < matsData.Count; j++) { for (int kk = 0; kk < matsData[j].objectsThatUse.Count; kk++) { GameObjectFilterInfo gofi = vs.Find(x => x.go == matsData[j].objectsThatUse[kk]); //Debug.Assert(gofi != null); ngos.Add(gofi); } } ngos[0].atlasIndex = (short)i; atlasGroups.Add(ngos); } gs2bakeGroupMap2.Add(k, atlasGroups); } else { gs2bakeGroupMap2.Add(k, gs2bakeGroupMap[k]); } } } else { gs2bakeGroupMap2 = gs2bakeGroupMap; } return(gs2bakeGroupMap2); }