//------------------ 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;
        }
Пример #2
0
        Rect[] _GetRects(List <Vector2> imgWidthHeights, int maxDimension, int padding, int minImageSizeX, int minImageSizeY, int masterImageSizeX, int masterImageSizeY, out int outW, out int outH, int recursionDepth)
        {
            if (LOG_LEVEL >= MBLogLevel.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)
            {
                Debug.LogError("Maximum recursion depth reached. Couldn't find packing for these textures.");
                outW = 0;
                outH = 0;
                return(new Rect[0]);
            }
            float area = 0;
            int   maxW = 0;
            int   maxH = 0;

            Image[] imgsToAdd = new Image[imgWidthHeights.Count];
            for (int i = 0; i < imgsToAdd.Length; i++)
            {
                Image im = imgsToAdd[i] = new Image(i, (int)imgWidthHeights[i].x, (int)imgWidthHeights[i].y, 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 >= MBLogLevel.debug)
                {
                    MBLog.LogDebug("Using height Comparer");
                }
                Array.Sort(imgsToAdd, new ImageHeightComparer());
            }
            else if ((float)maxH / (float)maxW < .5)
            {
                if (LOG_LEVEL >= MBLogLevel.debug)
                {
                    MBLog.LogDebug("Using width Comparer");
                }
                Array.Sort(imgsToAdd, new ImageWidthComparer());
            }
            else
            {
                if (LOG_LEVEL >= MBLogLevel.debug)
                {
                    MBLog.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 = 1;
            }
            if (idealAtlasH == 0)
            {
                idealAtlasH = 1;
            }
            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 >= MBLogLevel.trace)
                    {
                        Debug.Log("Probing h=" + steppedHeight + " w=" + steppedWidth);
                    }
                    if (Probe(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 >= MBLogLevel.debug)
                        {
                            MBLog.LogDebug("increasing Width h=" + steppedHeight + " w=" + steppedWidth);
                        }
                    }
                }
                steppedHeight = StepWidthHeight(steppedHeight, stepH, maxDimension);
                if (LOG_LEVEL >= MBLogLevel.debug)
                {
                    MBLog.LogDebug("increasing Height h=" + steppedHeight + " w=" + steppedWidth);
                }
            }

            outW = 0;
            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 = bestRoot.w;
                outH = bestRoot.h;
            }
            if (bestRoot == null)
            {
                return(null);
            }
            if (LOG_LEVEL >= MBLogLevel.debug)
            {
                MBLog.LogDebug("Best fit found: atlasW=" + outW + " atlasH" + outH + " w=" + bestRoot.w + " h=" + bestRoot.h + " efficiency=" + bestRoot.efficiency + " squareness=" + bestRoot.squareness + " fits in max dimension=" + bestRoot.fitsInMaxSize);
            }

            List <Image> images = new List <Image>();

            flattenTree(bestRoot.root, images);
            images.Sort(new ImgIDComparer());
            if (images.Count != imgsToAdd.Length)
            {
                Debug.LogError("Result images not the same lentgh as source");
            }

            //scale images if too large
            int   newMinSizeX = minImageSizeX;
            int   newMinSizeY = minImageSizeY;
            bool  redoPacking = false;
            float padX        = (float)padding / (float)outW;

            if (bestRoot.w > maxDimension)
            {
                //float minSizeX = ((float)minImageSizeX + 1) / maxDimension;
                padX = (float)padding / (float)maxDimension;
                float scaleFactor = (float)maxDimension / (float)bestRoot.w;
                if (LOG_LEVEL >= MBLogLevel.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 >= MBLogLevel.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 (bestRoot.h > maxDimension)
            {
                //float minSizeY = ((float)minImageSizeY + 1) / maxDimension;
                padY = (float)padding / (float)maxDimension;
                float scaleFactor = (float)maxDimension / (float)bestRoot.h;
                if (LOG_LEVEL >= MBLogLevel.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 >= MBLogLevel.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;
            }

            Rect[] rs;
            if (!redoPacking)
            {
                rs = new Rect[images.Count];
                for (int i = 0; i < images.Count; i++)
                {
                    Image im = images[i];
                    Rect  r  = rs[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);
                    if (LOG_LEVEL >= MBLogLevel.debug)
                    {
                        MBLog.LogDebug("Image: " + i + " imgID=" + im.imgId + " x=" + r.x * outW +
                                       " y=" + r.y * outH + " w=" + r.width * outW +
                                       " h=" + r.height * outH + " padding=" + padding);
                    }
                }
            }
            else
            {
                if (LOG_LEVEL >= MBLogLevel.debug)
                {
                    Debug.Log("==================== REDOING PACKING ================");
                }
                bestRoot = null;
                rs       = _GetRects(imgWidthHeights, maxDimension, padding, newMinSizeX, newMinSizeY, masterImageSizeX, masterImageSizeY, out outW, out outH, recursionDepth + 1);
            }

            if (LOG_LEVEL >= MBLogLevel.debug)
            {
                MBLog.LogDebug("Done GetRects");
            }
            return(rs);
        }
Пример #3
0
        //------------------ 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);
        }
Пример #4
0
        public Rect[] GetRects(List <Vector2> imgWidthHeights, int maxDimension, int padding, out int outW, out int outH)
        {
            float area = 0;
            int   maxW = 0;
            int   maxH = 0;

            Image[] imgsToAdd = new Image[imgWidthHeights.Count];
            for (int i = 0; i < imgsToAdd.Length; i++)
            {
                Image im = imgsToAdd[i] = new Image(i, (int)imgWidthHeights[i].x, (int)imgWidthHeights[i].y, padding);
                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());
            }
//			List<Node> ns = new List<Node>();

            //explore the space to find a resonably efficient packing
            int sqrtArea    = (int)Mathf.Sqrt(area);
            int idealAtlasW = sqrtArea;
            int 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 = 1;
            }
            if (idealAtlasH == 0)
            {
                idealAtlasH = 1;
            }
            int stepW = (int)(idealAtlasW * .15f);
            int stepH = (int)(idealAtlasH * .15f);

            if (stepW == 0)
            {
                stepW = 1;
            }
            if (stepH == 0)
            {
                stepH = 1;
            }
