public override AtlasPackingResult[] GetRects(List <Vector2> imgWidthHeights, int maxDimension, int padding, bool doMultiAtlas) { if (doMultiAtlas) { if (packingOrientation == TexturePackingOrientation.vertical) { return(_GetRectsMultiAtlasVertical(imgWidthHeights, maxDimension, padding, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2)); } else { return(_GetRectsMultiAtlasHorizontal(imgWidthHeights, maxDimension, padding, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2)); } } else { AtlasPackingResult apr = _GetRectsSingleAtlas(imgWidthHeights, maxDimension, padding, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 0); if (apr == null) { return(null); } else { return(new AtlasPackingResult[] { apr }); } } }
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; }
public override AtlasPackingResult[] GetRects(List <Vector2> imgWidthHeights, List <AtlasPadding> paddings, int maxDimensionX, int maxDimensionY, bool doMultiAtlas) { Debug.Assert(imgWidthHeights.Count == paddings.Count, imgWidthHeights.Count + " " + paddings.Count); int maxPaddingX = 0; int maxPaddingY = 0; for (int i = 0; i < paddings.Count; i++) { maxPaddingX = Mathf.Max(maxPaddingX, paddings[i].leftRight); maxPaddingY = Mathf.Max(maxPaddingY, paddings[i].topBottom); } if (doMultiAtlas) { return(_GetRectsMultiAtlas(imgWidthHeights, paddings, maxDimensionX, maxDimensionY, 2 + maxPaddingX * 2, 2 + maxPaddingY * 2, 2 + maxPaddingX * 2, 2 + maxPaddingY * 2)); } else { AtlasPackingResult apr = _GetRectsSingleAtlas(imgWidthHeights, paddings, maxDimensionX, maxDimensionY, 2 + maxPaddingX * 2, 2 + maxPaddingY * 2, 2 + maxPaddingX * 2, 2 + maxPaddingY * 2, 0); if (apr == null) { return(null); } else { return(new AtlasPackingResult[] { apr }); } } }
public AtlasPackingResult[] CalculateAtlasRectangles(MB3_TextureCombinerPipeline.TexturePipelineData data, bool doMultiAtlas, 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"); AtlasPackingResult[] packerRects = new AtlasPackingResult[1]; AtlasPadding[] paddings = new AtlasPadding[] { new AtlasPadding(data._atlasPadding) }; packerRects[0] = new AtlasPackingResult(paddings); packerRects[0].rects = new Rect[1]; packerRects[0].srcImgIdxs = new int[] { 0 }; packerRects[0].rects[0] = new Rect(0f, 0f, 1f, 1f); MeshBakerMaterialTexture dmt = null; if (data.distinctMaterialTextures[0].ts.Length > 0) { dmt = data.distinctMaterialTextures[0].ts[0]; } if (dmt == null || dmt.isNull) { packerRects[0].atlasX = 16; packerRects[0].atlasY = 16; packerRects[0].usedW = 16; packerRects[0].usedH = 16; } else { packerRects[0].atlasX = dmt.width; packerRects[0].atlasY = dmt.height; packerRects[0].usedW = dmt.width; packerRects[0].usedH = dmt.height; } return packerRects; }
//normalize atlases so that that rects are 0 to 1 public void ConvertToRectsWithoutPaddingAndNormalize01(AtlasPackingResult rr, AtlasPadding padding) { for (int i = 0; i < rr.rects.Length; i++) { rr.rects[i].x = (rr.rects[i].x + padding.leftRight) / rr.atlasX; rr.rects[i].y = (rr.rects[i].y + padding.topBottom) / rr.atlasY; rr.rects[i].width = (rr.rects[i].width - padding.leftRight * 2) / rr.atlasX; rr.rects[i].height = (rr.rects[i].height - padding.topBottom * 2) / rr.atlasY; } }
//normalize atlases so that that rects are 0 to 1 void normalizeRects(AtlasPackingResult rr, int padding) { for (int i = 0; i < rr.rects.Length; i++) { rr.rects[i].x = (rr.rects[i].x + padding) / rr.atlasX; rr.rects[i].y = (rr.rects[i].y + padding) / rr.atlasY; rr.rects[i].width = (rr.rects[i].width - padding * 2) / rr.atlasX; rr.rects[i].height = (rr.rects[i].height - padding * 2) / rr.atlasY; } }
public AtlasPackingResult CalculateAtlasRectangles(MB3_TextureCombinerPipeline.TexturePipelineData data, MB2_LogLevel LOG_LEVEL) { if (data._packingAlgorithm == MB2_PackingAlgorithmEnum.UnitysPackTextures) { //with Unity texture packer we don't find the rectangles, Unity does. When packer is run return(new AtlasPackingResult(new AtlasPadding[0])); } AtlasPackingResult uvRects; 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"); } AtlasPadding[] paddings = new AtlasPadding[] { new AtlasPadding(data._atlasPadding) }; uvRects = new AtlasPackingResult(paddings); uvRects.rects = new Rect[1]; uvRects.rects[0] = new Rect(0f, 0f, 1f, 1f); uvRects.atlasX = data.distinctMaterialTextures[0].idealWidth; uvRects.atlasY = data.distinctMaterialTextures[0].idealHeight; } else { List <Vector2> imageSizes = new List <Vector2>(); for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { imageSizes.Add(new Vector2(data.distinctMaterialTextures[i].idealWidth, data.distinctMaterialTextures[i].idealHeight)); } MB2_TexturePacker tp = MB3_TextureCombinerPipeline.CreateTexturePacker(data._packingAlgorithm); tp.atlasMustBePowerOfTwo = data._meshBakerTexturePackerForcePowerOfTwo; int atlasMaxDimension = data._maxAtlasSize; List <AtlasPadding> paddings = new List <AtlasPadding>(); for (int i = 0; i < imageSizes.Count; i++) { AtlasPadding padding = new AtlasPadding(); padding.topBottom = data._atlasPadding; padding.leftRight = data._atlasPadding; if (data._packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal) { padding.leftRight = 0; } if (data._packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical) { padding.topBottom = 0; } paddings.Add(padding); } AtlasPackingResult[] packerRects = tp.GetRects(imageSizes, paddings, atlasMaxDimension, atlasMaxDimension, false); uvRects = packerRects[0]; } return(uvRects); }
public AtlasPackingResult[] GetRects(List <Vector2> imgWidthHeights, int maxDimension, int padding, bool doMultiAtlas) { if (doMultiAtlas) { return(_GetRectsMultiAtlas(imgWidthHeights, maxDimension, padding, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2)); } else { AtlasPackingResult apr = _GetRectsSingleAtlas(imgWidthHeights, maxDimension, padding, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 2 + padding * 2, 0); if (apr == null) { return(null); } else { return(new AtlasPackingResult[] { apr }); } } }
public void MergeAtlasPackingResultStackBonAInternal(AtlasPackingResult a, AtlasPackingResult b, out Rect AatlasToFinal, out Rect BatlasToFinal, bool stretchBToAtlasWidth, int maxWidthDim, int maxHeightDim, out int atlasX, out int atlasY) { // first calc width scale and offset float finalW = a.usedW + b.usedW; float scaleXa, scaleXb; if (finalW > maxWidthDim) { scaleXa = maxWidthDim / finalW; //0,1 float offsetBx = ((float)Mathf.FloorToInt(a.usedW * scaleXa)) / maxWidthDim; //0,1 scaleXa = offsetBx; scaleXb = (1f - offsetBx); AatlasToFinal = new Rect(0, 0, scaleXa, 1); BatlasToFinal = new Rect(offsetBx, 0, scaleXb, 1); } else { float offsetBx = a.usedW / finalW; AatlasToFinal = new Rect(0, 0, offsetBx, 1); BatlasToFinal = new Rect(offsetBx, 0, b.usedW / finalW, 1); } //next calc width scale and offset if (a.atlasX > b.atlasX) { if (!stretchBToAtlasWidth) { // b rects will be placed in a larger atlas which will make them smaller BatlasToFinal.width = ((float)b.atlasX) / a.atlasX; } } else if (b.atlasX > a.atlasX) { // a rects will be placed in a larger atlas which will make them smaller AatlasToFinal.width = ((float)a.atlasX) / b.atlasX; } atlasX = a.usedW + b.usedW; atlasY = Mathf.Max(a.usedH, b.usedH); }
public void MergeAtlasPackingResultStackBonAInternal(AtlasPackingResult a, AtlasPackingResult b, out Rect AatlasToFinal, out Rect BatlasToFinal, bool stretchBToAtlasWidth, int maxWidthDim, int maxHeightDim, out int atlasX, out int atlasY) { float finalH = a.usedH + b.usedH; float scaleYa, scaleYb; if (finalH > maxHeightDim) { scaleYa = maxHeightDim / finalH; //0,1 float offsetBy = ((float)Mathf.FloorToInt(a.usedH * scaleYa)) / maxHeightDim; //0,1 scaleYa = offsetBy; scaleYb = (1f - offsetBy); AatlasToFinal = new Rect(0, 0, 1, scaleYa); BatlasToFinal = new Rect(0, offsetBy, 1, scaleYb); } else { float offsetBy = a.usedH / finalH; AatlasToFinal = new Rect(0, 0, 1, offsetBy); BatlasToFinal = new Rect(0, offsetBy, 1, b.usedH / finalH); } //next calc width scale and offset if (a.atlasX > b.atlasX) { if (!stretchBToAtlasWidth) { // b rects will be placed in a larger atlas which will make them smaller BatlasToFinal.width = ((float)b.atlasX) / a.atlasX; } } else if (b.atlasX > a.atlasX) { // a rects will be placed in a larger atlas which will make them smaller AatlasToFinal.width = ((float)a.atlasX) / b.atlasX; } atlasX = Mathf.Max(a.usedW, b.usedW); atlasY = a.usedH + b.usedH; }
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; }
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; }
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; }
public static AtlasPackingResult MergeAtlasPackingResultStackBonA(AtlasPackingResult a, AtlasPackingResult b, int maxHeightDim, int maxWidthDim, bool stretchBToAtlasWidth) { Debug.Assert(a.usedW == a.atlasX); Debug.Assert(a.usedH == a.atlasY); Debug.Assert(b.usedW == b.atlasX); Debug.Assert(b.usedH == b.atlasY); Debug.Assert(a.usedW <= maxWidthDim, a.usedW + " " + maxWidthDim); Debug.Assert(a.usedH <= maxHeightDim, a.usedH + " " + maxHeightDim); Debug.Assert(b.usedH <= maxHeightDim); Debug.Assert(b.usedW <= maxWidthDim); Rect AatlasToFinal; Rect BatlasToFinal; // first calc height scale and offset float finalH = a.usedH + b.usedH; float scaleYa, scaleYb; if (finalH > maxHeightDim) { scaleYa = maxHeightDim / finalH; //0,1 float offsetBy = ((float)Mathf.FloorToInt(a.usedH * scaleYa)) / maxHeightDim; //0,1 scaleYa = offsetBy; scaleYb = (1f - offsetBy); AatlasToFinal = new Rect(0, 0, 1, scaleYa); BatlasToFinal = new Rect(0, offsetBy, 1, scaleYb); } else { float offsetBy = a.usedH / finalH; AatlasToFinal = new Rect(0, 0, 1, offsetBy); BatlasToFinal = new Rect(0, offsetBy, 1, b.usedH / finalH); } //next calc width scale and offset if (a.atlasX > b.atlasX) { if (!stretchBToAtlasWidth) { // b rects will be placed in a larger atlas which will make them smaller BatlasToFinal.width = ((float)b.atlasX) / a.atlasX; } } else if (b.atlasX > a.atlasX) { // a rects will be placed in a larger atlas which will make them smaller AatlasToFinal.width = ((float)a.atlasX) / b.atlasX; } Rect[] newRects = new Rect[a.rects.Length + b.rects.Length]; AtlasPadding[] paddings = new AtlasPadding[a.rects.Length + b.rects.Length]; int[] srcImgIdxs = new int[a.rects.Length + b.rects.Length]; Array.Copy(a.padding, paddings, a.padding.Length); Array.Copy(b.padding, 0, paddings, a.padding.Length, b.padding.Length); Array.Copy(a.srcImgIdxs, srcImgIdxs, a.srcImgIdxs.Length); Array.Copy(b.srcImgIdxs, 0, srcImgIdxs, a.srcImgIdxs.Length, b.srcImgIdxs.Length); Array.Copy(a.rects, newRects, a.rects.Length); int atlasX = Mathf.Max(a.usedW, b.usedW); int atlasY = a.usedH + b.usedH; float maxx = 0f; float maxy = 0f; for (int i = 0; i < a.rects.Length; i++) { Rect r = a.rects[i]; r.x = AatlasToFinal.x + r.x * AatlasToFinal.width; r.y = AatlasToFinal.y + r.y * AatlasToFinal.height; r.width *= AatlasToFinal.width; r.height *= AatlasToFinal.height; maxx = Mathf.Max(maxx, r.xMax); maxy = Mathf.Max(maxy, r.yMax); newRects[i] = r; srcImgIdxs[i] = a.srcImgIdxs[i]; } for (int i = 0; i < b.rects.Length; i++) { Rect r = b.rects[i]; r.x = BatlasToFinal.x + r.x * BatlasToFinal.width; r.y = BatlasToFinal.y + r.y * BatlasToFinal.height; r.width *= BatlasToFinal.width; r.height *= BatlasToFinal.height; maxx = Mathf.Max(maxx, r.xMax); maxy = Mathf.Max(maxy, r.yMax); newRects[a.rects.Length + i] = r; srcImgIdxs[a.rects.Length + i] = b.srcImgIdxs[i]; } AtlasPackingResult res = new AtlasPackingResult(paddings); res.atlasX = atlasX; res.atlasY = atlasY; res.padding = paddings; res.rects = newRects; res.srcImgIdxs = srcImgIdxs; res.CalcUsedWidthAndHeight(); return(res); }
public override AtlasPackingResult[] CalculateAtlasRectangles(MB3_TextureCombinerPipeline.TexturePipelineData data, bool doMultiAtlas, MB2_LogLevel LOG_LEVEL) { Debug.Assert(!data.OnlyOneTextureInAtlasReuseTextures()); //TODO parameter int maxAtlasWidth = 512; Debug.Assert(data._packingAlgorithm != MB2_PackingAlgorithmEnum.UnitysPackTextures, "Unity texture packer cannot be used"); //split the list of distinctMaterialTextures into two bins List <MB_TexSet> horizontalVerticalDistinctMaterialTextures = new List <MB_TexSet>(); List <MB_TexSet> regularTextures = new List <MB_TexSet>(); for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { MB_TexSet texSet = data.distinctMaterialTextures[i]; if (texSet.idealWidth >= maxAtlasWidth && texSet.ts[0].GetEncapsulatingSamplingRect().width > 1f) { horizontalVerticalDistinctMaterialTextures.Add(texSet); } else { regularTextures.Add(texSet); } } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("Splitting list of distinctMaterialTextures numHorizontalVertical={0} numRegular={1}", horizontalVerticalDistinctMaterialTextures.Count, regularTextures.Count)); } //pack one with the horizontal texture packer MB2_TexturePacker tp; MB2_PackingAlgorithmEnum packingAlgorithm; int atlasMaxDimension = data._maxAtlasSize; AtlasPackingResult[] packerRectsHorizontalVertical; if (horizontalVerticalDistinctMaterialTextures.Count > 0) { packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal; List <Vector2> imageSizesHorizontalVertical = new List <Vector2>(); for (int i = 0; i < horizontalVerticalDistinctMaterialTextures.Count; i++) { horizontalVerticalDistinctMaterialTextures[i].SetTilingTreatmentAndAdjustEncapsulatingSamplingRect(MB_TextureTilingTreatment.edgeToEdgeX); imageSizesHorizontalVertical.Add(new Vector2(horizontalVerticalDistinctMaterialTextures[i].idealWidth, horizontalVerticalDistinctMaterialTextures[i].idealHeight)); } tp = MB3_TextureCombinerPipeline.CreateTexturePacker(packingAlgorithm); tp.atlasMustBePowerOfTwo = false; List <AtlasPadding> paddingsHorizontalVertical = new List <AtlasPadding>(); for (int i = 0; i < imageSizesHorizontalVertical.Count; i++) { AtlasPadding padding = new AtlasPadding(); padding.topBottom = data._atlasPadding; padding.leftRight = data._atlasPadding; if (packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Horizontal) { padding.leftRight = 0; } if (packingAlgorithm == MB2_PackingAlgorithmEnum.MeshBakerTexturePacker_Vertical) { padding.topBottom = 0; } paddingsHorizontalVertical.Add(padding); } tp.LOG_LEVEL = MB2_LogLevel.trace; packerRectsHorizontalVertical = tp.GetRects(imageSizesHorizontalVertical, paddingsHorizontalVertical, maxAtlasWidth, atlasMaxDimension, false); } else { packerRectsHorizontalVertical = new AtlasPackingResult[0]; } AtlasPackingResult[] packerRectsRegular; if (regularTextures.Count > 0) { //pack other with regular texture packer packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; List <Vector2> imageSizesRegular = new List <Vector2>(); for (int i = 0; i < regularTextures.Count; i++) { imageSizesRegular.Add(new Vector2(regularTextures[i].idealWidth, regularTextures[i].idealHeight)); } tp = MB3_TextureCombinerPipeline.CreateTexturePacker(MB2_PackingAlgorithmEnum.MeshBakerTexturePacker); tp.atlasMustBePowerOfTwo = false; List <AtlasPadding> paddingsRegular = new List <AtlasPadding>(); for (int i = 0; i < imageSizesRegular.Count; i++) { AtlasPadding padding = new AtlasPadding(); padding.topBottom = data._atlasPadding; padding.leftRight = data._atlasPadding; paddingsRegular.Add(padding); } tp.LOG_LEVEL = MB2_LogLevel.trace; packerRectsRegular = tp.GetRects(imageSizesRegular, paddingsRegular, maxAtlasWidth, atlasMaxDimension, false); } else { packerRectsRegular = new AtlasPackingResult[0]; } AtlasPackingResult result = null; if (packerRectsHorizontalVertical.Length == 0 && packerRectsRegular.Length == 0) { Debug.Assert(false, "Should never have reached this."); return(null); } else if (packerRectsHorizontalVertical.Length > 0 && packerRectsRegular.Length > 0) { result = MergeAtlasPackingResultStackBonA(packerRectsHorizontalVertical[0], packerRectsRegular[0], atlasMaxDimension, maxAtlasWidth, true); } else if (packerRectsHorizontalVertical.Length > 0) { Debug.LogError("TODO handle this"); result = packerRectsHorizontalVertical[0]; } else if (packerRectsRegular.Length > 0) { Debug.LogError("TODO handle this."); result = packerRectsRegular[0]; } Debug.Assert(data.distinctMaterialTextures.Count == result.rects.Length); //We re-ordered the distinctMaterial textures so replace the list with the new reordered one horizontalVerticalDistinctMaterialTextures.AddRange(regularTextures); data.distinctMaterialTextures = horizontalVerticalDistinctMaterialTextures; AtlasPackingResult[] results; if (result != null) { results = new AtlasPackingResult[] { result } } ; else { results = new AtlasPackingResult[0]; } return(results); }
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); } //create a game object GameObject renderAtlasesGO = null; try { renderAtlasesGO = new GameObject("MBrenderAtlasesGO"); MB3_AtlasPackerRenderTexture atlasRenderTexture = renderAtlasesGO.AddComponent <MB3_AtlasPackerRenderTexture>(); renderAtlasesGO.AddComponent <Camera>(); if (data._considerNonTextureProperties) { if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("Blend Non-Texture Properties has limited functionality when used with Mesh Baker Texture Packer Fast."); } } for (int i = 0; i < data.numAtlases; i++) { Texture2D atlas = null; if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(i, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Not creating atlas for " + data.texPropertyNames[i].name + " because textures are null and default value parameters are the same."); } } else { GC.Collect(); if (progressInfo != null) { progressInfo("Creating Atlas '" + data.texPropertyNames[i].name + "'", .01f); } // =========== // configure it if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("About to render " + data.texPropertyNames[i].name + " isNormal=" + data.texPropertyNames[i].isNormalMap); } atlasRenderTexture.LOG_LEVEL = LOG_LEVEL; atlasRenderTexture.width = atlasSizeX; atlasRenderTexture.height = atlasSizeY; atlasRenderTexture.padding = data._atlasPadding; atlasRenderTexture.rects = uvRects; atlasRenderTexture.textureSets = data.distinctMaterialTextures; atlasRenderTexture.indexOfTexSetToRender = i; atlasRenderTexture.texPropertyName = data.texPropertyNames[i]; atlasRenderTexture.isNormalMap = data.texPropertyNames[i].isNormalMap; atlasRenderTexture.fixOutOfBoundsUVs = data._fixOutOfBoundsUVs; atlasRenderTexture.considerNonTextureProperties = data._considerNonTextureProperties; atlasRenderTexture.resultMaterialTextureBlender = data.nonTexturePropertyBlender; // call render on it atlas = atlasRenderTexture.OnRenderAtlas(combiner); // destroy it // ============= if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Saving atlas " + data.texPropertyNames[i].name + " w=" + atlas.width + " h=" + atlas.height + " id=" + atlas.GetInstanceID()); } } atlases[i] = atlas; if (progressInfo != null) { progressInfo("Saving atlas: '" + data.texPropertyNames[i].name + "'", .04f); } if (data._saveAtlasesAsAssets && textureEditorMethods != null) { textureEditorMethods.SaveAtlasToAssetDatabase(atlases[i], data.texPropertyNames[i], i, data.resultMaterial); } else { data.resultMaterial.SetTexture(data.texPropertyNames[i].name, atlases[i]); } 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 } } catch (Exception ex) { //Debug.LogError(ex); Debug.LogException(ex); } finally { if (renderAtlasesGO != null) { MB_Utility.Destroy(renderAtlasesGO); } } } yield break; }
public abstract IEnumerator CreateAtlases(ProgressUpdateDelegate progressInfo, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, AtlasPackingResult packedAtlasRects, Texture2D[] atlases, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL);
private static AtlasPackingResult MergeAtlasPackingResultStackBonA(AtlasPackingResult a, AtlasPackingResult b, int maxWidthDim, int maxHeightDim, bool stretchBToAtlasWidth, IPipeline pipeline) { Debug.Assert(a.usedW == a.atlasX); Debug.Assert(a.usedH == a.atlasY); Debug.Assert(b.usedW == b.atlasX); Debug.Assert(b.usedH == b.atlasY); Debug.Assert(a.usedW <= maxWidthDim, a.usedW + " " + maxWidthDim); Debug.Assert(a.usedH <= maxHeightDim, a.usedH + " " + maxHeightDim); Debug.Assert(b.usedH <= maxHeightDim); Debug.Assert(b.usedW <= maxWidthDim, b.usedW + " " + maxWidthDim); Rect AatlasToFinal; Rect BatlasToFinal; // first calc height scale and offset int atlasX; int atlasY; pipeline.MergeAtlasPackingResultStackBonAInternal(a, b, out AatlasToFinal, out BatlasToFinal, stretchBToAtlasWidth, maxWidthDim, maxHeightDim, out atlasX, out atlasY); Rect[] newRects = new Rect[a.rects.Length + b.rects.Length]; AtlasPadding[] paddings = new AtlasPadding[a.rects.Length + b.rects.Length]; int[] srcImgIdxs = new int[a.rects.Length + b.rects.Length]; Array.Copy(a.padding, paddings, a.padding.Length); Array.Copy(b.padding, 0, paddings, a.padding.Length, b.padding.Length); Array.Copy(a.srcImgIdxs, srcImgIdxs, a.srcImgIdxs.Length); Array.Copy(b.srcImgIdxs, 0, srcImgIdxs, a.srcImgIdxs.Length, b.srcImgIdxs.Length); Array.Copy(a.rects, newRects, a.rects.Length); for (int i = 0; i < a.rects.Length; i++) { Rect r = a.rects[i]; r.x = AatlasToFinal.x + r.x * AatlasToFinal.width; r.y = AatlasToFinal.y + r.y * AatlasToFinal.height; r.width *= AatlasToFinal.width; r.height *= AatlasToFinal.height; Debug.Assert(r.max.x <= 1f); Debug.Assert(r.max.y <= 1f); Debug.Assert(r.min.x >= 0f); Debug.Assert(r.min.y >= 0f); newRects[i] = r; srcImgIdxs[i] = a.srcImgIdxs[i]; } for (int i = 0; i < b.rects.Length; i++) { Rect r = b.rects[i]; r.x = BatlasToFinal.x + r.x * BatlasToFinal.width; r.y = BatlasToFinal.y + r.y * BatlasToFinal.height; r.width *= BatlasToFinal.width; r.height *= BatlasToFinal.height; Debug.Assert(r.max.x <= 1f); Debug.Assert(r.max.y <= 1f); Debug.Assert(r.min.x >= 0f); Debug.Assert(r.min.y >= 0f); newRects[a.rects.Length + i] = r; srcImgIdxs[a.rects.Length + i] = b.srcImgIdxs[i]; } AtlasPackingResult res = new AtlasPackingResult(paddings); res.atlasX = atlasX; res.atlasY = atlasY; res.padding = paddings; res.rects = newRects; res.srcImgIdxs = srcImgIdxs; res.CalcUsedWidthAndHeight(); return(res); }
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()); Rect[] uvRects = packedAtlasRects.rects; int atlasSizeX = packedAtlasRects.atlasX; int atlasSizeY = packedAtlasRects.atlasY; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY); } int layer = data._layerTexturePackerFastV2; Debug.Assert(layer >= 0 && layer <= 32); //create a game object mesh = new Mesh(); renderAtlasesGO = null; cameraGameObject = null; try { System.Diagnostics.Stopwatch db_time_MB3_TextureCombinerPackerMeshBakerFastV2_CreateAtlases = new System.Diagnostics.Stopwatch(); db_time_MB3_TextureCombinerPackerMeshBakerFastV2_CreateAtlases.Start(); renderAtlasesGO = new GameObject("MBrenderAtlasesGO"); cameraGameObject = new GameObject("MBCameraGameObject"); MB3_AtlasPackerRenderTextureUsingMesh atlasRenderer = new MB3_AtlasPackerRenderTextureUsingMesh(); OneTimeSetup(atlasRenderer, renderAtlasesGO, cameraGameObject, atlasSizeX, atlasSizeY, data._atlasPadding, layer, LOG_LEVEL); if (data._considerNonTextureProperties && LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("Blend Non-Texture Properties has limited functionality when used with Mesh Baker Texture Packer Fast."); } List <Material> mats = new List <Material>(); 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 (progressInfo != null) { progressInfo("Creating Atlas '" + data.texPropertyNames[propIdx].name + "'", .01f); } // configure it if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("About to render " + data.texPropertyNames[propIdx].name + " isNormal=" + data.texPropertyNames[propIdx].isNormalMap); } // Create the mesh mats.Clear(); MB3_AtlasPackerRenderTextureUsingMesh.MeshAtlas.BuildAtlas(packedAtlasRects, data.distinctMaterialTextures, propIdx, packedAtlasRects.atlasX, packedAtlasRects.atlasY, mesh, mats, data.texPropertyNames[propIdx], data, combiner, textureEditorMethods, LOG_LEVEL); { MeshFilter mf = renderAtlasesGO.GetComponent <MeshFilter>(); mf.sharedMesh = mesh; MeshRenderer mrr = renderAtlasesGO.GetComponent <MeshRenderer>(); Material[] mrs = mats.ToArray(); mrr.sharedMaterials = mrs; } // Render atlas = atlasRenderer.DoRenderAtlas(cameraGameObject, packedAtlasRects.atlasX, packedAtlasRects.atlasY, data.texPropertyNames[propIdx].isNormalMap, data.texPropertyNames[propIdx]); { for (int i = 0; i < mats.Count; i++) { MB_Utility.Destroy(mats[i]); } mats.Clear(); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Saving atlas " + data.texPropertyNames[propIdx].name + " w=" + atlas.width + " h=" + atlas.height + " id=" + atlas.GetInstanceID()); } } atlases[propIdx] = atlas; if (progressInfo != null) { progressInfo("Saving atlas: '" + data.texPropertyNames[propIdx].name + "'", .04f); } if (data.resultType == MB2_TextureBakeResults.ResultType.atlas) { MB3_TextureCombinerPackerRoot.SaveAtlasAndConfigureResultMaterial(data, textureEditorMethods, atlases[propIdx], data.texPropertyNames[propIdx], propIdx); } combiner._destroyTemporaryTextures(data.texPropertyNames[propIdx].name); // need to save atlases before doing this } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.LogFormat("Timing MB3_TextureCombinerPackerMeshBakerFastV2.CreateAtlases={0}", db_time_MB3_TextureCombinerPackerMeshBakerFastV2_CreateAtlases.ElapsedMilliseconds * .001f); } } catch (Exception ex) { Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); } finally { if (renderAtlasesGO != null) { MB_Utility.Destroy(renderAtlasesGO); } if (cameraGameObject != null) { MB_Utility.Destroy(cameraGameObject); } if (mesh != null) { MB_Utility.Destroy(mesh); } } yield break; }
//----------------- Algorithm for fitting everything into multiple Atlases // // for images being added calc area, maxW, maxH. A perfectly packed atlas will match area exactly. atlas must be at least maxH and maxW in size. // Sort images from big to small using either height, width or area comparer // // If an image is bigger than maxDim, then shrink it to max size on the largest dimension // distribute images using the new algorithm, should never have to expand the atlas instead create new atlases as needed // should not need to scale atlases // AtlasPackingResult[] _GetRectsMultiAtlas(List <Vector2> imgWidthHeights, List <AtlasPadding> paddings, int maxDimensionPassedX, int maxDimensionPassedY, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("_GetRects numImages={0}, maxDimensionX={1}, maxDimensionY={2} minImageSizeX={3}, minImageSizeY={4}, masterImageSizeX={5}, masterImageSizeY={6}", imgWidthHeights.Count, maxDimensionPassedX, maxDimensionPassedY, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY)); } float area = 0; int maxW = 0; int maxH = 0; Image[] imgsToAdd = new Image[imgWidthHeights.Count]; int maxDimensionX = maxDimensionPassedX; int maxDimensionY = maxDimensionPassedY; if (atlasMustBePowerOfTwo) { maxDimensionX = RoundToNearestPositivePowerOfTwo(maxDimensionX); maxDimensionY = RoundToNearestPositivePowerOfTwo(maxDimensionY); } for (int i = 0; i < imgsToAdd.Length; i++) { int iw = (int)imgWidthHeights[i].x; int ih = (int)imgWidthHeights[i].y; //shrink the image so that it fits in maxDimenion if it is larger than maxDimension if atlas exceeds maxDim x maxDim then new alas will be created iw = Mathf.Min(iw, maxDimensionX - paddings[i].leftRight * 2); ih = Mathf.Min(ih, maxDimensionY - paddings[i].topBottom * 2); Image im = imgsToAdd[i] = new Image(i, iw, ih, paddings[i], minImageSizeX, minImageSizeY); area += im.w * im.h; maxW = Mathf.Max(maxW, im.w); maxH = Mathf.Max(maxH, im.h); } //explore the space to find a resonably efficient packing //int sqrtArea = (int)Mathf.Sqrt(area); int idealAtlasW; int idealAtlasH; if (atlasMustBePowerOfTwo) { idealAtlasH = RoundToNearestPositivePowerOfTwo(maxDimensionY); idealAtlasW = RoundToNearestPositivePowerOfTwo(maxDimensionX); } else { idealAtlasH = maxDimensionY; idealAtlasW = maxDimensionX; } if (idealAtlasW == 0) { idealAtlasW = 4; } if (idealAtlasH == 0) { idealAtlasH = 4; } ProbeResult pr = new ProbeResult(); Array.Sort(imgsToAdd, new ImageHeightComparer()); if (ProbeMultiAtlas(imgsToAdd, idealAtlasW, idealAtlasH, area, maxDimensionX, maxDimensionY, pr)) { bestRoot = pr; } Array.Sort(imgsToAdd, new ImageWidthComparer()); if (ProbeMultiAtlas(imgsToAdd, idealAtlasW, idealAtlasH, area, maxDimensionX, maxDimensionY, pr)) { if (pr.totalAtlasArea < bestRoot.totalAtlasArea) { bestRoot = pr; } } Array.Sort(imgsToAdd, new ImageAreaComparer()); if (ProbeMultiAtlas(imgsToAdd, idealAtlasW, idealAtlasH, area, maxDimensionX, maxDimensionY, pr)) { if (pr.totalAtlasArea < bestRoot.totalAtlasArea) { bestRoot = pr; } } if (bestRoot == null) { return(null); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Best fit found: w=" + bestRoot.w + " h=" + bestRoot.h + " efficiency=" + bestRoot.efficiency + " squareness=" + bestRoot.squareness + " fits in max dimension=" + bestRoot.largerOrEqualToMaxDim); } //the atlas can be larger than the max dimension scale it if this is the case //int newMinSizeX = minImageSizeX; //int newMinSizeY = minImageSizeY; List <AtlasPackingResult> rs = new List <AtlasPackingResult>(); // find all Nodes that are an individual atlas List <Node> atlasNodes = new List <Node>(); Stack <Node> stack = new Stack <Node>(); Node node = bestRoot.root; while (node != null) { stack.Push(node); node = node.child[0]; } // traverse the tree collecting atlasNodes while (stack.Count > 0) { node = stack.Pop(); if (node.isFullAtlas == NodeType.maxDim) { atlasNodes.Add(node); } if (node.child[1] != null) { node = node.child[1]; while (node != null) { stack.Push(node); node = node.child[0]; } } } //pack atlases so they all fit for (int i = 0; i < atlasNodes.Count; i++) { List <Image> images = new List <Image>(); flattenTree(atlasNodes[i], images); Rect[] rss = new Rect[images.Count]; int[] srcImgIdx = new int[images.Count]; for (int j = 0; j < images.Count; j++) { rss[j] = (new Rect(images[j].x - atlasNodes[i].r.x, images[j].y, images[j].w, images[j].h)); srcImgIdx[j] = images[j].imgId; } AtlasPackingResult res = new AtlasPackingResult(paddings.ToArray()); GetExtent(atlasNodes[i], ref res.usedW, ref res.usedH); res.usedW -= atlasNodes[i].r.x; int outW = atlasNodes[i].r.w; int outH = atlasNodes[i].r.h; if (atlasMustBePowerOfTwo) { outW = Mathf.Min(CeilToNearestPowerOfTwo(res.usedW), atlasNodes[i].r.w); outH = Mathf.Min(CeilToNearestPowerOfTwo(res.usedH), atlasNodes[i].r.h); if (outH < outW / 2) { outH = outW / 2; //smaller dim can't be less than half larger } if (outW < outH / 2) { outW = outH / 2; } } else { outW = res.usedW; outH = res.usedH; } res.atlasY = outH; res.atlasX = outW; res.rects = rss; res.srcImgIdxs = srcImgIdx; res.CalcUsedWidthAndHeight(); rs.Add(res); ConvertToRectsWithoutPaddingAndNormalize01(res, paddings[i]); if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug(String.Format("Done GetRects ")); } } return(rs.ToArray()); }
internal static void BuildAtlas( AtlasPackingResult packedAtlasRects, List <MB_TexSet> distinctMaterialTextures, int propIdx, int atlasSizeX, int atlasSizeY, Mesh m, List <Material> generatedMats, ShaderTextureProperty property, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, MB2_EditorMethodsInterface textureEditorMethods, MB2_LogLevel LOG_LEVEL) { // Collect vertices and quads for mesh that we will use for the atlas. Debug.Assert(generatedMats.Count == 0, "Previous mats should have been destroyed"); generatedMats.Clear(); List <Vector3> vs = new List <Vector3>(); List <Vector2> uvs = new List <Vector2>(); // One submesh and material per texture that we are packing List <int>[] ts = new List <int> [distinctMaterialTextures.Count]; for (int i = 0; i < ts.Length; i++) { ts[i] = new List <int>(); } MeshBakerMaterialTexture.readyToBuildAtlases = true; GC.Collect(); MB3_TextureCombinerPackerRoot.CreateTemporaryTexturesForAtlas(data.distinctMaterialTextures, combiner, propIdx, data); Rect[] uvRects = packedAtlasRects.rects; for (int texSetIdx = 0; texSetIdx < distinctMaterialTextures.Count; texSetIdx++) { MB_TexSet texSet = distinctMaterialTextures[texSetIdx]; MeshBakerMaterialTexture matTex = texSet.ts[propIdx]; if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(string.Format("Adding texture {0} to atlas {1} for texSet {2} srcMat {3}", matTex.GetTexName(), property.name, texSetIdx, texSet.matsAndGOs.mats[0].GetMaterialName())); } Rect r = uvRects[texSetIdx]; Texture2D t = matTex.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); r = new Rect(x, y, ww, hh); if (ww == 0 || hh == 0) { Debug.LogError("Image in atlas has no height or width " + r); } DRect samplingRect = texSet.ts[propIdx].GetEncapsulatingSamplingRect(); Debug.Assert(!texSet.ts[propIdx].isNull, string.Format("Adding texture {0} to atlas {1} for texSet {2} srcMat {3}", matTex.GetTexName(), property.name, texSetIdx, texSet.matsAndGOs.mats[0].GetMaterialName())); AtlasPadding padding = packedAtlasRects.padding[texSetIdx]; AddNineSlicedRect(r, padding.leftRight, padding.topBottom, samplingRect.GetRect(), vs, uvs, ts[texSetIdx], t.width, t.height, t.name); Material mt = new Material(Shader.Find("MeshBaker/Unlit/UnlitWithAlpha")); bool isSavingAsANormalMapAssetThatWillBeImported = property.isNormalMap && data._saveAtlasesAsAssets; MBVersion.PipelineType pipelineType = MBVersion.DetectPipeline(); if (pipelineType == MBVersion.PipelineType.URP) { ConfigureMaterial_DefaultPipeline(mt, t, isSavingAsANormalMapAssetThatWillBeImported, LOG_LEVEL); //ConfigureMaterial_URP(mt, t, isSavingAsANormalMapAssetThatWillBeImported, LOG_LEVEL); } else if (pipelineType == MBVersion.PipelineType.HDRP) { ConfigureMaterial_DefaultPipeline(mt, t, isSavingAsANormalMapAssetThatWillBeImported, LOG_LEVEL); } else { ConfigureMaterial_DefaultPipeline(mt, t, isSavingAsANormalMapAssetThatWillBeImported, LOG_LEVEL); } generatedMats.Add(mt); } // Apply to the mesh m.Clear(); m.vertices = vs.ToArray(); m.uv = uvs.ToArray(); m.subMeshCount = ts.Length; for (int i = 0; i < m.subMeshCount; i++) { m.SetIndices(ts[i].ToArray(), MeshTopology.Triangles, i); } MeshBakerMaterialTexture.readyToBuildAtlases = false; }
AtlasPackingResult[] _GetRectsMultiAtlasHorizontal(List <Vector2> imgWidthHeights, int maxDimensionPassed, int padding, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY) { List <AtlasPackingResult> rs = new List <AtlasPackingResult>(); int extent = 0; int maxh = 0; int maxw = 0; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Packing rects for: " + imgWidthHeights.Count); } List <Image> allImages = new List <Image>(); for (int i = 0; i < imgWidthHeights.Count; i++) { Image im = new Image(i, (int)imgWidthHeights[i].x, (int)imgWidthHeights[i].y, padding, minImageSizeX, minImageSizeY); im.w -= padding * 2; allImages.Add(im); } allImages.Sort(new ImageHeightComparer()); List <Image> images = new List <Image>(); List <Rect> rects = new List <Rect>(); int spaceRemaining = maxDimensionPassed; while (allImages.Count > 0 || images.Count > 0) { Image im = PopLargestThatFits(allImages, spaceRemaining, maxDimensionPassed, images.Count == 0); if (im == null) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Atlas filled creating a new atlas "); } AtlasPackingResult apr = new AtlasPackingResult(); apr.atlasX = maxw; apr.atlasY = maxh; Rect[] rss = new Rect[images.Count]; int[] srcImgIdx = new int[images.Count]; for (int j = 0; j < images.Count; j++) { Rect r = new Rect(images[j].x, images[j].y, stretchImagesToEdges ? maxw : images[j].w, images[j].h); rss[j] = r; srcImgIdx[j] = images[j].imgId; } apr.rects = rss; apr.srcImgIdxs = srcImgIdx; images.Clear(); rects.Clear(); extent = 0; maxh = 0; rs.Add(apr); spaceRemaining = maxDimensionPassed; } else { im.x = 0; im.y = extent; images.Add(im); rects.Add(new Rect(0, extent, im.w, im.h)); extent += im.h; maxw = Mathf.Max(maxw, im.w); maxh = extent; spaceRemaining = maxDimensionPassed - extent; } } for (int i = 0; i < rs.Count; i++) { int outH = rs[i].atlasY; int outW = Mathf.Min(rs[i].atlasX, maxDimensionPassed); if (atlasMustBePowerOfTwo) { outH = Mathf.Min(CeilToNearestPowerOfTwo(outH), maxDimensionPassed); } else { outH = Mathf.Min(outH, maxDimensionPassed); } rs[i].atlasY = outH; //------------------------------- //scale atlas to fit maxDimension float padX, padY; int newMinSizeX, newMinSizeY; ScaleAtlasToFitMaxDim(new Vector2(rs[i].atlasX, rs[i].atlasY), images, maxDimensionPassed, padding, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, ref outW, ref outH, out padX, out padY, out newMinSizeX, out newMinSizeY); } //normalize atlases so that that rects are 0 to 1 for (int i = 0; i < rs.Count; i++) { normalizeRects(rs[i], padding, 0); } //----------------------------- return(rs.ToArray()); }
public override 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; 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; ShaderTextureProperty property = data.texPropertyNames[propIdx]; if (!MB3_TextureCombinerPipeline._ShouldWeCreateAtlasForThisProperty(propIdx, data._considerNonTextureProperties, data.allTexturesAreNullAndSameColor)) { atlas = null; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("=== Not creating atlas for " + property.name + " because textures are null and default value parameters are the same."); } } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("=== Creating atlas for " + property.name); } GC.Collect(); CreateTemporaryTexturesForAtlas(data.distinctMaterialTextures, combiner, propIdx, data); //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 (property.isNormalMap) { isNormalMap = true; } for (int texSetIdx = 0; texSetIdx < data.distinctMaterialTextures.Count; texSetIdx++) { MB_TexSet texSet = data.distinctMaterialTextures[texSetIdx]; MeshBakerMaterialTexture matTex = texSet.ts[propIdx]; string s = "Creating Atlas '" + property.name + "' texture " + matTex.GetTexName(); if (progressInfo != null) { progressInfo(s, .01f); } if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(string.Format("Adding texture {0} to atlas {1} for texSet {2} srcMat {3}", matTex.GetTexName(), property.name, texSetIdx, texSet.matsAndGOs.mats[0].GetMaterialName())); } 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: '" + matTex.GetTexName() + "'", .02f); } DRect samplingRect = texSet.ts[propIdx].GetEncapsulatingSamplingRect(); Debug.Assert(!texSet.ts[propIdx].isNull, string.Format("Adding texture {0} to atlas {1} for texSet {2} srcMat {3}", matTex.GetTexName(), property.name, texSetIdx, texSet.matsAndGOs.mats[0].GetMaterialName())); yield return(CopyScaledAndTiledToAtlas(texSet.ts[propIdx], texSet, property, samplingRect, x, y, ww, hh, packedAtlasRects.padding[texSetIdx], atlasPixels, isNormalMap, data, combiner, progressInfo, LOG_LEVEL)); } yield return(data.numAtlases); if (progressInfo != null) { progressInfo("Applying changes to atlas: '" + property.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 " + property.name + " w=" + atlas.width + " h=" + atlas.height); } } atlases[propIdx] = atlas; if (progressInfo != null) { progressInfo("Saving atlas: '" + property.name + "'", .04f); } System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); if (data.resultType == MB2_TextureBakeResults.ResultType.atlas) { SaveAtlasAndConfigureResultMaterial(data, textureEditorMethods, atlases[propIdx], data.texPropertyNames[propIdx], propIdx); } combiner._destroyTemporaryTextures(data.texPropertyNames[propIdx].name); } yield break; }
/* * Packed rects may exceed atlas size and require scaling * When scaling want pixel perfect fit in atlas. Corners of rects should exactly align with pixel grid * Padding should be subtracted from pixel perfect rect to create pixel perfect square */ AtlasPackingResult ScaleAtlasToFitMaxDim(ProbeResult root, List <Vector2> imgWidthHeights, List <Image> images, int maxDimension, int padding, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, int outW, int outH, int recursionDepth) { int newMinSizeX = minImageSizeX; int newMinSizeY = minImageSizeY; bool redoPacking = false; //AtlasPackingResult[] rs = null; // the atlas may be packed larger than the maxDimension. If so then the atlas needs to be scaled down to fit float padX = (float)padding / (float)outW; //padding needs to be pixel perfect in size if (root.w > maxDimension) { padX = (float)padding / (float)maxDimension; float scaleFactor = (float)maxDimension / (float)root.w; if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("Packing exceeded atlas width shrinking to " + scaleFactor); } for (int i = 0; i < images.Count; i++) { Image im = images[i]; if (im.w * scaleFactor < masterImageSizeX) { //check if small images will be rounded too small. If so need to redo packing forcing a larger min size if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Small images are being scaled to zero. Will need to redo packing with larger minTexSizeX."); } redoPacking = true; newMinSizeX = Mathf.CeilToInt(minImageSizeX / scaleFactor); } int right = (int)((im.x + im.w) * scaleFactor); im.x = (int)(scaleFactor * im.x); im.w = right - im.x; } outW = maxDimension; } float padY = (float)padding / (float)outH; if (root.h > maxDimension) { //float minSizeY = ((float)minImageSizeY + 1) / maxDimension; padY = (float)padding / (float)maxDimension; float scaleFactor = (float)maxDimension / (float)root.h; if (LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogWarning("Packing exceeded atlas height shrinking to " + scaleFactor); } for (int i = 0; i < images.Count; i++) { Image im = images[i]; if (im.h * scaleFactor < masterImageSizeY) { //check if small images will be rounded too small. If so need to redo packing forcing a larger min size if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Small images are being scaled to zero. Will need to redo packing with larger minTexSizeY."); } redoPacking = true; newMinSizeY = Mathf.CeilToInt(minImageSizeY / scaleFactor); } int bottom = (int)((im.y + im.h) * scaleFactor); im.y = (int)(scaleFactor * im.y); im.h = bottom - im.y; } outH = maxDimension; } AtlasPackingResult res; if (!redoPacking) { res = new AtlasPackingResult(); res.rects = new Rect[images.Count]; res.srcImgIdxs = new int[images.Count]; res.atlasX = outW; res.atlasY = outH; res.usedW = -1; res.usedH = -1; for (int i = 0; i < images.Count; i++) { Image im = images[i]; Rect r = res.rects[i] = new Rect((float)im.x / (float)outW + padX, (float)im.y / (float)outH + padY, (float)im.w / (float)outW - padX * 2f, (float)im.h / (float)outH - padY * 2f); res.srcImgIdxs[i] = im.imgId; if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Image: " + i + " imgID=" + im.imgId + " x=" + r.x * outW + " y=" + r.y * outH + " w=" + r.width * outW + " h=" + r.height * outH + " padding=" + padding); } } return(res); } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("==================== REDOING PACKING ================"); } root = null; return(_GetRectsSingleAtlas(imgWidthHeights, maxDimension, padding, newMinSizeX, newMinSizeY, masterImageSizeX, masterImageSizeY, recursionDepth + 1)); } }
//------------------ Algorithm for fitting everything into one atlas and scaling down // // for images being added calc area, maxW, maxH. A perfectly packed atlas will match area exactly. atlas must be at least maxH and maxW in size. // Sort images from big to small using either height, width or area comparer // Explore space to find a resonably efficient packing. Grow the atlas gradually until a fit is found // Scale atlas to fit // AtlasPackingResult _GetRectsSingleAtlas(List <Vector2> imgWidthHeights, int maxDimension, int padding, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, int recursionDepth) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("_GetRects numImages={0}, maxDimension={1}, padding={2}, minImageSizeX={3}, minImageSizeY={4}, masterImageSizeX={5}, masterImageSizeY={6}, recursionDepth={7}", imgWidthHeights.Count, maxDimension, padding, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, recursionDepth)); } if (recursionDepth > 10) { if (LOG_LEVEL >= MB2_LogLevel.error) { Debug.LogError("Maximum recursion depth reached. Couldn't find packing for these textures."); } return(null); } float area = 0; int maxW = 0; int maxH = 0; Image[] imgsToAdd = new Image[imgWidthHeights.Count]; for (int i = 0; i < imgsToAdd.Length; i++) { int iw = (int)imgWidthHeights[i].x; int ih = (int)imgWidthHeights[i].y; Image im = imgsToAdd[i] = new Image(i, iw, ih, padding, minImageSizeX, minImageSizeY); area += im.w * im.h; maxW = Mathf.Max(maxW, im.w); maxH = Mathf.Max(maxH, im.h); } if ((float)maxH / (float)maxW > 2) { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using height Comparer"); } Array.Sort(imgsToAdd, new ImageHeightComparer()); } else if ((float)maxH / (float)maxW < .5) { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using width Comparer"); } Array.Sort(imgsToAdd, new ImageWidthComparer()); } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using area Comparer"); } Array.Sort(imgsToAdd, new ImageAreaComparer()); } //explore the space to find a resonably efficient packing int sqrtArea = (int)Mathf.Sqrt(area); int idealAtlasW; int idealAtlasH; if (doPowerOfTwoTextures) { idealAtlasW = idealAtlasH = RoundToNearestPositivePowerOfTwo(sqrtArea); if (maxW > idealAtlasW) { idealAtlasW = CeilToNearestPowerOfTwo(idealAtlasW); } if (maxH > idealAtlasH) { idealAtlasH = CeilToNearestPowerOfTwo(idealAtlasH); } } else { idealAtlasW = sqrtArea; idealAtlasH = sqrtArea; if (maxW > sqrtArea) { idealAtlasW = maxW; idealAtlasH = Mathf.Max(Mathf.CeilToInt(area / maxW), maxH); } if (maxH > sqrtArea) { idealAtlasW = Mathf.Max(Mathf.CeilToInt(area / maxH), maxW); idealAtlasH = maxH; } } if (idealAtlasW == 0) { idealAtlasW = 4; } if (idealAtlasH == 0) { idealAtlasH = 4; } int stepW = (int)(idealAtlasW * .15f); int stepH = (int)(idealAtlasH * .15f); if (stepW == 0) { stepW = 1; } if (stepH == 0) { stepH = 1; } int numWIterations = 2; int steppedWidth = idealAtlasW; int steppedHeight = idealAtlasH; while (numWIterations >= 1 && steppedHeight < sqrtArea * 1000) { bool successW = false; numWIterations = 0; steppedWidth = idealAtlasW; while (!successW && steppedWidth < sqrtArea * 1000) { ProbeResult pr = new ProbeResult(); if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("Probing h=" + steppedHeight + " w=" + steppedWidth); } if (ProbeSingleAtlas(imgsToAdd, steppedWidth, steppedHeight, area, maxDimension, pr)) { successW = true; if (bestRoot == null) { bestRoot = pr; } else if (pr.GetScore(doPowerOfTwoTextures) > bestRoot.GetScore(doPowerOfTwoTextures)) { bestRoot = pr; } } else { numWIterations++; steppedWidth = StepWidthHeight(steppedWidth, stepW, maxDimension); if (LOG_LEVEL >= MB2_LogLevel.trace) { MB2_Log.LogDebug("increasing Width h=" + steppedHeight + " w=" + steppedWidth); } } } steppedHeight = StepWidthHeight(steppedHeight, stepH, maxDimension); if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("increasing Height h=" + steppedHeight + " w=" + steppedWidth); } } if (bestRoot == null) { return(null); } int outW = 0; int outH = 0; if (doPowerOfTwoTextures) { outW = Mathf.Min(CeilToNearestPowerOfTwo(bestRoot.w), maxDimension); outH = Mathf.Min(CeilToNearestPowerOfTwo(bestRoot.h), maxDimension); if (outH < outW / 2) { outH = outW / 2; //smaller dim can't be less than half larger } if (outW < outH / 2) { outW = outH / 2; } } else { outW = Mathf.Min(bestRoot.w, maxDimension); outH = Mathf.Min(bestRoot.h, maxDimension); } bestRoot.outW = outW; bestRoot.outH = outH; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Best fit found: atlasW=" + outW + " atlasH" + outH + " w=" + bestRoot.w + " h=" + bestRoot.h + " efficiency=" + bestRoot.efficiency + " squareness=" + bestRoot.squareness + " fits in max dimension=" + bestRoot.largerOrEqualToMaxDim); } //Debug.Assert(images.Count != imgsToAdd.Length, "Result images not the same lentgh as source")); //the atlas can be larger than the max dimension scale it if this is the case //int newMinSizeX = minImageSizeX; //int newMinSizeY = minImageSizeY; List <Image> images = new List <Image>(); flattenTree(bestRoot.root, images); images.Sort(new ImgIDComparer()); // the atlas may be packed larger than the maxDimension. If so then the atlas needs to be scaled down to fit AtlasPackingResult res = ScaleAtlasToFitMaxDim(bestRoot, imgWidthHeights, images, maxDimension, padding, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, outW, outH, recursionDepth); if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug(String.Format("Done GetRects atlasW={0} atlasH={1}", bestRoot.w, bestRoot.h)); } return(res); }
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()); Rect[] uvRects = packedAtlasRects.rects; int atlasSizeX = packedAtlasRects.atlasX; int atlasSizeY = packedAtlasRects.atlasY; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Generated atlas will be " + atlasSizeX + "x" + atlasSizeY); } //create a game object GameObject renderAtlasesGO = null; try { renderAtlasesGO = new GameObject("MBrenderAtlasesGO"); MB3_AtlasPackerRenderTexture atlasRenderTexture = renderAtlasesGO.AddComponent <MB3_AtlasPackerRenderTexture>(); renderAtlasesGO.AddComponent <Camera>(); if (data._considerNonTextureProperties && LOG_LEVEL >= MB2_LogLevel.warn) { Debug.LogError("Blend Non-Texture Properties has limited functionality when used with Mesh Baker Texture Packer Fast. If no texture is pesent, then a small texture matching the non-texture property will be created and used in the atlas. But non-texture properties will not be blended into texture."); } 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 { GC.Collect(); MB3_TextureCombinerPackerRoot.CreateTemporaryTexturesForAtlas(data.distinctMaterialTextures, combiner, propIdx, data); if (progressInfo != null) { progressInfo("Creating Atlas '" + data.texPropertyNames[propIdx].name + "'", .01f); } // =========== // configure it if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("About to render " + data.texPropertyNames[propIdx].name + " isNormal=" + data.texPropertyNames[propIdx].isNormalMap); } atlasRenderTexture.LOG_LEVEL = LOG_LEVEL; atlasRenderTexture.width = atlasSizeX; atlasRenderTexture.height = atlasSizeY; atlasRenderTexture.padding = data._atlasPadding; atlasRenderTexture.rects = uvRects; atlasRenderTexture.textureSets = data.distinctMaterialTextures; atlasRenderTexture.indexOfTexSetToRender = propIdx; atlasRenderTexture.texPropertyName = data.texPropertyNames[propIdx]; atlasRenderTexture.isNormalMap = data.texPropertyNames[propIdx].isNormalMap; atlasRenderTexture.fixOutOfBoundsUVs = data._fixOutOfBoundsUVs; atlasRenderTexture.considerNonTextureProperties = data._considerNonTextureProperties; atlasRenderTexture.resultMaterialTextureBlender = data.nonTexturePropertyBlender; // call render on it atlas = atlasRenderTexture.OnRenderAtlas(combiner); // destroy it // ============= if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Saving atlas " + data.texPropertyNames[propIdx].name + " w=" + atlas.width + " h=" + atlas.height + " id=" + atlas.GetInstanceID()); } } atlases[propIdx] = atlas; if (progressInfo != null) { progressInfo("Saving atlas: '" + data.texPropertyNames[propIdx].name + "'", .04f); } if (data.resultType == MB2_TextureBakeResults.ResultType.atlas) { MB3_TextureCombinerPackerRoot.SaveAtlasAndConfigureResultMaterial(data, textureEditorMethods, atlases[propIdx], data.texPropertyNames[propIdx], propIdx); } combiner._destroyTemporaryTextures(data.texPropertyNames[propIdx].name); // need to save atlases before doing this } } catch (Exception ex) { //Debug.LogError(ex); Debug.LogError(ex.Message + "\n" + ex.StackTrace.ToString()); } finally { if (renderAtlasesGO != null) { MB_Utility.Destroy(renderAtlasesGO); } } yield break; }
public static AtlasPackingResult TestStackRectanglesVertical(AtlasPackingResult a, AtlasPackingResult b, int maxHeightDim, int maxWidthDim, bool stretchBToAtlasWidth) { return(MergeAtlasPackingResultStackBonA(a, b, maxWidthDim, maxHeightDim, stretchBToAtlasWidth, new VerticalPipeline())); }
AtlasPackingResult _GetRectsSingleAtlas(List <Vector2> imgWidthHeights, int maxDimension, int padding, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, int recursionDepth) { AtlasPackingResult res = new AtlasPackingResult(); List <Rect> rects = new List <Rect>(); int extent = 0; int maxh = 0; int maxw = 0; List <Image> images = new List <Image>(); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Packing rects for: " + imgWidthHeights.Count); } for (int i = 0; i < imgWidthHeights.Count; i++) { Image im = new Image(i, (int)imgWidthHeights[i].x, (int)imgWidthHeights[i].y, padding, minImageSizeX, minImageSizeY); // if images are stacked horizontally then there is no padding at the top or bottom if (packingOrientation == TexturePackingOrientation.vertical) { im.h -= padding * 2; im.x = extent; im.y = 0; rects.Add(new Rect(im.w, im.h, extent, 0)); extent += im.w; maxh = Mathf.Max(maxh, im.h); } else { im.w -= padding * 2; im.y = extent; im.x = 0; rects.Add(new Rect(im.w, im.h, 0, extent)); extent += im.h; maxw = Mathf.Max(maxw, im.w); } images.Add(im); } //scale atlas to fit maxDimension Vector2 rootWH; if (packingOrientation == TexturePackingOrientation.vertical) { rootWH = new Vector2(extent, maxh); } else { rootWH = new Vector2(maxw, extent); } int outW = (int)rootWH.x; int outH = (int)rootWH.y; if (packingOrientation == TexturePackingOrientation.vertical) { if (atlasMustBePowerOfTwo) { outW = Mathf.Min(CeilToNearestPowerOfTwo(outW), maxDimension); } else { outW = Mathf.Min(outW, maxDimension); } } else { if (atlasMustBePowerOfTwo) { outH = Mathf.Min(CeilToNearestPowerOfTwo(outH), maxDimension); } else { outH = Mathf.Min(outH, maxDimension); } } float padX, padY; int newMinSizeX, newMinSizeY; if (!ScaleAtlasToFitMaxDim(rootWH, images, maxDimension, padding, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, ref outW, ref outH, out padX, out padY, out newMinSizeX, out newMinSizeY)) { res = new AtlasPackingResult(); res.rects = new Rect[images.Count]; res.srcImgIdxs = new int[images.Count]; res.atlasX = outW; res.atlasY = outH; res.usedW = -1; res.usedH = -1; for (int i = 0; i < images.Count; i++) { Image im = images[i]; Rect r; if (packingOrientation == TexturePackingOrientation.vertical) { r = res.rects[i] = new Rect((float)im.x / (float)outW + padX, (float)im.y / (float)outH, (float)im.w / (float)outW - padX * 2f, stretchImagesToEdges ? 1f : (float)im.h / (float)outH); // all images are stretched to fill the height } else { r = res.rects[i] = new Rect((float)im.x / (float)outW, (float)im.y / (float)outH + padY, (stretchImagesToEdges ? 1f : ((float)im.w / (float)outW)), (float)im.h / (float)outH - padY * 2f); // all images are stretched to fill the height } res.srcImgIdxs[i] = im.imgId; if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Image: " + i + " imgID=" + im.imgId + " x=" + r.x * outW + " y=" + r.y * outH + " w=" + r.width * outW + " h=" + r.height * outH + " padding=" + padding + " outW=" + outW + " outH=" + outH); } } return(res); } Debug.Log("Packing failed returning null atlas result"); return(null); }
//------------------ Algorithm for fitting everything into one atlas and scaling down // // for images being added calc area, maxW, maxH. A perfectly packed atlas will match area exactly. atlas must be at least maxH and maxW in size. // Sort images from big to small using either height, width or area comparer // Explore space to find a resonably efficient packing. Grow the atlas gradually until a fit is found // Scale atlas to fit // AtlasPackingResult _GetRectsSingleAtlas(List <Vector2> imgWidthHeights, List <AtlasPadding> paddings, int maxDimensionX, int maxDimensionY, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, int recursionDepth) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("_GetRects numImages={0}, maxDimension={1}, minImageSizeX={2}, minImageSizeY={3}, masterImageSizeX={4}, masterImageSizeY={5}, recursionDepth={6}", imgWidthHeights.Count, maxDimensionX, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, recursionDepth)); } if (recursionDepth > MAX_RECURSION_DEPTH) { if (LOG_LEVEL >= MB2_LogLevel.error) { Debug.LogError("Maximum recursion depth reached. The baked atlas is likely not very good. " + " This happens when the packed atlases exceeds the maximum" + " atlas size in one or both dimensions so that the atlas needs to be downscaled AND there are some very thin or very small images (only-a-few-pixels)." + " these very thin images can 'vanish' completely when the atlas is downscaled.\n\n" + " Try one or more of the following: using multiple atlases, increase the maximum atlas size, don't use 'force-power-of-two', remove the source materials that are are using very small/thin textures."); } //return null; } float area = 0; int maxW = 0; int maxH = 0; Image[] imgsToAdd = new Image[imgWidthHeights.Count]; for (int i = 0; i < imgsToAdd.Length; i++) { int iw = (int)imgWidthHeights[i].x; int ih = (int)imgWidthHeights[i].y; Image im = imgsToAdd[i] = new Image(i, iw, ih, paddings[i], minImageSizeX, minImageSizeY); area += im.w * im.h; maxW = Mathf.Max(maxW, im.w); maxH = Mathf.Max(maxH, im.h); } if ((float)maxH / (float)maxW > 2) { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using height Comparer"); } Array.Sort(imgsToAdd, new ImageHeightComparer()); } else if ((float)maxH / (float)maxW < .5) { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using width Comparer"); } Array.Sort(imgsToAdd, new ImageWidthComparer()); } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Using area Comparer"); } Array.Sort(imgsToAdd, new ImageAreaComparer()); } //explore the space to find a resonably efficient packing int sqrtArea = (int)Mathf.Sqrt(area); int idealAtlasW; int idealAtlasH; if (atlasMustBePowerOfTwo) { idealAtlasW = idealAtlasH = RoundToNearestPositivePowerOfTwo(sqrtArea); if (maxW > idealAtlasW) { idealAtlasW = CeilToNearestPowerOfTwo(idealAtlasW); } if (maxH > idealAtlasH) { idealAtlasH = CeilToNearestPowerOfTwo(idealAtlasH); } } else { idealAtlasW = sqrtArea; idealAtlasH = sqrtArea; if (maxW > sqrtArea) { idealAtlasW = maxW; idealAtlasH = Mathf.Max(Mathf.CeilToInt(area / maxW), maxH); } if (maxH > sqrtArea) { idealAtlasW = Mathf.Max(Mathf.CeilToInt(area / maxH), maxW); idealAtlasH = maxH; } } if (idealAtlasW == 0) { idealAtlasW = 4; } if (idealAtlasH == 0) { idealAtlasH = 4; } int stepW = (int)(idealAtlasW * .15f); int stepH = (int)(idealAtlasH * .15f); if (stepW == 0) { stepW = 1; } if (stepH == 0) { stepH = 1; } int numWIterations = 2; int steppedWidth = idealAtlasW; int steppedHeight = idealAtlasH; while (numWIterations >= 1 && steppedHeight < sqrtArea * 1000) { bool successW = false; numWIterations = 0; steppedWidth = idealAtlasW; while (!successW && steppedWidth < sqrtArea * 1000) { ProbeResult pr = new ProbeResult(); if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log("Probing h=" + steppedHeight + " w=" + steppedWidth); } if (ProbeSingleAtlas(imgsToAdd, steppedWidth, steppedHeight, area, maxDimensionX, maxDimensionY, pr)) { successW = true; if (bestRoot == null) { bestRoot = pr; } else if (pr.GetScore(atlasMustBePowerOfTwo) > bestRoot.GetScore(atlasMustBePowerOfTwo)) { bestRoot = pr; } } else { numWIterations++; steppedWidth = StepWidthHeight(steppedWidth, stepW, maxDimensionX); if (LOG_LEVEL >= MB2_LogLevel.trace) { MB2_Log.LogDebug("increasing Width h=" + steppedHeight + " w=" + steppedWidth); } } } steppedHeight = StepWidthHeight(steppedHeight, stepH, maxDimensionY); if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("increasing Height h=" + steppedHeight + " w=" + steppedWidth); } } if (bestRoot == null) { return(null); } int outW = 0; int outH = 0; if (atlasMustBePowerOfTwo) { outW = Mathf.Min(CeilToNearestPowerOfTwo(bestRoot.w), maxDimensionX); outH = Mathf.Min(CeilToNearestPowerOfTwo(bestRoot.h), maxDimensionY); if (outH < outW / 2) { outH = outW / 2; //smaller dim can't be less than half larger } if (outW < outH / 2) { outW = outH / 2; } } else { outW = Mathf.Min(bestRoot.w, maxDimensionX); outH = Mathf.Min(bestRoot.h, maxDimensionY); } bestRoot.outW = outW; bestRoot.outH = outH; if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Best fit found: atlasW=" + outW + " atlasH" + outH + " w=" + bestRoot.w + " h=" + bestRoot.h + " efficiency=" + bestRoot.efficiency + " squareness=" + bestRoot.squareness + " fits in max dimension=" + bestRoot.largerOrEqualToMaxDim); } //Debug.Assert(images.Count != imgsToAdd.Length, "Result images not the same lentgh as source")); //the atlas can be larger than the max dimension scale it if this is the case //int newMinSizeX = minImageSizeX; //int newMinSizeY = minImageSizeY; List <Image> images = new List <Image>(); flattenTree(bestRoot.root, images); images.Sort(new ImgIDComparer()); // the atlas may be packed larger than the maxDimension. If so then the atlas needs to be scaled down to fit Vector2 rootWH = new Vector2(bestRoot.w, bestRoot.h); float padX, padY; int newMinSizeX, newMinSizeY; if (!ScaleAtlasToFitMaxDim(rootWH, images, maxDimensionX, maxDimensionY, paddings[0], minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, ref outW, ref outH, out padX, out padY, out newMinSizeX, out newMinSizeY) || recursionDepth > MAX_RECURSION_DEPTH) { AtlasPackingResult res = new AtlasPackingResult(paddings.ToArray()); res.rects = new Rect[images.Count]; res.srcImgIdxs = new int[images.Count]; res.atlasX = outW; res.atlasY = outH; res.usedW = -1; res.usedH = -1; for (int i = 0; i < images.Count; i++) { Image im = images[i]; Rect r = res.rects[i] = new Rect((float)im.x / (float)outW + padX, (float)im.y / (float)outH + padY, (float)im.w / (float)outW - padX * 2f, (float)im.h / (float)outH - padY * 2f); res.srcImgIdxs[i] = im.imgId; if (LOG_LEVEL >= MB2_LogLevel.debug) { MB2_Log.LogDebug("Image: " + i + " imgID=" + im.imgId + " x=" + r.x * outW + " y=" + r.y * outH + " w=" + r.width * outW + " h=" + r.height * outH + " padding=" + (paddings[i].leftRight * 2) + "x" + (paddings[i].topBottom * 2)); } } res.CalcUsedWidthAndHeight(); return(res); } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("==================== REDOING PACKING ================"); } //root = null; return(_GetRectsSingleAtlas(imgWidthHeights, paddings, maxDimensionX, maxDimensionY, newMinSizeX, newMinSizeY, masterImageSizeX, masterImageSizeY, recursionDepth + 1)); } //if (LOG_LEVEL >= MB2_LogLevel.debug) MB2_Log.LogDebug(String.Format("Done GetRects atlasW={0} atlasH={1}", bestRoot.w, bestRoot.h)); //return res; }
public override AtlasPackingResult[] CalculateAtlasRectangles(MB3_TextureCombinerPipeline.TexturePipelineData data, bool doMultiAtlas, MB2_LogLevel LOG_LEVEL) { Debug.Assert(!data.OnlyOneTextureInAtlasReuseTextures()); Debug.Assert(data._packingAlgorithm != MB2_PackingAlgorithmEnum.UnitysPackTextures, "Unity texture packer cannot be used"); IPipeline pipeline; if (_atlasDirection == AtlasDirection.horizontal) { pipeline = new HorizontalPipeline(); } else { pipeline = new VerticalPipeline(); } //int maxAtlasWidth = data._maxAtlasWidth; //int maxAtlasHeight = data._maxAtlasHeight; if (_atlasDirection == AtlasDirection.horizontal) { if (!data._useMaxAtlasWidthOverride) { // need to get the width of the atlas without mesh uvs considered int maxWidth = 2; for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { MB_TexSet ts = data.distinctMaterialTextures[i]; int w; if (data._fixOutOfBoundsUVs) { Vector2 rawHeightWidth = ts.GetMaxRawTextureHeightWidth(); w = (int)rawHeightWidth.x; } else { w = ts.idealWidth; } if (ts.idealWidth > maxWidth) { maxWidth = w; } } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Calculated max atlas width: " + maxWidth); } data._maxAtlasWidth = maxWidth; } } else { if (!data._useMaxAtlasHeightOverride) { int maxHeight = 2; for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { MB_TexSet ts = data.distinctMaterialTextures[i]; int h; if (data._fixOutOfBoundsUVs) { Vector2 rawHeightWidth = ts.GetMaxRawTextureHeightWidth(); h = (int)rawHeightWidth.y; } else { h = ts.idealHeight; } if (ts.idealHeight > maxHeight) { maxHeight = h; } } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Calculated max atlas height: " + maxHeight); } data._maxAtlasHeight = maxHeight; } } //split the list of distinctMaterialTextures into two bins List <MB_TexSet> horizontalVerticalDistinctMaterialTextures = new List <MB_TexSet>(); List <MB_TexSet> regularTextures = new List <MB_TexSet>(); for (int i = 0; i < data.distinctMaterialTextures.Count; i++) { pipeline.SortTexSetIntoBins(data.distinctMaterialTextures[i], horizontalVerticalDistinctMaterialTextures, regularTextures, data._maxAtlasWidth, data._maxAtlasHeight); } if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("Splitting list of distinctMaterialTextures numHorizontalVertical={0} numRegular={1} maxAtlasWidth={2} maxAtlasHeight={3}", horizontalVerticalDistinctMaterialTextures.Count, regularTextures.Count, data._maxAtlasWidth, data._maxAtlasHeight)); } //pack one bin with the horizontal vertical texture packer. MB2_TexturePacker tp; MB2_PackingAlgorithmEnum packingAlgorithm; AtlasPackingResult[] packerRectsHorizontalVertical; if (horizontalVerticalDistinctMaterialTextures.Count > 0) { packingAlgorithm = pipeline.GetPackingAlg(); List <Vector2> imageSizesHorizontalVertical = new List <Vector2>(); for (int i = 0; i < horizontalVerticalDistinctMaterialTextures.Count; i++) { horizontalVerticalDistinctMaterialTextures[i].SetTilingTreatmentAndAdjustEncapsulatingSamplingRect(pipeline.GetEdge2EdgeTreatment()); imageSizesHorizontalVertical.Add(new Vector2(horizontalVerticalDistinctMaterialTextures[i].idealWidth, horizontalVerticalDistinctMaterialTextures[i].idealHeight)); } tp = MB3_TextureCombinerPipeline.CreateTexturePacker(packingAlgorithm); tp.atlasMustBePowerOfTwo = false; List <AtlasPadding> paddingsHorizontalVertical = new List <AtlasPadding>(); for (int i = 0; i < imageSizesHorizontalVertical.Count; i++) { AtlasPadding padding = new AtlasPadding(); pipeline.InitializeAtlasPadding(ref padding, data._atlasPadding); paddingsHorizontalVertical.Add(padding); } tp.LOG_LEVEL = MB2_LogLevel.trace; packerRectsHorizontalVertical = tp.GetRects(imageSizesHorizontalVertical, paddingsHorizontalVertical, data._maxAtlasWidth, data._maxAtlasHeight, false); if (LOG_LEVEL >= MB2_LogLevel.trace) { Debug.Log(String.Format("Packed {0} textures with edgeToEdge tiling into an atlas of size {1} by {2} usedW {3} usedH {4}", horizontalVerticalDistinctMaterialTextures.Count, packerRectsHorizontalVertical[0].atlasX, packerRectsHorizontalVertical[0].atlasY, packerRectsHorizontalVertical[0].usedW, packerRectsHorizontalVertical[0].usedH)); } } else { packerRectsHorizontalVertical = new AtlasPackingResult[0]; } //pack other bin with regular texture packer AtlasPackingResult[] packerRectsRegular; if (regularTextures.Count > 0) { packingAlgorithm = MB2_PackingAlgorithmEnum.MeshBakerTexturePacker; List <Vector2> imageSizesRegular = new List <Vector2>(); for (int i = 0; i < regularTextures.Count; i++) { imageSizesRegular.Add(new Vector2(regularTextures[i].idealWidth, regularTextures[i].idealHeight)); } tp = MB3_TextureCombinerPipeline.CreateTexturePacker(MB2_PackingAlgorithmEnum.MeshBakerTexturePacker); tp.atlasMustBePowerOfTwo = false; List <AtlasPadding> paddingsRegular = new List <AtlasPadding>(); for (int i = 0; i < imageSizesRegular.Count; i++) { AtlasPadding padding = new AtlasPadding(); padding.topBottom = data._atlasPadding; padding.leftRight = data._atlasPadding; paddingsRegular.Add(padding); } int atlasRegularMaxWidth, atlasRegularMaxHeight; int usedHorizontalVertWidth = 0, usedHorizontalVertHeight = 0; if (packerRectsHorizontalVertical.Length > 0) { usedHorizontalVertHeight = packerRectsHorizontalVertical[0].atlasY; usedHorizontalVertWidth = packerRectsHorizontalVertical[0].atlasX; } pipeline.GetExtraRoomForRegularAtlas(usedHorizontalVertWidth, usedHorizontalVertHeight, data._maxAtlasWidth, data._maxAtlasHeight, out atlasRegularMaxWidth, out atlasRegularMaxHeight); packerRectsRegular = tp.GetRects(imageSizesRegular, paddingsRegular, atlasRegularMaxWidth, atlasRegularMaxHeight, false); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("Packed {0} textures without edgeToEdge tiling into an atlas of size {1} by {2} usedW {3} usedH {4}", regularTextures.Count, packerRectsRegular[0].atlasX, packerRectsRegular[0].atlasY, packerRectsRegular[0].usedW, packerRectsRegular[0].usedH)); } } else { packerRectsRegular = new AtlasPackingResult[0]; } AtlasPackingResult result = null; if (packerRectsHorizontalVertical.Length == 0 && packerRectsRegular.Length == 0) { Debug.Assert(false, "Should never have reached this."); return(null); } else if (packerRectsHorizontalVertical.Length > 0 && packerRectsRegular.Length > 0) { result = MergeAtlasPackingResultStackBonA(packerRectsHorizontalVertical[0], packerRectsRegular[0], data._maxAtlasWidth, data._maxAtlasHeight, true, pipeline); } else if (packerRectsHorizontalVertical.Length > 0) { result = packerRectsHorizontalVertical[0]; } else if (packerRectsRegular.Length > 0) { result = packerRectsRegular[0]; } Debug.Assert(data.distinctMaterialTextures.Count == result.rects.Length); //We re-ordered the distinctMaterial textures so replace the list with the new reordered one horizontalVerticalDistinctMaterialTextures.AddRange(regularTextures); data.distinctMaterialTextures = horizontalVerticalDistinctMaterialTextures; AtlasPackingResult[] results; if (result != null) { results = new AtlasPackingResult[] { result } } ; else { results = new AtlasPackingResult[0]; } return(results); }