//------------------ 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; }
//----------------- 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()); }
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); }
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); }
AtlasPackingResult _GetRectsSingleAtlas(List <Vector2> imgWidthHeights, List <AtlasPadding> paddings, int maxDimensionX, int maxDimensionY, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, int recursionDepth) { AtlasPackingResult res = new AtlasPackingResult(paddings.ToArray()); 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, paddings[i], minImageSizeX, minImageSizeY); // if images are stacked horizontally then there is no padding at the top or bottom if (packingOrientation == TexturePackingOrientation.vertical) { im.h -= paddings[i].topBottom * 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 -= paddings[i].leftRight * 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), maxDimensionX); } else { outW = Mathf.Min(outW, maxDimensionX); } } else { if (atlasMustBePowerOfTwo) { outH = Mathf.Min(CeilToNearestPowerOfTwo(outH), maxDimensionY); } else { outH = Mathf.Min(outH, maxDimensionY); } } 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)) { res = new AtlasPackingResult(paddings.ToArray()); res.rects = new Rect[images.Count]; res.srcImgIdxs = new int[images.Count]; res.atlasX = outW; res.atlasY = outH; 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=" + paddings[i] + " outW=" + outW + " outH=" + outH); } } res.CalcUsedWidthAndHeight(); return(res); } Debug.Log("Packing failed returning null atlas result"); return(null); }
AtlasPackingResult[] _GetRectsMultiAtlasVertical(List <Vector2> imgWidthHeights, List <AtlasPadding> paddings, int maxDimensionPassedX, int maxDimensionPassedY, 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, paddings[i], minImageSizeX, minImageSizeY); im.h -= paddings[i].topBottom * 2; allImages.Add(im); } allImages.Sort(new ImageWidthComparer()); List <Image> images = new List <Image>(); List <Rect> rects = new List <Rect>(); int spaceRemaining = maxDimensionPassedX; while (allImages.Count > 0 || images.Count > 0) { Image im = PopLargestThatFits(allImages, spaceRemaining, maxDimensionPassedX, images.Count == 0); if (im == null) { if (LOG_LEVEL >= MB2_LogLevel.debug) { Debug.Log("Atlas filled creating a new atlas "); } AtlasPackingResult apr = new AtlasPackingResult(paddings.ToArray()); 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, images[j].w, stretchImagesToEdges ? maxh : images[j].h); rss[j] = r; srcImgIdx[j] = images[j].imgId; } apr.rects = rss; apr.srcImgIdxs = srcImgIdx; apr.CalcUsedWidthAndHeight(); images.Clear(); rects.Clear(); extent = 0; maxh = 0; rs.Add(apr); spaceRemaining = maxDimensionPassedX; } else { im.x = extent; im.y = 0; images.Add(im); rects.Add(new Rect(extent, 0, im.w, im.h)); extent += im.w; maxh = Mathf.Max(maxh, im.h); maxw = extent; spaceRemaining = maxDimensionPassedX - extent; } } for (int i = 0; i < rs.Count; i++) { int outW = rs[i].atlasX; int outH = Mathf.Min(rs[i].atlasY, maxDimensionPassedY); if (atlasMustBePowerOfTwo) { outW = Mathf.Min(CeilToNearestPowerOfTwo(outW), maxDimensionPassedX); } else { outW = Mathf.Min(outW, maxDimensionPassedX); } rs[i].atlasX = outW; //------------------------------- //scale atlas to fit maxDimension float padX, padY; int newMinSizeX, newMinSizeY; ScaleAtlasToFitMaxDim(new Vector2(rs[i].atlasX, rs[i].atlasY), images, maxDimensionPassedX, maxDimensionPassedY, paddings[0], 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], paddings[i]); rs[i].CalcUsedWidthAndHeight(); } //----------------------------- return(rs.ToArray()); }