TextureCube.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //
  2. // Copyright (c) 2008-2020 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 Urho3D
  39. {
  40. static const char* cubeMapLayoutNames[] = {
  41. "horizontal",
  42. "horizontalnvidia",
  43. "horizontalcross",
  44. "verticalcross",
  45. "blender",
  46. nullptr
  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 URHO3D_OPENGL
  57. target_ = GL_TEXTURE_CUBE_MAP;
  58. #endif
  59. // Default to clamp mode addressing
  60. addressModes_[COORD_U] = ADDRESS_CLAMP;
  61. addressModes_[COORD_V] = ADDRESS_CLAMP;
  62. addressModes_[COORD_W] = ADDRESS_CLAMP;
  63. }
  64. TextureCube::~TextureCube()
  65. {
  66. Release();
  67. }
  68. void TextureCube::RegisterObject(Context* context)
  69. {
  70. context->RegisterFactory<TextureCube>();
  71. }
  72. bool TextureCube::BeginLoad(Deserializer& source)
  73. {
  74. auto* cache = GetSubsystem<ResourceCache>();
  75. // In headless mode, do not actually load the texture, just return success
  76. if (!graphics_)
  77. return true;
  78. // If device is lost, retry later
  79. if (graphics_->IsDeviceLost())
  80. {
  81. URHO3D_LOGWARNING("Texture load while device is lost");
  82. dataPending_ = true;
  83. return true;
  84. }
  85. cache->ResetDependencies(this);
  86. String texPath, texName, texExt;
  87. SplitPath(GetName(), texPath, texName, texExt);
  88. loadParameters_ = (new XMLFile(context_));
  89. if (!loadParameters_->Load(source))
  90. {
  91. loadParameters_.Reset();
  92. return false;
  93. }
  94. loadImages_.Clear();
  95. XMLElement textureElem = loadParameters_->GetRoot();
  96. XMLElement imageElem = textureElem.GetChild("image");
  97. // Single image and multiple faces with layout
  98. if (imageElem)
  99. {
  100. String name = imageElem.GetAttribute("name");
  101. // If path is empty, add the XML file path
  102. if (GetPath(name).Empty())
  103. name = texPath + name;
  104. SharedPtr<Image> image = cache->GetTempResource<Image>(name);
  105. if (!image)
  106. return false;
  107. int faceWidth, faceHeight;
  108. loadImages_.Resize(MAX_CUBEMAP_FACES);
  109. if (image->IsCubemap())
  110. {
  111. loadImages_[FACE_POSITIVE_X] = image;
  112. loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
  113. loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
  114. loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
  115. loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
  116. loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
  117. }
  118. else
  119. {
  120. CubeMapLayout layout =
  121. (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
  122. switch (layout)
  123. {
  124. case CML_HORIZONTAL:
  125. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  126. faceHeight = image->GetHeight();
  127. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  128. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  129. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  130. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
  131. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
  132. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
  133. break;
  134. case CML_HORIZONTALNVIDIA:
  135. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  136. faceHeight = image->GetHeight();
  137. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  138. loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
  139. break;
  140. case CML_HORIZONTALCROSS:
  141. faceWidth = image->GetWidth() / 4;
  142. faceHeight = image->GetHeight() / 3;
  143. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  144. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  145. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  146. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  147. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
  148. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  149. break;
  150. case CML_VERTICALCROSS:
  151. faceWidth = image->GetWidth() / 3;
  152. faceHeight = image->GetHeight() / 4;
  153. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  154. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  155. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  156. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  157. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  158. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
  159. if (loadImages_[FACE_NEGATIVE_Z])
  160. {
  161. loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
  162. loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
  163. }
  164. break;
  165. case CML_BLENDER:
  166. faceWidth = image->GetWidth() / 3;
  167. faceHeight = image->GetHeight() / 2;
  168. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  169. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  170. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  171. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  172. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  173. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  174. break;
  175. }
  176. }
  177. }
  178. // Face per image
  179. else
  180. {
  181. XMLElement faceElem = textureElem.GetChild("face");
  182. while (faceElem)
  183. {
  184. String name = faceElem.GetAttribute("name");
  185. // If path is empty, add the XML file path
  186. if (GetPath(name).Empty())
  187. name = texPath + name;
  188. loadImages_.Push(cache->GetTempResource<Image>(name));
  189. cache->StoreResourceDependency(this, name);
  190. faceElem = faceElem.GetNext("face");
  191. }
  192. }
  193. // Precalculate mip levels if async loading
  194. if (GetAsyncLoadState() == ASYNC_LOADING)
  195. {
  196. for (unsigned i = 0; i < loadImages_.Size(); ++i)
  197. {
  198. if (loadImages_[i])
  199. loadImages_[i]->PrecalculateLevels();
  200. }
  201. }
  202. return true;
  203. }
  204. bool TextureCube::EndLoad()
  205. {
  206. // In headless mode, do not actually load the texture, just return success
  207. if (!graphics_ || graphics_->IsDeviceLost())
  208. return true;
  209. // If over the texture budget, see if materials can be freed to allow textures to be freed
  210. CheckTextureBudget(GetTypeStatic());
  211. SetParameters(loadParameters_);
  212. for (unsigned i = 0; i < loadImages_.Size() && i < MAX_CUBEMAP_FACES; ++i)
  213. SetData((CubeMapFace)i, loadImages_[i]);
  214. loadImages_.Clear();
  215. loadParameters_.Reset();
  216. return true;
  217. }
  218. bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int multiSample)
  219. {
  220. if (size <= 0)
  221. {
  222. URHO3D_LOGERROR("Zero or negative cube texture size");
  223. return false;
  224. }
  225. if (usage == TEXTURE_DEPTHSTENCIL)
  226. {
  227. URHO3D_LOGERROR("Depth-stencil usage not supported for cube textures");
  228. return false;
  229. }
  230. multiSample = Clamp(multiSample, 1, 16);
  231. if (multiSample > 1 && usage < TEXTURE_RENDERTARGET)
  232. {
  233. URHO3D_LOGERROR("Multisampling is only supported for rendertarget cube textures");
  234. return false;
  235. }
  236. // Delete the old rendersurfaces if any
  237. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  238. {
  239. renderSurfaces_[i].Reset();
  240. faceMemoryUse_[i] = 0;
  241. }
  242. usage_ = usage;
  243. if (usage == TEXTURE_RENDERTARGET)
  244. {
  245. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  246. {
  247. renderSurfaces_[i] = new RenderSurface(this);
  248. #ifdef URHO3D_OPENGL
  249. renderSurfaces_[i]->target_ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
  250. #endif
  251. }
  252. // Nearest filtering by default
  253. filterMode_ = FILTER_NEAREST;
  254. }
  255. if (usage == TEXTURE_RENDERTARGET)
  256. SubscribeToEvent(E_RENDERSURFACEUPDATE, URHO3D_HANDLER(TextureCube, HandleRenderSurfaceUpdate));
  257. else
  258. UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
  259. width_ = size;
  260. height_ = size;
  261. depth_ = 1;
  262. format_ = format;
  263. multiSample_ = multiSample;
  264. autoResolve_ = multiSample > 1;
  265. return Create();
  266. }
  267. SharedPtr<Image> TextureCube::GetImage(CubeMapFace face) const
  268. {
  269. if (format_ != Graphics::GetRGBAFormat() && format_ != Graphics::GetRGBFormat())
  270. {
  271. URHO3D_LOGERROR("Unsupported texture format, can not convert to Image");
  272. return SharedPtr<Image>();
  273. }
  274. auto* rawImage = new Image(context_);
  275. if (format_ == Graphics::GetRGBAFormat())
  276. rawImage->SetSize(width_, height_, 4);
  277. else if (format_ == Graphics::GetRGBFormat())
  278. rawImage->SetSize(width_, height_, 3);
  279. else
  280. assert(false);
  281. GetData(face, 0, rawImage->GetData());
  282. return SharedPtr<Image>(rawImage);
  283. }
  284. void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
  285. {
  286. auto* renderer = GetSubsystem<Renderer>();
  287. for (auto& renderSurface : renderSurfaces_)
  288. {
  289. if (renderSurface && (renderSurface->GetUpdateMode() == SURFACE_UPDATEALWAYS || renderSurface->IsUpdateQueued()))
  290. {
  291. if (renderer)
  292. renderer->QueueRenderSurface(renderSurface);
  293. renderSurface->ResetUpdateQueued();
  294. }
  295. }
  296. }
  297. }