public PipelineVariationSomeTexturesUseDifferentMatTiling(MB_TexSet ts) { texSet = ts; Debug.Assert(texSet.allTexturesUseSameMatTiling == false); }
/* public void SetMaterialTilingTo0011() { matTilingRect = new DRect(0, 0, 1, 1); } */ /// <summary> /// The ts variable serves no functional purpose. I would like this method to ONLY be called from /// MB_TexSet but there is no way in C# to enforce that. If you want to use this /// outside of MB_TexSet then add a method to MB_TexSet that does the add on your /// behalf. The new method should assert that MB_TexSet is in the correct state and should /// do any necessary stated changes to MB_TexSet depending on the context. /// /// After you are done you should "FindAllReferences" for this function to ensure that it is only /// called by MB_TexSet. /// </summary> /// <param name="ts"> /// Not used, provided as an indicator to developers that all /// access to this must go through MB_TexSet. /// </param> public void SetEncapsulatingSamplingRect(MB_TexSet ts, DRect r) { encapsulatingSamplingRect = r; }
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); }
public PipelineVariationAllTexturesUseSameMatTiling(MB_TexSet ts) { texSet = ts; Debug.Assert(texSet.allTexturesUseSameMatTiling == true); }
internal static IEnumerator CopyScaledAndTiledToAtlas(MeshBakerMaterialTexture source, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName, DRect srcSamplingRect, int targX, int targY, int targW, int targH, AtlasPadding padding, Color[][] atlasPixels, bool isNormalMap, MB3_TextureCombinerPipeline.TexturePipelineData data, MB3_TextureCombiner combiner, ProgressUpdateDelegate progressInfo = null, MB2_LogLevel LOG_LEVEL = MB2_LogLevel.info) { //HasFinished = false; Texture2D t = source.GetTexture2D(); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(String.Format("CopyScaledAndTiledToAtlas: {0} inAtlasX={1} inAtlasY={2} inAtlasW={3} inAtlasH={4} paddX={5} paddY={6} srcSamplingRect={7}", t, targX, targY, targW, targH, padding.leftRight, padding.topBottom, srcSamplingRect)); } float newWidth = targW; float newHeight = targH; float scx = (float)srcSamplingRect.width; float scy = (float)srcSamplingRect.height; float ox = (float)srcSamplingRect.x; float oy = (float)srcSamplingRect.y; int w = (int)newWidth; int h = (int)newHeight; if (data._considerNonTextureProperties) { t = combiner._createTextureCopy(shaderPropertyName.name, t); t = data.nonTexturePropertyBlender.TintTextureWithTextureCombiner(t, sourceMaterial, shaderPropertyName); } for (int i = 0; i < w; i++) { if (progressInfo != null && w > 0) { progressInfo("CopyScaledAndTiledToAtlas " + (((float)i / (float)w) * 100f).ToString("F0"), .2f); } for (int j = 0; j < h; j++) { float u = i / newWidth * scx + ox; float v = j / newHeight * scy + oy; atlasPixels[targY + j][targX + i] = t.GetPixelBilinear(u, v); } } //bleed the border colors into the padding for (int i = 0; i < w; i++) { for (int j = 1; j <= padding.topBottom; j++) { //top margin atlasPixels[(targY - j)][targX + i] = atlasPixels[(targY)][targX + i]; //bottom margin atlasPixels[(targY + h - 1 + j)][targX + i] = atlasPixels[(targY + h - 1)][targX + i]; } } for (int j = 0; j < h; j++) { for (int i = 1; i <= padding.leftRight; i++) { //left margin atlasPixels[(targY + j)][targX - i] = atlasPixels[(targY + j)][targX]; //right margin atlasPixels[(targY + j)][targX + w + i - 1] = atlasPixels[(targY + j)][targX + w - 1]; } } //corners for (int i = 1; i <= padding.leftRight; i++) { for (int j = 1; j <= padding.topBottom; j++) { atlasPixels[(targY - j)][targX - i] = atlasPixels[targY][targX]; atlasPixels[(targY + h - 1 + j)][targX - i] = atlasPixels[(targY + h - 1)][targX]; atlasPixels[(targY + h - 1 + j)][targX + w + i - 1] = atlasPixels[(targY + h - 1)][targX + w - 1]; atlasPixels[(targY - j)][targX + w + i - 1] = atlasPixels[targY][targX + w - 1]; yield return(null); } yield return(null); } // Debug.Log("copyandscaledatlas finished too!"); //HasFinished = true; yield break; }
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._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(data.texPropertyNames[propIdx].name); } yield break; }
public Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName) { Debug.Assert(_textureProperties._considerNonTextureProperties == false); Debug.LogError("TintTextureWithTextureCombiner should never be called if resultMaterialTextureBlender is null"); return(t); }
internal Texture2D TintTextureWithTextureCombiner(Texture2D t, MB_TexSet sourceMaterial, ShaderTextureProperty shaderPropertyName) { return(_nonTexturePropertiesBlender.TintTextureWithTextureCombiner(t, sourceMaterial, shaderPropertyName)); }
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()); long estArea = 0; int atlasSizeX = 1; int atlasSizeY = 1; Rect[] 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, 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 numTextures " + texToPack.Length + " 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) { SaveAtlasAndConfigureResultMaterial(data, textureEditorMethods, atlases[propIdx], data.texPropertyNames[propIdx], propIdx); } 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 void MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects(List <MB_TexSet> distinctMaterialTextures) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects num atlas rects" + distinctMaterialTextures.Count); } int numMerged = 0; // IMPORTANT: Note that the verts stored in the mesh are NOT Normalized UV Coords. They are normalized * [UVTrans]. To get normalized UV // coords we must multiply them by [invUVTrans]. Need to do this to the verts in the mesh before we do any transforms with them. // Also check that all textures use same tiling. This is a prerequisite for merging. // Mark MB3_TexSet that are mergable (allTexturesUseSameMatTiling) for (int i = 0; i < distinctMaterialTextures.Count; i++) { MB_TexSet tx = distinctMaterialTextures[i]; int idxOfFirstNotNull = -1; bool allAreSame = true; DRect firstRect = new DRect(); for (int propIdx = 0; propIdx < tx.ts.Length; propIdx++) { if (idxOfFirstNotNull != -1) { if (!tx.ts[propIdx].isNull && firstRect != tx.ts[propIdx].matTilingRect) { allAreSame = false; } } else if (!tx.ts[propIdx].isNull) { idxOfFirstNotNull = propIdx; firstRect = tx.ts[propIdx].matTilingRect; } } if (LOG_LEVEL >= MB2_LogLevel.debug || LOG_LEVEL_TRACE_MERGE_MAT_SUBRECTS == true) { if (allAreSame) { Debug.LogFormat("TextureSet {0} allTexturesUseSameMatTiling = {1}", i, allAreSame); } else { Debug.Log(string.Format("Textures in material(s) do not all use the same material tiling. This set of textures will not be considered for merge: {0} ", tx.GetDescription())); } } if (allAreSame) { tx.SetAllTexturesUseSameMatTilingTrue(); } } for (int i = 0; i < distinctMaterialTextures.Count; i++) { MB_TexSet tx = distinctMaterialTextures[i]; for (int matIdx = 0; matIdx < tx.matsAndGOs.mats.Count; matIdx++) { if (tx.matsAndGOs.gos.Count > 0) { tx.matsAndGOs.mats[matIdx].objName = tx.matsAndGOs.gos[0].name; } else if (tx.ts[0] != null) { tx.matsAndGOs.mats[matIdx].objName = string.Format("[objWithTx:{0} atlasBlock:{1} matIdx{2}]", tx.ts[0].GetTexName(), i, matIdx); } else { tx.matsAndGOs.mats[matIdx].objName = string.Format("[objWithTx:{0} atlasBlock:{1} matIdx{2}]", "Unknown", i, matIdx); } } tx.CalcInitialFullSamplingRects(fixOutOfBoundsUVs); tx.CalcMatAndUVSamplingRects(); } _HasBeenInitialized = true; // need to calculate the srcSampleRect for the complete tiling in the atlas // for each material need to know what the subrect would be in the atlas if material UVRect was 0,0,1,1 and Merged uvRect was full tiling List <int> MarkedForDeletion = new List <int>(); for (int i = 0; i < distinctMaterialTextures.Count; i++) { MB_TexSet tx2 = distinctMaterialTextures[i]; for (int j = i + 1; j < distinctMaterialTextures.Count; j++) { MB_TexSet tx1 = distinctMaterialTextures[j]; if (tx1.AllTexturesAreSameForMerge(tx2, _considerNonTextureProperties, resultMaterialTextureBlender)) { double accumulatedAreaCombined = 0f; double accumulatedAreaNotCombined = 0f; DRect encapsulatingRectMerged = new DRect(); int idxOfFirstNotNull = -1; for (int propIdx = 0; propIdx < tx2.ts.Length; propIdx++) { if (!tx2.ts[propIdx].isNull) { if (idxOfFirstNotNull == -1) { idxOfFirstNotNull = propIdx; } } } DRect encapsulatingRect1 = new DRect(); DRect encapsulatingRect2 = new DRect(); if (idxOfFirstNotNull != -1) { // only in here if all properties use the same tiling so don't need to worry about which propIdx we are dealing with //Get the rect that encapsulates all material and UV tiling for materials and meshes in tx1 encapsulatingRect1 = tx1.matsAndGOs.mats[0].samplingRectMatAndUVTiling; for (int matIdx = 1; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { DRect tmpSsamplingRectMatAndUVTilingTx1 = tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling; encapsulatingRect1 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref tmpSsamplingRectMatAndUVTilingTx1); } //same for tx2 encapsulatingRect2 = tx2.matsAndGOs.mats[0].samplingRectMatAndUVTiling; for (int matIdx = 1; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { DRect tmpSsamplingRectMatAndUVTilingTx2 = tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling; encapsulatingRect2 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect2, ref tmpSsamplingRectMatAndUVTilingTx2); } encapsulatingRectMerged = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref encapsulatingRect2); accumulatedAreaCombined += encapsulatingRectMerged.width * encapsulatingRectMerged.height; accumulatedAreaNotCombined += encapsulatingRect1.width * encapsulatingRect1.height + encapsulatingRect2.width * encapsulatingRect2.height; } else { encapsulatingRectMerged = new DRect(0f, 0f, 1f, 1f); } //the distinct material textures may overlap. //if the area of these rectangles combined is less than the sum of these areas of these rectangles then merge these distinctMaterialTextures if (accumulatedAreaCombined < accumulatedAreaNotCombined) { // merge tx2 into tx1 numMerged++; StringBuilder sb = null; if (LOG_LEVEL >= MB2_LogLevel.info) { sb = new StringBuilder(); sb.AppendFormat("About To Merge:\n TextureSet1 {0}\n TextureSet2 {1}\n", tx1.GetDescription(), tx2.GetDescription()); if (LOG_LEVEL >= MB2_LogLevel.trace) { for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect()); } for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx2 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx2.matsAndGOs.mats[matIdx].mat, tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx2.ts[0].GetEncapsulatingSamplingRect()); } } } //copy game objects over for (int k = 0; k < tx2.matsAndGOs.gos.Count; k++) { if (!tx1.matsAndGOs.gos.Contains(tx2.matsAndGOs.gos[k])) { tx1.matsAndGOs.gos.Add(tx2.matsAndGOs.gos[k]); } } //copy materials over from tx2 to tx1 for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { tx1.matsAndGOs.mats.Add(tx2.matsAndGOs.mats[matIdx]); } tx1.SetEncapsulatingSamplingRectWhenMergingTexSets(encapsulatingRectMerged); if (!MarkedForDeletion.Contains(i)) { MarkedForDeletion.Add(i); } if (LOG_LEVEL >= MB2_LogLevel.debug) { if (LOG_LEVEL >= MB2_LogLevel.trace) { sb.AppendFormat("=== After Merge TextureSet {0}\n", tx1.GetDescription()); for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect()); } //Integrity check that sampling rects fit into enapsulating rects if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); } } } Debug.Log(sb.ToString()); } break; } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(string.Format("Considered merging {0} and {1} but there was not enough overlap. It is more efficient to bake these to separate rectangles.", tx1.GetDescription() + encapsulatingRect1, tx2.GetDescription() + encapsulatingRect2)); } } } } } //remove distinctMaterialTextures that were merged for (int j = MarkedForDeletion.Count - 1; j >= 0; j--) { distinctMaterialTextures.RemoveAt(MarkedForDeletion[j]); } MarkedForDeletion.Clear(); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(string.Format("MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects complete merged {0} now have {1}", numMerged, distinctMaterialTextures.Count)); } if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); } }
// This should only be called after regular merge so that rects have been correctly setup. public void MergeDistinctMaterialTexturesThatWouldExceedMaxAtlasSizeAndCalcMaterialSubrects(List <MB_TexSet> distinctMaterialTextures, int maxAtlasSize) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("MergeDistinctMaterialTexturesThatWouldExceedMaxAtlasSizeAndCalcMaterialSubrects num atlas rects" + distinctMaterialTextures.Count); } Debug.Assert(_HasBeenInitialized, "MergeOverlappingDistinctMaterialTexturesAndCalcMaterialSubrects must be called before MergeDistinctMaterialTexturesThatWouldExceedMaxAtlasSizeAndCalcMaterialSubrects"); int numMerged = 0; List <int> MarkedForDeletion = new List <int>(); for (int i = 0; i < distinctMaterialTextures.Count; i++) { MB_TexSet tx2 = distinctMaterialTextures[i]; for (int j = i + 1; j < distinctMaterialTextures.Count; j++) { MB_TexSet tx1 = distinctMaterialTextures[j]; if (tx1.AllTexturesAreSameForMerge(tx2, _considerNonTextureProperties, resultMaterialTextureBlender)) { //Check if the size of the rect in the atlas would be greater than max atlas size. DRect encapsulatingRectMerged = new DRect(); int idxOfFirstNotNull = -1; for (int propIdx = 0; propIdx < tx2.ts.Length; propIdx++) { if (!tx2.ts[propIdx].isNull) { if (idxOfFirstNotNull == -1) { idxOfFirstNotNull = propIdx; } } } DRect encapsulatingRect1 = new DRect(); DRect encapsulatingRect2 = new DRect(); if (idxOfFirstNotNull != -1) { // only in here if all properties use the same tiling so don't need to worry about which propIdx we are dealing with //Get the rect that encapsulates all material and UV tiling for materials and meshes in tx1 encapsulatingRect1 = tx1.matsAndGOs.mats[0].samplingRectMatAndUVTiling; for (int matIdx = 1; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { DRect tmpSsamplingRectMatAndUVTilingTx1 = tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling; encapsulatingRect1 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref tmpSsamplingRectMatAndUVTilingTx1); } //same for tx2 encapsulatingRect2 = tx2.matsAndGOs.mats[0].samplingRectMatAndUVTiling; for (int matIdx = 1; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { DRect tmpSsamplingRectMatAndUVTilingTx2 = tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling; encapsulatingRect2 = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect2, ref tmpSsamplingRectMatAndUVTilingTx2); } encapsulatingRectMerged = MB3_UVTransformUtility.GetEncapsulatingRectShifted(ref encapsulatingRect1, ref encapsulatingRect2); } else { encapsulatingRectMerged = new DRect(0f, 0f, 1f, 1f); } Vector2 maxHeightWidth = tx1.GetMaxRawTextureHeightWidth(); if (encapsulatingRectMerged.width * maxHeightWidth.x > maxAtlasSize || encapsulatingRectMerged.height * maxHeightWidth.y > maxAtlasSize) { // merge tx2 into tx1 numMerged++; StringBuilder sb = null; if (LOG_LEVEL >= MB2_LogLevel.info) { sb = new StringBuilder(); sb.AppendFormat("About To Merge:\n TextureSet1 {0}\n TextureSet2 {1}\n", tx1.GetDescription(), tx2.GetDescription()); if (LOG_LEVEL >= MB2_LogLevel.trace) { for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect()); } for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx2 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx2.matsAndGOs.mats[matIdx].mat, tx2.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx2.ts[0].GetEncapsulatingSamplingRect()); } } } //copy game objects over for (int k = 0; k < tx2.matsAndGOs.gos.Count; k++) { if (!tx1.matsAndGOs.gos.Contains(tx2.matsAndGOs.gos[k])) { tx1.matsAndGOs.gos.Add(tx2.matsAndGOs.gos[k]); } } //copy materials over from tx2 to tx1 for (int matIdx = 0; matIdx < tx2.matsAndGOs.mats.Count; matIdx++) { tx1.matsAndGOs.mats.Add(tx2.matsAndGOs.mats[matIdx]); } tx1.SetEncapsulatingSamplingRectWhenMergingTexSets(encapsulatingRectMerged); if (!MarkedForDeletion.Contains(i)) { MarkedForDeletion.Add(i); } if (LOG_LEVEL >= MB2_LogLevel.debug) { if (LOG_LEVEL >= MB2_LogLevel.trace) { sb.AppendFormat("=== After Merge TextureSet {0}\n", tx1.GetDescription()); for (int matIdx = 0; matIdx < tx1.matsAndGOs.mats.Count; matIdx++) { sb.AppendFormat("tx1 Mat {0} matAndMeshUVRect {1} fullSamplingRect {2}\n", tx1.matsAndGOs.mats[matIdx].mat, tx1.matsAndGOs.mats[matIdx].samplingRectMatAndUVTiling, tx1.ts[0].GetEncapsulatingSamplingRect()); } //Integrity check that sampling rects fit into enapsulating rects if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); } } } Debug.Log(sb.ToString()); } break; } else { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(string.Format("Considered merging {0} and {1} but there was not enough overlap. It is more efficient to bake these to separate rectangles.", tx1.GetDescription() + encapsulatingRect1, tx2.GetDescription() + encapsulatingRect2)); } } } } } //remove distinctMaterialTextures that were merged for (int j = MarkedForDeletion.Count - 1; j >= 0; j--) { distinctMaterialTextures.RemoveAt(MarkedForDeletion[j]); } MarkedForDeletion.Clear(); if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log(string.Format("MergeDistinctMaterialTexturesThatWouldExceedMaxAtlasSizeAndCalcMaterialSubrects complete merged {0} now have {1}", numMerged, distinctMaterialTextures.Count)); } if (MB3_MeshBakerRoot.DO_INTEGRITY_CHECKS) { DoIntegrityCheckMergedEncapsulatingSamplingRects(distinctMaterialTextures); } }
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; }
public bool AllTexturesAreSameForMerge(MB_TexSet other, /*bool considerTintColor*/ bool considerNonTextureProperties, MB3_TextureCombinerNonTextureProperties resultMaterialTextureBlender) { if (other.ts.Length != ts.Length) { return(false); } else { if (!other.allTexturesUseSameMatTiling || !allTexturesUseSameMatTiling) { return(false); } // must use same set of textures int idxOfFirstNoneNull = -1; for (int i = 0; i < ts.Length; i++) { if (!ts[i].AreTexturesEqual(other.ts[i])) { return(false); } if (idxOfFirstNoneNull == -1 && !ts[i].isNull) { idxOfFirstNoneNull = i; } if (considerNonTextureProperties) { if (resultMaterialTextureBlender != null) { if (!resultMaterialTextureBlender.NonTexturePropertiesAreEqual(matsAndGOs.mats[0].mat, other.matsAndGOs.mats[0].mat)) { return(false); } } } } if (idxOfFirstNoneNull != -1) { //check that all textures are the same. Have already checked all tiling is same for (int i = 0; i < ts.Length; i++) { if (!ts[i].AreTexturesEqual(other.ts[i])) { return(false); } } //========================================================= // OLD check less strict //When comparting two sets of textures (main, bump, spec ...) A and B that have different scales & offsets.They can share if: // - the scales of each texPropertyName (main, bump ...) are the same ratio: ASmain / BSmain = ASbump / BSbump = ASspec / BSspec // - the offset of A to B in uv space is the same for each texPropertyName: // offset = final - initial = OA / SB - OB must be the same /* * MeshBakerMaterialTexture ma = ts[idxOfFirstNoneNull]; * MeshBakerMaterialTexture mb = other.ts[idxOfFirstNoneNull]; * //construct a rect that will ratio and offset * DRect r1 = new DRect( (ma.matTilingRect.x / mb.matTilingRect.width - mb.matTilingRect.x), * (ma.matTilingRect.y / mb.matTilingRect.height - mb.matTilingRect.y), * (mb.matTilingRect.width / ma.matTilingRect.width), * (mb.matTilingRect.height / ma.matTilingRect.height)); * for (int i = 0; i < ts.Length; i++) * { * if (ts[i].t != null) * { * ma = ts[i]; * mb = other.ts[i]; * DRect r2 = new DRect( (ma.matTilingRect.x / mb.matTilingRect.width - mb.matTilingRect.x), * (ma.matTilingRect.y / mb.matTilingRect.height - mb.matTilingRect.y), * (mb.matTilingRect.width / ma.matTilingRect.width), * (mb.matTilingRect.height / ma.matTilingRect.height)); * if (Math.Abs(r2.x - r1.x) > 10e-10f) return false; * if (Math.Abs(r2.y - r1.y) > 10e-10f) return false; * if (Math.Abs(r2.width - r1.width) > 10e-10f) return false; * if (Math.Abs(r2.height - r1.height) > 10e-10f) return false; * } * } */ } return(true); } }