BsTextureAtlasLayout.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Image/BsTextureAtlasLayout.h"
  4. #include "Debug/BsDebug.h"
  5. #include "Utility/BsBitwise.h"
  6. namespace bs
  7. {
  8. TextureAtlasLayout::TexAtlasNode::TexAtlasNode()
  9. :x(0), y(0), width(0), height(0), children{ (UINT32)-1, (UINT32)-1 }, nodeFull(false)
  10. { }
  11. TextureAtlasLayout::TexAtlasNode::TexAtlasNode(UINT32 x, UINT32 y, UINT32 width, UINT32 height)
  12. : x(x), y(y), width(width), height(height), children{ (UINT32)-1, (UINT32)-1 }, nodeFull(false)
  13. { }
  14. TextureAtlasLayout::TextureAtlasLayout()
  15. : mInitialWidth(0), mInitialHeight(0), mWidth(0), mHeight(0), mMaxWidth(0), mMaxHeight(0), mPow2(false)
  16. { }
  17. TextureAtlasLayout::TextureAtlasLayout(UINT32 width, UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2)
  18. : mInitialWidth(width), mInitialHeight(height), mWidth(width), mHeight(height), mMaxWidth(maxWidth)
  19. , mMaxHeight(maxHeight), mPow2(pow2)
  20. {
  21. mNodes.push_back(TexAtlasNode(0, 0, maxWidth, maxHeight));
  22. }
  23. bool TextureAtlasLayout::addElement(UINT32 width, UINT32 height, UINT32& x, UINT32& y)
  24. {
  25. if(width == 0 || height == 0)
  26. {
  27. x = 0;
  28. y = 0;
  29. return true;
  30. }
  31. // Try adding without expanding, if that fails try to expand
  32. if(!addToNode(0, width, height, x, y, false))
  33. {
  34. if (!addToNode(0, width, height, x, y, true))
  35. return false;
  36. }
  37. // Update size to cover all nodes
  38. if(mPow2)
  39. {
  40. mWidth = std::max(mWidth, Bitwise::nextPow2(x + width));
  41. mHeight = std::max(mHeight, Bitwise::nextPow2(y + height));
  42. }
  43. else
  44. {
  45. mWidth = std::max(mWidth, x + width);
  46. mHeight = std::max(mHeight, y + height);
  47. }
  48. return true;
  49. }
  50. void TextureAtlasLayout::clear()
  51. {
  52. mNodes.clear();
  53. mNodes.push_back(TexAtlasNode(0, 0, mWidth, mHeight));
  54. mWidth = mInitialWidth;
  55. mHeight = mInitialHeight;
  56. }
  57. bool TextureAtlasLayout::addToNode(UINT32 nodeIdx, UINT32 width, UINT32 height, UINT32& x, UINT32& y, bool allowGrowth)
  58. {
  59. TexAtlasNode* node = &mNodes[nodeIdx];
  60. float aspect = node->width / (float)node->height;
  61. if (node->children[0] != (UINT32)-1)
  62. {
  63. if (addToNode(node->children[0], width, height, x, y, allowGrowth))
  64. return true;
  65. return addToNode(node->children[1], width, height, x, y, allowGrowth);
  66. }
  67. else
  68. {
  69. if (node->nodeFull)
  70. return false;
  71. if (width > node->width || height > node->height)
  72. return false;
  73. if(!allowGrowth)
  74. {
  75. if (node->x + width > mWidth || node->y + height > mHeight)
  76. return false;
  77. }
  78. if (width == node->width && height == node->height)
  79. {
  80. x = node->x;
  81. y = node->y;
  82. node->nodeFull = true;
  83. return true;
  84. }
  85. float dw = (float)(node->width - width);
  86. float dh = (node->height - height) * aspect;
  87. UINT32 nextChildIdx = (UINT32)mNodes.size();
  88. node->children[0] = nextChildIdx;
  89. node->children[1] = nextChildIdx + 1;
  90. TexAtlasNode nodeCopy = *node;
  91. node = nullptr; // Undefined past this point
  92. if (dw > dh)
  93. {
  94. mNodes.emplace_back(nodeCopy.x, nodeCopy.y, width, nodeCopy.height);
  95. mNodes.emplace_back(nodeCopy.x + width, nodeCopy.y, nodeCopy.width - width, nodeCopy.height);
  96. }
  97. else
  98. {
  99. mNodes.emplace_back(nodeCopy.x, nodeCopy.y, nodeCopy.width, height);
  100. mNodes.emplace_back(nodeCopy.x, nodeCopy.y + height, nodeCopy.width, nodeCopy.height - height);
  101. }
  102. return addToNode(nodeCopy.children[0], width, height, x, y, allowGrowth);
  103. }
  104. }
  105. Vector<TextureAtlasUtility::Page> TextureAtlasUtility::createAtlasLayout(Vector<Element>& elements, UINT32 width,
  106. UINT32 height, UINT32 maxWidth, UINT32 maxHeight, bool pow2)
  107. {
  108. for (size_t i = 0; i < elements.size(); i++)
  109. {
  110. elements[i].output.idx = (UINT32)i; // Preserve original index before sorting
  111. elements[i].output.page = -1;
  112. }
  113. std::sort(elements.begin(), elements.end(),
  114. [](const Element& a, const Element& b)
  115. {
  116. return a.input.width * a.input.height > b.input.width * b.input.height;
  117. });
  118. Vector<TextureAtlasLayout> layouts;
  119. UINT32 remainingCount = (UINT32)elements.size();
  120. while (remainingCount > 0)
  121. {
  122. layouts.push_back(TextureAtlasLayout(width, height, maxWidth, maxHeight, pow2));
  123. TextureAtlasLayout& curLayout = layouts.back();
  124. // Find largest unassigned element that fits
  125. UINT32 sizeLimit = std::numeric_limits<UINT32>::max();
  126. while (true)
  127. {
  128. UINT32 largestId = -1;
  129. // Assumes elements are sorted from largest to smallest
  130. for (UINT32 i = 0; i < (UINT32)elements.size(); i++)
  131. {
  132. if (elements[i].output.page == -1)
  133. {
  134. UINT32 size = elements[i].input.width * elements[i].input.height;
  135. if (size < sizeLimit)
  136. {
  137. largestId = i;
  138. break;
  139. }
  140. }
  141. }
  142. if (largestId == (UINT32)-1)
  143. break; // Nothing fits, start a new page
  144. Element& element = elements[largestId];
  145. // Check if an element is too large to ever fit
  146. if(element.input.width > maxWidth || element.input.height > maxHeight)
  147. {
  148. LOGWRN("Some of the provided elements don't fit in an atlas of provided size. Returning empty array of pages.");
  149. return Vector<Page>();
  150. }
  151. if (curLayout.addElement(element.input.width, element.input.height, element.output.x, element.output.y))
  152. {
  153. element.output.page = (UINT32)layouts.size() - 1;
  154. remainingCount--;
  155. }
  156. else
  157. sizeLimit = element.input.width * element.input.height;
  158. }
  159. }
  160. Vector<Page> pages;
  161. for (auto& layout : layouts)
  162. pages.push_back({ layout.getWidth(), layout.getHeight() });
  163. return pages;
  164. }
  165. }