BsTexAtlasGenerator.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. //__________________________ Banshee Project - A modern game development toolkit _________________________________//
  2. //_____________________________________ www.banshee-project.com __________________________________________________//
  3. //________________________ Copyright (c) 2014 Marko Pintera. All rights reserved. ________________________________//
  4. #include "BsTexAtlasGenerator.h"
  5. #include "BsDebug.h"
  6. namespace BansheeEngine
  7. {
  8. class TexAtlasNode
  9. {
  10. public:
  11. TexAtlasNode()
  12. :x(0), y(0), width(0), height(0), children(nullptr), nodeFull(false)
  13. { }
  14. TexAtlasNode(UINT32 _x, UINT32 _y, UINT32 _width, UINT32 _height)
  15. :x(_x), y(_y), width(_width), height(_height), children(nullptr), nodeFull(false)
  16. { }
  17. ~TexAtlasNode()
  18. {
  19. if(children != nullptr)
  20. bs_deleteN<ScratchAlloc>(children, 2);
  21. int myVal = *bs_new<int, GenAlloc>();
  22. float myVal2 = *bs_new<float, GenAlloc>();
  23. }
  24. UINT32 x, y, width, height;
  25. TexAtlasNode* children;
  26. bool nodeFull;
  27. bool insert(TexAtlasElementDesc& element)
  28. {
  29. float aspect = width / (float)height;
  30. return insert(element, aspect);
  31. }
  32. bool insert(TexAtlasElementDesc& element, float aspect)
  33. {
  34. if (children != nullptr)
  35. {
  36. if (children[0].insert(element))
  37. return true;
  38. return children[1].insert(element);
  39. }
  40. else
  41. {
  42. if(nodeFull)
  43. return false;
  44. if (element.input.width > width || element.input.height > height)
  45. return false;
  46. if (element.input.width == width && element.input.height == height)
  47. {
  48. element.output.x = x;
  49. element.output.y = y;
  50. nodeFull = true;
  51. return true;
  52. }
  53. float dw = (float)(width - element.input.width);
  54. float dh = (height - element.input.height) * aspect;
  55. children = bs_newN<TexAtlasNode, ScratchAlloc>(2);
  56. if (dw > dh)
  57. {
  58. children[0].x = x;
  59. children[0].y = y;
  60. children[0].width = element.input.width;
  61. children[0].height = height;
  62. children[1].x = x + element.input.width;
  63. children[1].y = y;
  64. children[1].width = width - element.input.width;
  65. children[1].height = height;
  66. }
  67. else
  68. {
  69. children[0].x = x;
  70. children[0].y = y;
  71. children[0].width = width;
  72. children[0].height = element.input.height;
  73. children[1].x = x;
  74. children[1].y = y + element.input.height;
  75. children[1].width = width;
  76. children[1].height = height - element.input.height;
  77. }
  78. return children[0].insert(element);
  79. }
  80. }
  81. };
  82. TexAtlasGenerator::TexAtlasGenerator(bool square, UINT32 maxTexWidth, UINT32 maxTexHeight, bool fixedSize)
  83. :mSquare(square), mMaxTexWidth(maxTexWidth), mMaxTexHeight(maxTexHeight), mFixedSize(fixedSize)
  84. {
  85. if(square)
  86. {
  87. if(maxTexWidth > maxTexHeight)
  88. maxTexWidth = maxTexHeight;
  89. if(maxTexHeight > maxTexWidth)
  90. maxTexHeight = maxTexWidth;
  91. }
  92. }
  93. Vector<TexAtlasPageDesc> TexAtlasGenerator::createAtlasLayout(Vector<TexAtlasElementDesc>& elements) const
  94. {
  95. for(size_t i = 0; i < elements.size(); i++)
  96. elements[i].output.page = -1;
  97. //sortBySize(elements);
  98. int numPages = generatePagesForSize(elements, mMaxTexWidth, mMaxTexHeight);
  99. if(numPages == -1)
  100. {
  101. LOGWRN("Some of the provided elements don't fit in an atlas of provided size. Returning empty array of pages.");
  102. return Vector<TexAtlasPageDesc>();
  103. }
  104. if(numPages == 0)
  105. return Vector<TexAtlasPageDesc>();
  106. UINT32 lastPageWidth = mMaxTexWidth;
  107. UINT32 lastPageHeight = mMaxTexHeight;
  108. INT32 lastPageIdx = numPages - 1;
  109. // If size isn't fixed, try to reduce the size of the last page
  110. if(!mFixedSize)
  111. {
  112. while (true)
  113. {
  114. if (lastPageWidth <= 1 || lastPageHeight <= 1)
  115. break;
  116. int newLastPageWidth = lastPageWidth;
  117. int newLastPageHeight = lastPageHeight;
  118. if(mSquare)
  119. {
  120. if (newLastPageWidth > newLastPageHeight)
  121. newLastPageWidth /= 2;
  122. else
  123. newLastPageHeight /= 2;
  124. }
  125. else
  126. {
  127. if (newLastPageWidth > newLastPageHeight)
  128. newLastPageWidth /= 2;
  129. else
  130. newLastPageHeight /= 2;
  131. }
  132. // Clear page indexes so we know which pages to process
  133. for(size_t i = 0; i < elements.size(); i++)
  134. {
  135. if(elements[i].output.page >= lastPageIdx)
  136. elements[i].output.page = -1;
  137. }
  138. if(generatePagesForSize(elements, newLastPageWidth, newLastPageHeight, lastPageIdx) == 1)
  139. {
  140. lastPageWidth = newLastPageWidth;
  141. lastPageHeight = newLastPageHeight;
  142. }
  143. else
  144. {
  145. // We're done but we need to re-do all the pages with the last valid size
  146. for(size_t i = 0; i < elements.size(); i++)
  147. {
  148. if(elements[i].output.page >= lastPageIdx)
  149. elements[i].output.page = -1;
  150. }
  151. generatePagesForSize(elements, lastPageWidth, lastPageHeight, lastPageIdx);
  152. break;
  153. }
  154. }
  155. }
  156. // Handle degenerate case
  157. for(size_t i = 0; i < elements.size(); i++)
  158. {
  159. if(elements[i].output.page == -1 && elements[i].input.width == 0 && elements[i].input.height == 0)
  160. {
  161. elements[i].output.x = 0;
  162. elements[i].output.y = 0;
  163. }
  164. }
  165. // Create page descriptors and return
  166. Vector<TexAtlasPageDesc> pages;
  167. for(int i = 0; i < numPages - 1; i++)
  168. {
  169. TexAtlasPageDesc pageDesc;
  170. pageDesc.width = mMaxTexWidth;
  171. pageDesc.height = mMaxTexHeight;
  172. pages.push_back(pageDesc);
  173. }
  174. TexAtlasPageDesc lastPageDesc;
  175. lastPageDesc.width = lastPageWidth;
  176. lastPageDesc.height = lastPageHeight;
  177. pages.push_back(lastPageDesc);
  178. return pages;
  179. }
  180. int TexAtlasGenerator::generatePagesForSize(Vector<TexAtlasElementDesc>& elements, UINT32 width, UINT32 height, UINT32 startPage) const
  181. {
  182. if(elements.size() == 0)
  183. return 0;
  184. int currentPage = startPage;
  185. int numPages = 0;
  186. while (true)
  187. {
  188. int largestTexId = findLargestTextureWithoutPage(elements); // Start with the largest available texture
  189. if (largestTexId == -1) // No more textures, we're done
  190. return numPages;
  191. TexAtlasElementDesc& currentElem = elements[largestTexId];
  192. // If the texture is larger than the atlas size then it can never fit
  193. while (width < currentElem.input.width || height < currentElem.input.height)
  194. return -1;
  195. TexAtlasNode atlasNode(0, 0, width, height);
  196. atlasNode.insert(elements[largestTexId]);
  197. elements[largestTexId].output.page = currentPage;
  198. // Now that the first (largest) texture has been added, do the same for every other texture until atlas is full
  199. while (true)
  200. {
  201. int addedTextureId = addLargestTextureWithoutPageThatFits(elements, atlasNode); // Try to add next largest texture
  202. if (addedTextureId == -1)
  203. break;
  204. elements[addedTextureId].output.page = currentPage;
  205. }
  206. currentPage++;
  207. numPages++;
  208. }
  209. }
  210. int TexAtlasGenerator::addLargestTextureWithoutPageThatFits(Vector<TexAtlasElementDesc>& elements, TexAtlasNode& node) const
  211. {
  212. UINT32 sizeLimit = std::numeric_limits<UINT32>::max();
  213. while (true)
  214. {
  215. UINT32 largestSize = 0;
  216. INT32 largestId = -1;
  217. for (size_t i = 0; i < elements.size(); i++)
  218. {
  219. if (elements[i].output.page == -1) // Signifies that we haven't processed this node yet
  220. {
  221. UINT32 size = elements[i].input.width * elements[i].input.height;
  222. if (size > largestSize && size < sizeLimit)
  223. {
  224. largestSize = size;
  225. largestId = (INT32)i;
  226. }
  227. }
  228. }
  229. if (largestId == -1)
  230. return -1;
  231. if (node.insert(elements[largestId]))
  232. return largestId;
  233. else
  234. sizeLimit = largestSize;
  235. }
  236. }
  237. int TexAtlasGenerator::findLargestTextureWithoutPage(const Vector<TexAtlasElementDesc>& elements) const
  238. {
  239. INT32 largestId = -1;
  240. UINT32 largestSize = 0;
  241. for (size_t i = 0; i < elements.size(); i++)
  242. {
  243. if (elements[i].output.page == -1) // Signifies that we haven't processed this node yet
  244. {
  245. UINT32 size = elements[i].input.width * elements[i].input.height;
  246. if (size > largestSize)
  247. {
  248. largestSize = size;
  249. largestId = (INT32)i;
  250. }
  251. }
  252. }
  253. return largestId;
  254. }
  255. void TexAtlasGenerator::sortBySize(Vector<TexAtlasElementDesc>& elements) const
  256. {
  257. for (size_t i = 0; i < elements.size(); i++)
  258. {
  259. INT32 largestId = -1;
  260. UINT32 largestSize = 0;
  261. for (size_t j = i; j < elements.size(); j++)
  262. {
  263. UINT32 size = elements[j].input.width * elements[j].input.height;
  264. if (size > largestSize)
  265. {
  266. largestSize = size;
  267. largestId = (INT32)j;
  268. }
  269. }
  270. if(largestId != -1)
  271. {
  272. TexAtlasElementDesc temp = elements[i];
  273. elements[i] = elements[largestId];
  274. elements[largestId] = temp;
  275. }
  276. }
  277. }
  278. }