TextureCube.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. //
  2. // Copyright (c) 2008-2017 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Core/Context.h"
  24. #include "../Core/Profiler.h"
  25. #include "../Graphics/Graphics.h"
  26. #include "../Graphics/GraphicsEvents.h"
  27. #include "../Graphics/GraphicsImpl.h"
  28. #include "../Graphics/Renderer.h"
  29. #include "../Graphics/TextureCube.h"
  30. #include "../IO/FileSystem.h"
  31. #include "../IO/Log.h"
  32. #include "../Resource/ResourceCache.h"
  33. #include "../Resource/XMLFile.h"
  34. #include "../DebugNew.h"
  35. #ifdef _MSC_VER
  36. #pragma warning(disable:4355)
  37. #endif
  38. namespace Atomic
  39. {
  40. static const char* cubeMapLayoutNames[] = {
  41. "horizontal",
  42. "horizontalnvidia",
  43. "horizontalcross",
  44. "verticalcross",
  45. "blender",
  46. 0
  47. };
  48. static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
  49. {
  50. return SharedPtr<Image>(
  51. src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
  52. }
  53. TextureCube::TextureCube(Context* context) :
  54. Texture(context)
  55. {
  56. #ifdef ATOMIC_OPENGL
  57. target_ = GL_TEXTURE_CUBE_MAP;
  58. #endif
  59. // Default to clamp mode addressing
  60. addressMode_[COORD_U] = ADDRESS_CLAMP;
  61. addressMode_[COORD_V] = ADDRESS_CLAMP;
  62. addressMode_[COORD_W] = ADDRESS_CLAMP;
  63. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  64. faceMemoryUse_[i] = 0;
  65. }
  66. TextureCube::~TextureCube()
  67. {
  68. Release();
  69. }
  70. void TextureCube::RegisterObject(Context* context)
  71. {
  72. context->RegisterFactory<TextureCube>();
  73. }
  74. bool TextureCube::BeginLoad(Deserializer& source)
  75. {
  76. ResourceCache* cache = GetSubsystem<ResourceCache>();
  77. // In headless mode, do not actually load the texture, just return success
  78. if (!graphics_)
  79. return true;
  80. // If device is lost, retry later
  81. if (graphics_->IsDeviceLost())
  82. {
  83. ATOMIC_LOGWARNING("Texture load while device is lost");
  84. dataPending_ = true;
  85. return true;
  86. }
  87. cache->ResetDependencies(this);
  88. String texPath, texName, texExt;
  89. SplitPath(GetName(), texPath, texName, texExt);
  90. loadParameters_ = (new XMLFile(context_));
  91. if (!loadParameters_->Load(source))
  92. {
  93. loadParameters_.Reset();
  94. return false;
  95. }
  96. loadImages_.Clear();
  97. XMLElement textureElem = loadParameters_->GetRoot();
  98. XMLElement imageElem = textureElem.GetChild("image");
  99. // Single image and multiple faces with layout
  100. if (imageElem)
  101. {
  102. String name = imageElem.GetAttribute("name");
  103. // If path is empty, add the XML file path
  104. if (GetPath(name).Empty())
  105. name = texPath + name;
  106. SharedPtr<Image> image = cache->GetTempResource<Image>(name);
  107. if (!image)
  108. return false;
  109. int faceWidth, faceHeight;
  110. loadImages_.Resize(MAX_CUBEMAP_FACES);
  111. if (image->IsCubemap())
  112. {
  113. loadImages_[FACE_POSITIVE_X] = image;
  114. loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
  115. loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
  116. loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
  117. loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
  118. loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
  119. }
  120. else
  121. {
  122. CubeMapLayout layout =
  123. (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
  124. switch (layout)
  125. {
  126. case CML_HORIZONTAL:
  127. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  128. faceHeight = image->GetHeight();
  129. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  130. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  131. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  132. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
  133. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
  134. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
  135. break;
  136. case CML_HORIZONTALNVIDIA:
  137. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  138. faceHeight = image->GetHeight();
  139. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  140. loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
  141. break;
  142. case CML_HORIZONTALCROSS:
  143. faceWidth = image->GetWidth() / 4;
  144. faceHeight = image->GetHeight() / 3;
  145. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  146. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  147. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  148. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  149. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
  150. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  151. break;
  152. case CML_VERTICALCROSS:
  153. faceWidth = image->GetWidth() / 3;
  154. faceHeight = image->GetHeight() / 4;
  155. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  156. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  157. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  158. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  159. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  160. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
  161. if (loadImages_[FACE_NEGATIVE_Z])
  162. {
  163. loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
  164. loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
  165. }
  166. break;
  167. case CML_BLENDER:
  168. faceWidth = image->GetWidth() / 3;
  169. faceHeight = image->GetHeight() / 2;
  170. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  171. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  172. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  173. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  174. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  175. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  176. break;
  177. }
  178. }
  179. }
  180. // Face per image
  181. else
  182. {
  183. XMLElement faceElem = textureElem.GetChild("face");
  184. while (faceElem)
  185. {
  186. String name = faceElem.GetAttribute("name");
  187. // If path is empty, add the XML file path
  188. if (GetPath(name).Empty())
  189. name = texPath + name;
  190. loadImages_.Push(cache->GetTempResource<Image>(name));
  191. cache->StoreResourceDependency(this, name);
  192. faceElem = faceElem.GetNext("face");
  193. }
  194. }
  195. // Precalculate mip levels if async loading
  196. if (GetAsyncLoadState() == ASYNC_LOADING)
  197. {
  198. for (unsigned i = 0; i < loadImages_.Size(); ++i)
  199. {
  200. if (loadImages_[i])
  201. loadImages_[i]->PrecalculateLevels();
  202. }
  203. }
  204. return true;
  205. }
  206. bool TextureCube::EndLoad()
  207. {
  208. // In headless mode, do not actually load the texture, just return success
  209. if (!graphics_ || graphics_->IsDeviceLost())
  210. return true;
  211. // If over the texture budget, see if materials can be freed to allow textures to be freed
  212. CheckTextureBudget(GetTypeStatic());
  213. SetParameters(loadParameters_);
  214. for (unsigned i = 0; i < loadImages_.Size() && i < MAX_CUBEMAP_FACES; ++i)
  215. SetData((CubeMapFace)i, loadImages_[i]);
  216. loadImages_.Clear();
  217. loadParameters_.Reset();
  218. return true;
  219. }
  220. bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int multiSample)
  221. {
  222. if (size <= 0)
  223. {
  224. ATOMIC_LOGERROR("Zero or negative cube texture size");
  225. return false;
  226. }
  227. if (usage == TEXTURE_DEPTHSTENCIL)
  228. {
  229. ATOMIC_LOGERROR("Depth-stencil usage not supported for cube textures");
  230. return false;
  231. }
  232. multiSample = Clamp(multiSample, 1, 16);
  233. if (multiSample > 1 && usage < TEXTURE_RENDERTARGET)
  234. {
  235. ATOMIC_LOGERROR("Multisampling is only supported for rendertarget cube textures");
  236. return false;
  237. }
  238. // Delete the old rendersurfaces if any
  239. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  240. {
  241. renderSurfaces_[i].Reset();
  242. faceMemoryUse_[i] = 0;
  243. }
  244. usage_ = usage;
  245. if (usage == TEXTURE_RENDERTARGET)
  246. {
  247. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  248. {
  249. renderSurfaces_[i] = new RenderSurface(this);
  250. #ifdef ATOMIC_OPENGL
  251. renderSurfaces_[i]->target_ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
  252. #endif
  253. }
  254. // Nearest filtering by default
  255. filterMode_ = FILTER_NEAREST;
  256. }
  257. if (usage == TEXTURE_RENDERTARGET)
  258. SubscribeToEvent(E_RENDERSURFACEUPDATE, ATOMIC_HANDLER(TextureCube, HandleRenderSurfaceUpdate));
  259. else
  260. UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
  261. width_ = size;
  262. height_ = size;
  263. depth_ = 1;
  264. format_ = format;
  265. multiSample_ = multiSample;
  266. autoResolve_ = multiSample > 1;
  267. return Create();
  268. }
  269. SharedPtr<Image> TextureCube::GetImage(CubeMapFace face) const
  270. {
  271. if (format_ != Graphics::GetRGBAFormat() && format_ != Graphics::GetRGBFormat())
  272. {
  273. ATOMIC_LOGERROR("Unsupported texture format, can not convert to Image");
  274. return SharedPtr<Image>();
  275. }
  276. Image* rawImage = new Image(context_);
  277. if (format_ == Graphics::GetRGBAFormat())
  278. rawImage->SetSize(width_, height_, 4);
  279. else if (format_ == Graphics::GetRGBFormat())
  280. rawImage->SetSize(width_, height_, 3);
  281. else
  282. assert(0);
  283. GetData(face, 0, rawImage->GetData());
  284. return SharedPtr<Image>(rawImage);
  285. }
  286. void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
  287. {
  288. Renderer* renderer = GetSubsystem<Renderer>();
  289. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  290. {
  291. if (renderSurfaces_[i] && (renderSurfaces_[i]->GetUpdateMode() == SURFACE_UPDATEALWAYS || renderSurfaces_[i]->IsUpdateQueued()))
  292. {
  293. if (renderer)
  294. renderer->QueueRenderSurface(renderSurfaces_[i]);
  295. renderSurfaces_[i]->ResetUpdateQueued();
  296. }
  297. }
  298. }
  299. }