Esempio n. 1
0
        /// <summary>
        /// Packs the provided texture elementsToPack into <see cref="AtlasTextureLayouts"/>, given the provided heuristic algorithm.
        /// </summary>
        /// <param name="textureElements">The texture elementsToPack to pack</param>
        /// <param name="algorithm">Packing algorithm to use</param>
        /// <returns>True indicates all textures could be packed; False otherwise</returns>
        private bool PackTextures(List <AtlasTextureElement> textureElements, TexturePackingMethod algorithm)
        {
            var elementsToPack = textureElements;

            if (elementsToPack.Count == 0) // always successful if there is no element to pack (note we do not create a layout)
            {
                return(true);
            }

            do
            {
                // Do not create the atlas if all the elements are "empty". We don't want empty atlas.
                if (textureElements.All(e => e.SourceRegion.IsEmpty()))
                {
                    return(true);
                }

                List <AtlasTextureElement> remainingElements;
                var atlasLayout = CreateBestAtlasLayout(elementsToPack, algorithm, out remainingElements);

                // Check if at least one element could be packed in the texture.
                if (elementsToPack.Count == remainingElements.Count)
                {
                    return(false);
                }

                elementsToPack = remainingElements;
                atlasTextureLayouts.Add(atlasLayout);
            }while (AllowMultipack && elementsToPack.Count > 0);

            return(elementsToPack.Count == 0);
        }