//			bool doStepHeight = true;
//			bool successH = false;
            int numWIterations = 2;
            int steppedHeight  = idealAtlasH;

            while (numWIterations > 1 && steppedHeight < sqrtArea * 1000)
            {
                bool successW = false;
                numWIterations = 0;
                int steppedWidth = idealAtlasW;
                while (!successW && steppedWidth < sqrtArea * 1000)
                {
                    ProbeResult pr = new ProbeResult();
                    if (Probe(imgsToAdd, steppedWidth, steppedHeight, area, maxDimension, pr))
                    {
                        successW = true;
                        if (bestRoot == null)
                        {
                            bestRoot = pr;
                        }
                        else if (pr.GetScore() > bestRoot.GetScore())
                        {
                            bestRoot = pr;
                        }
                    }
                    else
                    {
                        numWIterations++;
                        steppedWidth += stepW;
                        if (LOG_LEVEL >= MB2_LogLevel.debug)
                        {
                            MB2_Log.LogDebug("increasing Width h=" + steppedHeight + " w=" + steppedWidth);
                        }
                    }
                }
                steppedHeight += stepH;
                if (LOG_LEVEL >= MB2_LogLevel.debug)
                {
                    MB2_Log.LogDebug("increasing Height h=" + steppedHeight + " w=" + steppedWidth);
                }
            }

            outW = 0;
            outH = 0;
            if (bestRoot == null)
            {
                return(null);
            }
            if (LOG_LEVEL >= MB2_LogLevel.debug)
            {
                MB2_Log.LogDebug("Best fit found: w=" + bestRoot.w + " h=" + bestRoot.h + " efficiency=" + bestRoot.efficiency + " squareness=" + bestRoot.squareness + " fits in max dimension=" + bestRoot.fitsInMaxSize);
            }
            outW = bestRoot.w;
            outH = bestRoot.h;
            List <Image> images = new List <Image>();

            flattenTree(bestRoot.root, images);
            images.Sort(new ImgIDComparer());
            if (images.Count != imgsToAdd.Length)
            {
                Debug.LogError("Result images not the same lentgh as source");
            }

            //scale images if too large
            float padX = (float)padding / (float)bestRoot.w;

            if (bestRoot.w > maxDimension)
            {
                padX = (float)padding / (float)maxDimension;
                float scaleFactor = (float)maxDimension / (float)bestRoot.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];
                    int   right = (int)((im.x + im.w) * scaleFactor);
                    im.x = (int)(scaleFactor * im.x);
                    im.w = right - im.x;
                    if (im.w == 0)
                    {
                        Debug.LogError("rounding scaled image w to zero");
                    }
                }
                outW = maxDimension;
            }

            float padY = (float)padding / (float)bestRoot.h;

            if (bestRoot.h > maxDimension)
            {
                padY = (float)padding / (float)maxDimension;
                float scaleFactor = (float)maxDimension / (float)bestRoot.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];
                    int   bottom = (int)((im.y + im.h) * scaleFactor);
                    im.y = (int)(scaleFactor * im.y);
                    im.h = bottom - im.y;
                    if (im.h == 0)
                    {
                        Debug.LogError("rounding scaled image h to zero");
                    }
                }
                outH = maxDimension;
            }

            Rect[] rs = new Rect[images.Count];
            for (int i = 0; i < images.Count; i++)
            {
                Image im = images[i];
                Rect  r  = rs[i] = new Rect((float)im.x / (float)outW + padX,
                                            (float)im.y / (float)outH + padY,
                                            (float)im.w / (float)outW - padX * 2,
                                            (float)im.h / (float)outH - padY * 2);
                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);
                }
            }

            if (LOG_LEVEL >= MB2_LogLevel.debug)
            {
                MB2_Log.LogDebug("Done GetRects");
            }
            return(rs);
        }
