LightMapPacker.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. //
  21. #include <ThirdParty/STB/stb_rect_pack.h>
  22. #include <Atomic/IO/Log.h>
  23. #include <Atomic/Resource/Image.h>
  24. #include "BakeMesh.h"
  25. #include "LightMapPacker.h"
  26. namespace AtomicGlow
  27. {
  28. LightMapPacker::LightMapPacker(Context* context) : Object(context)
  29. {
  30. }
  31. LightMapPacker::~LightMapPacker()
  32. {
  33. }
  34. void LightMapPacker::AddRadianceMap(RadianceMap* radianceMap)
  35. {
  36. if (!radianceMap)
  37. return;
  38. radMaps_.Push(SharedPtr<RadianceMap>(radianceMap));
  39. }
  40. bool LightMapPacker::TryAddRadianceMap(RadianceMap* radMap)
  41. {
  42. if (radMap->packed_)
  43. return false;
  44. const int numnodes = GlobalGlowSettings.lightmapSize_;
  45. SharedArrayPtr<unsigned char> nodes(new unsigned char[sizeof(stbrp_node) * numnodes]);
  46. SharedArrayPtr<unsigned char> rects(new unsigned char[sizeof(stbrp_rect) * (workingSet_.Size() + 1)]);
  47. stbrp_context rectContext;
  48. stbrp_init_target (&rectContext, numnodes, numnodes, (stbrp_node *) nodes.Get(), numnodes);
  49. stbrp_rect* rect = (stbrp_rect*) rects.Get();
  50. // add working set, we do this brute force for best results
  51. for (unsigned i = 0; i < workingSet_.Size(); i++)
  52. {
  53. RadianceMap* wmap = workingSet_[i];
  54. rect->id = (int) i;
  55. rect->w = wmap->GetWidth() + LIGHTMAP_PADDING * 2;
  56. rect->h = wmap->GetHeight() + LIGHTMAP_PADDING * 2;
  57. rect++;
  58. }
  59. rect->id = (int) workingSet_.Size();
  60. rect->w = radMap->GetWidth() + LIGHTMAP_PADDING * 2;
  61. rect->h = radMap->GetHeight() + LIGHTMAP_PADDING * 2;
  62. if (!stbrp_pack_rects (&rectContext, (stbrp_rect *)rects.Get(), workingSet_.Size() + 1))
  63. {
  64. return false;
  65. }
  66. return true;
  67. }
  68. void LightMapPacker::DilatedBlit(const Image* srcImage, Image* destImage, const IntRect& destRect)
  69. {
  70. for (int y = 0; y < srcImage->GetHeight(); y++)
  71. {
  72. for (int x = 0; x < srcImage->GetWidth(); x++)
  73. {
  74. destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
  75. destRect.top_ + y + LIGHTMAP_PADDING,
  76. srcImage->GetPixelInt(x, y));
  77. }
  78. }
  79. // dilate top and bottom
  80. for (int x = 0; x < srcImage->GetWidth(); x++)
  81. {
  82. for (int i = 0; i < LIGHTMAP_PADDING; i++)
  83. {
  84. destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
  85. destRect.top_ + i,
  86. srcImage->GetPixelInt(x, 0));
  87. destImage->SetPixelInt(destRect.left_ + x + LIGHTMAP_PADDING,
  88. destRect.bottom_ - i,
  89. srcImage->GetPixelInt(x, srcImage->GetHeight() - 1));
  90. }
  91. }
  92. // dilate left and right
  93. for (int y = 0; y < srcImage->GetHeight(); y++)
  94. {
  95. for (int i = 0; i < LIGHTMAP_PADDING; i++)
  96. {
  97. destImage->SetPixelInt(destRect.left_ + i,
  98. destRect.top_ + y + LIGHTMAP_PADDING,
  99. srcImage->GetPixelInt(0, y));
  100. destImage->SetPixelInt(destRect.right_ - i,
  101. destRect.top_ + y + LIGHTMAP_PADDING,
  102. srcImage->GetPixelInt(srcImage->GetWidth() - 1, y));
  103. }
  104. }
  105. // dilate corners
  106. for (int y = LIGHTMAP_PADDING - 1; y >= 0 ; y--)
  107. {
  108. for (int x = LIGHTMAP_PADDING - 1; x >= 0 ; x--)
  109. {
  110. // upper left
  111. unsigned pixel = destImage->GetPixelInt(destRect.left_ + x + 1, destRect.top_ + y + 1);
  112. //pixel = Color::BLUE.ToUInt();
  113. destImage->SetPixelInt(destRect.left_ + x, destRect.top_ + y, pixel);
  114. // upper right
  115. pixel = destImage->GetPixelInt(destRect.right_ - x - 1, destRect.top_ + y + 1);
  116. //pixel = Color::RED.ToUInt();
  117. destImage->SetPixelInt(destRect.right_ - x, destRect.top_ + y, pixel);
  118. // lower left
  119. pixel = destImage->GetPixelInt(destRect.left_ + x + 1, destRect.bottom_ - y - 1);
  120. //pixel = Color::GREEN.ToUInt();
  121. destImage->SetPixelInt(destRect.left_ + x, destRect.bottom_ - y, pixel);
  122. // lower right
  123. pixel = destImage->GetPixelInt(destRect.right_ - x - 1, destRect.bottom_ - y - 1);
  124. //pixel = Color::MAGENTA.ToUInt();
  125. destImage->SetPixelInt(destRect.right_ - x, destRect.bottom_ - y, pixel);
  126. }
  127. }
  128. }
  129. void LightMapPacker::EmitLightmap(unsigned lightMapID)
  130. {
  131. int width = GlobalGlowSettings.lightmapSize_;
  132. int height = GlobalGlowSettings.lightmapSize_;
  133. // see note in stbrp_init_target docs
  134. int numnodes = width;
  135. SharedArrayPtr<unsigned char> nodes(new unsigned char[sizeof(stbrp_node) * numnodes]);
  136. SharedArrayPtr<unsigned char> rects(new unsigned char[sizeof(stbrp_rect) * workingSet_.Size()]);
  137. stbrp_context rectContext;
  138. stbrp_init_target (&rectContext, width, height, (stbrp_node *) nodes.Get(), numnodes);
  139. stbrp_rect* rect = (stbrp_rect*) rects.Get();
  140. for (unsigned i = 0; i < workingSet_.Size(); i++)
  141. {
  142. RadianceMap* radMap = workingSet_[i];
  143. rect->id = (int) i;
  144. rect->w = radMap->GetWidth() + LIGHTMAP_PADDING * 2;
  145. rect->h = radMap->GetHeight() + LIGHTMAP_PADDING * 2;
  146. rect++;
  147. }
  148. if (!stbrp_pack_rects (&rectContext, (stbrp_rect *)rects.Get(), workingSet_.Size()))
  149. {
  150. ATOMIC_LOGERROR("SceneBaker::Light() - not all rects packed");
  151. return;
  152. }
  153. SharedPtr<Image> image(new Image(context_));
  154. image->SetSize(width, height, 3);
  155. image->Clear(Color::CYAN);
  156. rect = (stbrp_rect*) rects.Get();
  157. for (unsigned i = 0; i < workingSet_.Size(); i++)
  158. {
  159. RadianceMap* radMap = workingSet_[i];
  160. if (!rect->was_packed)
  161. {
  162. ATOMIC_LOGERROR("LightMapPacker::Light() - skipping unpacked lightmap");
  163. continue;
  164. }
  165. DilatedBlit(radMap->image_, image, IntRect(rect->x, rect->y, rect->x + rect->w - 1, rect->y + rect->h - 1));
  166. radMap->bakeMesh_->Pack(lightMapID, Vector4(float(radMap->image_->GetWidth())/float(width),
  167. float(radMap->image_->GetHeight())/float(height),
  168. float(rect->x + LIGHTMAP_PADDING)/float(width),
  169. float(rect->y + LIGHTMAP_PADDING)/float(height)));
  170. rect++;
  171. }
  172. // dilate left and right maximum extents
  173. for (int i = 0; i < height; i++)
  174. {
  175. image->SetPixelInt(width -1, i, image->GetPixelInt(0, i));
  176. }
  177. for (int i = 0; i < width; i++)
  178. {
  179. image->SetPixelInt(i, height - 1, image->GetPixelInt(i, 0));
  180. }
  181. SharedPtr<LightMap> lightmap(new LightMap(context_));
  182. lightMaps_.Push(lightmap);
  183. lightmap->SetID(lightMapID);
  184. lightmap->SetImage(image);
  185. workingSet_.Clear();
  186. }
  187. void LightMapPacker::SaveLightmaps()
  188. {
  189. for (unsigned i = 0; i < lightMaps_.Size(); i++)
  190. {
  191. LightMap* lightmap = lightMaps_[i];
  192. #ifdef ATOMIC_PLATFORM_WINDOWS
  193. String filename = ToString("C:/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/Resources/Textures/Scene_Lightmap%u.png", lightmap->GetID());
  194. #else
  195. const char* format = GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? "png" : "dds";
  196. String filename = ToString("%s/Resources/Textures/Scene_Lightmap%u.%s", GlobalGlowSettings.projectPath_.CString(), lightmap->GetID(), format);
  197. #endif
  198. //ATOMIC_LOGINFOF("Saving Lightmap: %s", filename.CString());
  199. GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? lightmap->GetImage()->SavePNG(filename) : lightmap->GetImage()->SaveDDS(filename);
  200. }
  201. }
  202. static bool CompareRadianceMap(RadianceMap* lhs, RadianceMap* rhs)
  203. {
  204. int lhsWeight = lhs->GetWidth();
  205. lhsWeight += lhs->GetHeight();
  206. int rhsWeight = rhs->GetWidth();
  207. rhsWeight += rhs->GetHeight();
  208. // sort from biggest to smallest
  209. return lhsWeight > rhsWeight;
  210. }
  211. void LightMapPacker::Pack()
  212. {
  213. unsigned lightmapID = 0;
  214. SharedPtr<LightMap> lightmap;
  215. Sort(radMaps_.Begin(), radMaps_.End(), CompareRadianceMap);
  216. for (unsigned i = 0; i < radMaps_.Size(); i++)
  217. {
  218. RadianceMap* radMap = radMaps_[i];
  219. if (radMap->packed_)
  220. continue;
  221. if (radMap->GetWidth() >= GlobalGlowSettings.lightmapSize_ || radMap->GetHeight() >= GlobalGlowSettings.lightmapSize_)
  222. {
  223. lightmap = new LightMap(context_);
  224. lightMaps_.Push(lightmap);
  225. lightmap->SetID(lightmapID);
  226. lightmap->SetImage(radMap->image_);
  227. radMap->bakeMesh_->Pack(lightmapID, Vector4(1.0f, 1.0f, 0.0f, 0.0f));
  228. lightmapID++;
  229. continue;
  230. }
  231. workingSet_.Push(radMap);
  232. for (unsigned j = 0; j < radMaps_.Size(); j++)
  233. {
  234. if (i == j)
  235. continue;
  236. RadianceMap* otherMap = radMaps_[j];
  237. if (TryAddRadianceMap(otherMap))
  238. {
  239. workingSet_.Push(otherMap);
  240. }
  241. }
  242. EmitLightmap(lightmapID++);
  243. workingSet_.Clear();
  244. }
  245. }
  246. }