Esempio n. 2
0
        /// <summary>
        /// Packs input elements using the MaxRects algorithm.
        /// Note that any element that could be packed is removed from the elementsToPack collection.
        /// </summary>
        /// <param name="elementsToPack">a list of rectangles to be packed</param>
        /// <param name="method">MaxRects heuristic method which default value is BestShortSideFit</param>
        public void PackRectangles(List <AtlasTextureElement> elementsToPack, TexturePackingMethod method = TexturePackingMethod.BestShortSideFit)
        {
            var bestRectangle = new RotableRectangle();

            // Prune all the empty elements (elements with null region) from the list of elements to pack
            // Reason: reduce the size of the atlas and wrap/mirror/clamp border mode are undetermined for empty elements.
            for (int i = elementsToPack.Count - 1; i >= 0; --i)
            {
                if (elementsToPack[i].SourceRegion.IsEmpty())
                {
                    elementsToPack.RemoveAt(i);
                }
            }

            // Pack the elements.
            while (elementsToPack.Count > 0)
            {
                var bestScore1 = int.MaxValue;
                var bestScore2 = int.MaxValue;

                var bestRectangleIndex = -1;

                for (var i = 0; i < elementsToPack.Count; ++i)
                {
                    int score1;
                    int score2;
                    var element         = elementsToPack[i];
                    var width           = element.SourceRegion.Width + 2 * element.BorderSize;
                    var height          = element.SourceRegion.Height + 2 * element.BorderSize;
                    var pickedRectangle = ChooseTargetPosition(width, height, method, out score1, out score2);

                    if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))
                    // Found the new best free region to hold a rectangle
                    {
                        bestScore1         = score1;
                        bestScore2         = score2;
                        bestRectangleIndex = i;
                        bestRectangle      = pickedRectangle;
                    }
                }

                // Could not find any free region to hold a rectangle, terminate packing process
                if (bestRectangleIndex == -1)
                {
                    break;
                }

                // Update the free space of the packer
                TakeSpaceForRectangle(bestRectangle);

                // Update the packed element
                var packedElement = elementsToPack[bestRectangleIndex];
                packedElement.DestinationRegion = bestRectangle;

                // Update the packed and remaining element lists
                packedElements.Add(packedElement);
                elementsToPack.RemoveAt(bestRectangleIndex);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Packs input elements using the MaxRects algorithm.
        /// Note that any element that could be packed is removed from the elementsToPack collection.
        /// </summary>
        /// <param name="elementsToPack">a list of rectangles to be packed</param>
        /// <param name="method">MaxRects heuristic method which default value is BestShortSideFit</param>
        public void PackRectangles(List<AtlasTextureElement> elementsToPack, TexturePackingMethod method = TexturePackingMethod.BestShortSideFit)
        {
            var bestRectangle = new RotableRectangle();

            // Prune all the empty elements (elements with null region) from the list of elements to pack 
            // Reason: reduce the size of the atlas and wrap/mirror/clamp border mode are undetermined for empty elements.
            for (int i = elementsToPack.Count-1; i >= 0; --i)
            {
                if (elementsToPack[i].SourceRegion.IsEmpty())
                    elementsToPack.RemoveAt(i);
            }

            // Pack the elements.
            while (elementsToPack.Count > 0)
            {
                var bestScore1 = int.MaxValue;
                var bestScore2 = int.MaxValue;

                var bestRectangleIndex = -1;

                for (var i = 0; i < elementsToPack.Count; ++i)
                {
                    int score1;
                    int score2;
                    var element = elementsToPack[i];
                    var width = element.SourceRegion.Width + 2*element.BorderSize;
                    var height = element.SourceRegion.Height+ 2*element.BorderSize;
                    var pickedRectangle = ChooseTargetPosition(width, height, method, out score1, out score2);

                    if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))
                    // Found the new best free region to hold a rectangle
                    {
                        bestScore1 = score1;
                        bestScore2 = score2;
                        bestRectangleIndex = i;
                        bestRectangle = pickedRectangle;
                    }
                }

                // Could not find any free region to hold a rectangle, terminate packing process
                if (bestRectangleIndex == -1) break;

                // Update the free space of the packer
                TakeSpaceForRectangle(bestRectangle);

                // Update the packed element
                var packedElement = elementsToPack[bestRectangleIndex];
                packedElement.DestinationRegion = bestRectangle;

                // Update the packed and remaining element lists
                packedElements.Add(packedElement);
                elementsToPack.RemoveAt(bestRectangleIndex);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Determines a target position to place a given rectangle by a given heuristic method
        /// </summary>
        /// <param name="method">A heuristic placement method</param>
        /// <param name="score1">First score</param>
        /// <param name="score2">Second score</param>
        /// <param name="width">The width of the element to place</param>
        /// <param name="height">The height of the element to place</param>
        /// <returns></returns>
        private RotableRectangle ChooseTargetPosition(int width, int height, TexturePackingMethod method, out int score1, out int score2)
        {
            var bestNode = new RotableRectangle();

            // null sized rectangle fits everywhere with a perfect score.
            if (width == 0 || height == 0)
            {
                score1 = 0;
                score2 = 0;
                return(bestNode);
            }

            score1 = int.MaxValue;
            score2 = int.MaxValue;

            switch (method)
            {
            case TexturePackingMethod.BestShortSideFit:
                bestNode = FindPositionForNewNodeBestShortSideFit(width, height, out score1, ref score2);
                break;

            case TexturePackingMethod.BottomLeftRule:
                bestNode = FindPositionForNewNodeBottomLeft(width, height, out score1, ref score2);
                break;

            case TexturePackingMethod.ContactPointRule:
                bestNode = FindPositionForNewNodeContactPoint(width, height, out score1);
                score1  *= -1;
                break;

            case TexturePackingMethod.BestLongSideFit:
                bestNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, out score1);
                break;

            case TexturePackingMethod.BestAreaFit:
                bestNode = FindPositionForNewNodeBestAreaFit(width, height, out score1, ref score2);
                break;

            default:
                throw new ArgumentOutOfRangeException("method");
            }

            // there is no available space big enough to fit the rectangle
            if (bestNode.Height == 0)
            {
                score1 = int.MaxValue;
                score2 = int.MaxValue;
            }

            return(bestNode);
        }