Пример #5
0
        //------------------ 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)
        {
            Debug.Log(string.Format("_GetRects 图片数量 ={0}, 最大尺寸X ={1}, 最小尺寸 X={2}, 最小尺寸 Y ={3}, masterImageSizeX={4}, masterImageSizeY={5}, 递归深度 = {6}",
                                    imgWidthHeights.Count, maxDimensionX, minImageSizeX, minImageSizeY, masterImageSizeX, masterImageSizeY, recursionDepth));
            if (recursionDepth > 10)
            {
                //最大递归深度设定为 10
                Debug.LogError("Maximum recursion depth reached. Couldn't find packing for these textures.");
                return(null);
            }
            float allImageTotalArea = 0;
            int   maxW = 0;
            int   maxH = 0;

            ImageAreaInAtlas[] imgsToAdd = new ImageAreaInAtlas[imgWidthHeights.Count];
            for (int i = 0; i < imgsToAdd.Length; i++)
            {
                int iw = (int)imgWidthHeights[i].x;
                int ih = (int)imgWidthHeights[i].y;

                ImageAreaInAtlas im = imgsToAdd[i] = new ImageAreaInAtlas(i, iw, ih, paddings[i], minImageSizeX, minImageSizeY);
                allImageTotalArea += im.w * im.h;
                maxW = Mathf.Max(maxW, im.w);
                maxH = Mathf.Max(maxH, im.h);
            }

            if ((float)maxH / (float)maxW > 2)
            {
                Array.Sort(imgsToAdd, new ImageHeightComparer());
            }
            else if ((float)maxH / (float)maxW < .5)
            {
                Array.Sort(imgsToAdd, new ImageWidthComparer());
            }
            else
            {
                Array.Sort(imgsToAdd, new ImageAreaComparer());
            }

            //explore the space to find a resonably efficient packing
            //探索图片空间以找到合理有效的包装
            int sqrtArea = (int)Mathf.Sqrt(allImageTotalArea);
            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(allImageTotalArea / maxW), maxH);
                }
                if (maxH > sqrtArea)
                {
                    idealAtlasW = Mathf.Max(Mathf.CeilToInt(allImageTotalArea / 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();
                    Debug.Log("Probing h=" + steppedHeight + " w=" + steppedWidth);

                    if (ProbeSingleAtlas(imgsToAdd, steppedWidth, steppedHeight, allImageTotalArea, maxDimensionX, maxDimensionY, pr))
                    {
                        successW = true;
                        if (bestRoot == null)
                        {
                            bestRoot = pr;
                        }
                        else if (pr.GetScore(atlasMustBePowerOfTwo) > bestRoot.GetScore(atlasMustBePowerOfTwo))
                        {
                            bestRoot = pr;
                        }
                    }
                    else
                    {
                        numWIterations++;
                        steppedWidth = SetStepWidthHeight(steppedWidth, stepW, maxDimensionX);
                        Debug.Log("增加 Width h=" + steppedHeight + " w=" + steppedWidth);
                    }
                }
                steppedHeight = SetStepWidthHeight(steppedHeight, stepH, maxDimensionY);
                Debug.Log("增加 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;
            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 <ImageAreaInAtlas> images = new List <ImageAreaInAtlas>();

            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))
            {
                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++)
                {
                    ImageAreaInAtlas 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;
                    Debug.Log("Image: " + i + " imgID=" + im.imgId + " x=" + r.x * outW +
                              " y=" + r.y * outH + " w=" + r.width * outW +
                              " h=" + r.height * outH + " padding=" + paddings[i]);
                }
                res.CalcUsedWidthAndHeight();
                return(res);
            }
            else
            {
                Debug.Log("==================== REDOING PACKING ================");
                //root = null;
                return(_GetRectsSingleAtlas(imgWidthHeights, paddings, maxDimensionX, maxDimensionY, newMinSizeX, newMinSizeY, masterImageSizeX, masterImageSizeY, recursionDepth + 1));
            }


            //Debug.Log(String.Format("Done GetRects atlasW={0} atlasH={1}", bestRoot.w, bestRoot.h));

            //return res;
        }