1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #pragma once |
4 | |
5 | #include "Prerequisites/BsPrerequisitesUtil.h" |
6 | #include "Math/BsVector2.h" |
7 | |
8 | namespace bs |
9 | { |
10 | /** @addtogroup Image |
11 | * @{ |
12 | */ |
13 | |
14 | /** Organizes a set of textures into a single larger texture (an atlas) by minimizing empty space. */ |
15 | class BS_UTILITY_EXPORT TextureAtlasLayout |
16 | { |
17 | /** Represent a single node in the texture atlas binary tree. */ |
18 | class TexAtlasNode |
19 | { |
20 | public: |
21 | constexpr TexAtlasNode() = default; |
22 | constexpr TexAtlasNode(UINT32 x, UINT32 y, UINT32 width, UINT32 height) |
23 | : x(x), y(y), width(width), height(height) |
24 | { } |
25 | |
26 | UINT32 x = 0; |
27 | UINT32 y = 0; |
28 | UINT32 width = 0; |
29 | UINT32 height = 0; |
30 | UINT32 children[2] { std::numeric_limits<UINT32>::max(), std::numeric_limits<UINT32>::max() }; |
31 | bool nodeFull = false; |
32 | }; |
33 | |
34 | public: |
35 | TextureAtlasLayout() = default; |
36 | |
37 | /** |
38 | * Constructs a new texture atlas layout with the provided parameters. |
39 | * |
40 | * @param[in] width Initial width of the atlas texture. |
41 | * @param[in] height Initial height of the atlas texture. |
42 | * @param[in] maxWidth Maximum width the atlas texture is allowed to grow to, when elements don't fit. |
43 | * @param[in] maxHeight Maximum height the atlas texture is allowed to grow to, when elements don't fit. |
44 | * @param[in] pow2 When true the resulting atlas size will always be a power of two. |
45 | */ |
46 | TextureAtlasLayout(UINT32 width, UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2 = false) |
47 | : mInitialWidth(width), mInitialHeight(height), mWidth(width), mHeight(height), mPow2(pow2) |
48 | { |
49 | mNodes.push_back(TexAtlasNode(0, 0, maxWidth, maxHeight)); |
50 | } |
51 | |
52 | /** |
53 | * Attempts to add a new element in the layout. Elements should be added to the atlas from largest to smallest, |
54 | * otherwise a non-optimal layout is likely to be generated. |
55 | * |
56 | * @param[in] width Width of the new element, in pixels. |
57 | * @param[in] height Height of the new element, in pixels. |
58 | * @param[out] x Horizontal position of the new element within the atlas. Only valid if method returns true. |
59 | * @param[out] y Vertical position of the new element within the atlas. Only valid if method returns true. |
60 | * @return True if the element was added to the atlas, false if the element doesn't fit. |
61 | */ |
62 | bool addElement(UINT32 width, UINT32 height, UINT32& x, UINT32& y); |
63 | |
64 | /** Removes all entries from the layout. */ |
65 | void clear(); |
66 | |
67 | /** Checks have any elements been added to the layout. */ |
68 | bool isEmpty() const { return mNodes.size() == 1; } |
69 | |
70 | /** Returns the width of the atlas texture, in pixels. */ |
71 | UINT32 getWidth() const { return mWidth; } |
72 | |
73 | /** Returns the height of the atlas texture, in pixels. */ |
74 | UINT32 getHeight() const { return mHeight; } |
75 | |
76 | private: |
77 | /* |
78 | * Attempts to add a new element to the specified layout node. |
79 | * |
80 | * @param[in] nodeIdx Index of the node to which to add the element. |
81 | * @param[in] width Width of the new element, in pixels. |
82 | * @param[in] height Height of the new element, in pixels. |
83 | * @param[out] x Horizontal position of the new element within the atlas. Only valid if method |
84 | * returns true. |
85 | * @param[out] y Vertical position of the new element within the atlas. Only valid if method returns |
86 | * true. |
87 | * @param[in] allowGrowth When true, the width/height of the atlas will be allowed to grow to fit the element. |
88 | * @return True if the element was added to the atlas, false if the element doesn't fit. |
89 | */ |
90 | bool addToNode(UINT32 nodeIdx, UINT32 width, UINT32 height, UINT32& x, UINT32& y, bool allowGrowth); |
91 | |
92 | UINT32 mInitialWidth = 0; |
93 | UINT32 mInitialHeight = 0; |
94 | UINT32 mWidth = 0; |
95 | UINT32 mHeight = 0; |
96 | bool mPow2 = false; |
97 | |
98 | Vector<TexAtlasNode> mNodes; |
99 | }; |
100 | |
101 | /** Utility class used for texture atlas layouts. */ |
102 | class BS_UTILITY_EXPORT TextureAtlasUtility |
103 | { |
104 | public: |
105 | /** |
106 | * Represents a single element used as in input to TextureAtlasUtility. Usually represents a single texture. |
107 | * |
108 | * @note input is required to be filled in before passing it to TextureAtlasUtility. |
109 | * @note output will be filled after a call to TextureAtlasUtility::createAtlasLayout(). |
110 | */ |
111 | struct Element |
112 | { |
113 | struct |
114 | { |
115 | UINT32 width, height; |
116 | } input; |
117 | |
118 | struct |
119 | { |
120 | UINT32 x, y; |
121 | UINT32 idx; |
122 | INT32 page; |
123 | } output; |
124 | }; |
125 | |
126 | /** Describes a single page of the texture atlas. */ |
127 | struct Page |
128 | { |
129 | UINT32 width, height; |
130 | }; |
131 | |
132 | /** |
133 | * Creates an optimal texture layout by packing texture elements in order to end up with as little empty space |
134 | * as possible. Algorithm will split elements over multiple textures if they don't fit in a single texture. |
135 | * |
136 | * @param[in] elements Elements to process. They need to have their input structures filled in, |
137 | * and this method will fill output when it returns. |
138 | * @param[in] width Initial width of the atlas texture. |
139 | * @param[in] height Initial height of the atlas texture. |
140 | * @param[in] maxWidth Maximum width the atlas texture is allowed to grow to, when elements don't fit. |
141 | * @param[in] maxHeight Maximum height the atlas texture is allowed to grow to, when elements don't fit. |
142 | * @param[in] pow2 When true the resulting atlas size will always be a power of two. |
143 | * @return One or more descriptors that determine the size of the final atlas textures. |
144 | * Texture elements will reference these pages with their output.page parameter. |
145 | */ |
146 | static Vector<Page> createAtlasLayout(Vector<Element>& elements, UINT32 width, UINT32 height, UINT32 maxWidth, |
147 | UINT32 maxHeight, bool pow2 = false); |
148 | }; |
149 | |
150 | /** @} */ |
151 | } |
152 | |