Esempio n. 5
0
        /// <summary>
        /// Create the best atlas layout possible given the elementsToPack to pack, the algorithm and the atlas maximum size.
        /// Note: when all the elementsToPack cannot fit into the texture, it tries to pack as much as possible of them.
        /// </summary>
        /// <returns>False if</returns>
        private AtlasTextureLayout CreateBestAtlasLayout(List <AtlasTextureElement> elementsToPack, TexturePackingMethod algorithm, out List <AtlasTextureElement> remainingElements)
        {
            remainingElements = elementsToPack;

            var textureAtlas = new AtlasTextureLayout();

            var bestElementPackedCount = int.MaxValue;

            // Generate sub size array
            var subSizeArray = CreateSubSizeArray(atlasMaxSize.X, atlasMaxSize.Y, 512, 512);

            foreach (var subArray in subSizeArray)
            {
                var currentRemaingElements = new List <AtlasTextureElement>(elementsToPack);

                // Reset packer state
                maxRectPacker.Initialize(subArray.Width, subArray.Height, AllowRotation);

                // Pack
                maxRectPacker.PackRectangles(currentRemaingElements, algorithm);

                // Find true size from packed regions
                var packedSize = CalculatePackedRectanglesBound(maxRectPacker.PackedElements);

                // Alter the size of atlas so that it is a power of two
                if (!AllowNonPowerOfTwo)
                {
                    packedSize.Width  = MathUtil.NextPowerOfTwo(packedSize.Width);
                    packedSize.Height = MathUtil.NextPowerOfTwo(packedSize.Height);

                    if (packedSize.Width > subArray.Width || packedSize.Height > subArray.Height)
                    {
                        continue;
                    }
                }

                if (currentRemaingElements.Count >= bestElementPackedCount)
                {
                    continue;
                }

                // Found new best pack, cache it
                bestElementPackedCount = currentRemaingElements.Count;

                // Resize texture atlas
                textureAtlas.Width  = packedSize.Width;
                textureAtlas.Height = packedSize.Height;

                textureAtlas.Textures.Clear();

                // Store all packed regions into Atlas
                foreach (var element in maxRectPacker.PackedElements)
                {
                    textureAtlas.Textures.Add(element.Clone());
                }

                remainingElements = currentRemaingElements;
            }

            return(textureAtlas);
        }
Esempio n. 6
0
        /// <summary>
        /// Create the best atlas layout possible given the elementsToPack to pack, the algorithm and the atlas maximum size.
        /// Note: when all the elementsToPack cannot fit into the texture, it tries to pack as much as possible of them.
        /// </summary>
        /// <returns>False if</returns>
        private AtlasTextureLayout CreateBestAtlasLayout(List<AtlasTextureElement> elementsToPack, TexturePackingMethod algorithm, out List<AtlasTextureElement> remainingElements)
        {
            remainingElements = elementsToPack;

            var textureAtlas = new AtlasTextureLayout();

            var bestElementPackedCount = int.MaxValue;

            // Generate sub size array
            var subSizeArray = CreateSubSizeArray(atlasMaxSize.X, atlasMaxSize.Y, 512, 512);

            foreach (var subArray in subSizeArray)
            {
                var currentRemaingElements = new List<AtlasTextureElement>(elementsToPack);

                // Reset packer state
                maxRectPacker.Initialize(subArray.Width, subArray.Height, AllowRotation);

                // Pack
                maxRectPacker.PackRectangles(currentRemaingElements, algorithm);

                // Find true size from packed regions
                var packedSize = CalculatePackedRectanglesBound(maxRectPacker.PackedElements);

                // Alter the size of atlas so that it is a power of two
                if (!AllowNonPowerOfTwo)
                {
                    packedSize.Width = MathUtil.NextPowerOfTwo(packedSize.Width);
                    packedSize.Height = MathUtil.NextPowerOfTwo(packedSize.Height);

                    if (packedSize.Width > subArray.Width || packedSize.Height > subArray.Height)
                        continue;
                }

                if (currentRemaingElements.Count >= bestElementPackedCount)
                    continue;

                // Found new best pack, cache it
                bestElementPackedCount = currentRemaingElements.Count;

                // Resize texture atlas
                textureAtlas.Width = packedSize.Width;
                textureAtlas.Height = packedSize.Height;

                textureAtlas.Textures.Clear();

                // Store all packed regions into Atlas
                foreach (var element in maxRectPacker.PackedElements)
                    textureAtlas.Textures.Add(element.Clone());

                remainingElements = currentRemaingElements;
            }

            return textureAtlas;
        }
Esempio n. 7
0
        /// <summary>
        /// Packs the provided texture elementsToPack into <see cref="AtlasTextureLayouts"/>, given the provided heuristic algorithm.
        /// </summary>
        /// <param name="textureElements">The texture elementsToPack to pack</param>
        /// <param name="algorithm">Packing algorithm to use</param>
        /// <returns>True indicates all textures could be packed; False otherwise</returns>
        private bool PackTextures(List<AtlasTextureElement> textureElements, TexturePackingMethod algorithm)
        {
            var elementsToPack = textureElements;
            if (elementsToPack.Count == 0) // always successful if there is no element to pack (note we do not create a layout)
                return true;

            do
            {
                // Do not create the atlas if all the elements are "empty". We don't want empty atlas.
                if (textureElements.All(e => e.SourceRegion.IsEmpty()))
                    return true;

                List<AtlasTextureElement> remainingElements;
                var atlasLayout = CreateBestAtlasLayout(elementsToPack, algorithm, out remainingElements);

                // Check if at least one element could be packed in the texture.
                if (elementsToPack.Count == remainingElements.Count)
                    return false;

                elementsToPack = remainingElements;
                atlasTextureLayouts.Add(atlasLayout);
            }
            while (AllowMultipack && elementsToPack.Count > 0);

            return elementsToPack.Count == 0;
        }
Esempio n. 8
0
        /// <summary>
        /// Determines a target position to place a given rectangle by a given heuristic method
        /// </summary>
        /// <param name="method">A heuristic placement method</param>
        /// <param name="score1">First score</param>
        /// <param name="score2">Second score</param>
        /// <param name="width">The width of the element to place</param>
        /// <param name="height">The height of the element to place</param>
        /// <returns></returns>
        private RotableRectangle ChooseTargetPosition(int width, int height, TexturePackingMethod method, out int score1, out int score2)
        {
            var bestNode = new RotableRectangle();

            // null sized rectangle fits everywhere with a perfect score.
            if (width == 0 || height == 0)
            {
                score1 = 0;
                score2 = 0;
                return bestNode;
            }

            score1 = int.MaxValue;
            score2 = int.MaxValue;

            switch (method)
            {
                case TexturePackingMethod.BestShortSideFit:
                    bestNode = FindPositionForNewNodeBestShortSideFit(width, height, out score1, ref score2);
                    break;
                case TexturePackingMethod.BottomLeftRule:
                    bestNode = FindPositionForNewNodeBottomLeft(width, height, out score1, ref score2);
                    break;
                case TexturePackingMethod.ContactPointRule:
                    bestNode = FindPositionForNewNodeContactPoint(width, height, out score1);
                    score1 *= -1;
                    break;
                case TexturePackingMethod.BestLongSideFit:
                    bestNode = FindPositionForNewNodeBestLongSideFit(width, height, ref score2, out score1);
                    break;
                case TexturePackingMethod.BestAreaFit:
                    bestNode = FindPositionForNewNodeBestAreaFit(width, height, out score1, ref score2);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("method");
            }

            // there is no available space big enough to fit the rectangle
            if (bestNode.Height == 0)
            {
                score1 = int.MaxValue;
                score2 = int.MaxValue;
            }

            return bestNode;
        }