TextureCube.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../Core/Profiler.h"
  6. #include "../Graphics/Graphics.h"
  7. #include "../Graphics/GraphicsEvents.h"
  8. #include "../Graphics/Renderer.h"
  9. #include "../GraphicsAPI/GraphicsImpl.h"
  10. #include "../GraphicsAPI/TextureCube.h"
  11. #include "../IO/FileSystem.h"
  12. #include "../IO/Log.h"
  13. #include "../Resource/ResourceCache.h"
  14. #include "../Resource/XMLFile.h"
  15. #include "../DebugNew.h"
  16. #ifdef _MSC_VER
  17. #pragma warning(disable:4355)
  18. #endif
  19. namespace Urho3D
  20. {
  21. static const char* cubeMapLayoutNames[] = {
  22. "horizontal",
  23. "horizontalnvidia",
  24. "horizontalcross",
  25. "verticalcross",
  26. "blender",
  27. nullptr
  28. };
  29. static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
  30. {
  31. return SharedPtr<Image>(
  32. src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
  33. }
  34. TextureCube::TextureCube(Context* context) :
  35. Texture(context)
  36. {
  37. #ifdef URHO3D_OPENGL
  38. if (Graphics::GetGAPI() == GAPI_OPENGL)
  39. target_ = GL_TEXTURE_CUBE_MAP;
  40. #endif
  41. // Default to clamp mode addressing
  42. addressModes_[COORD_U] = ADDRESS_CLAMP;
  43. addressModes_[COORD_V] = ADDRESS_CLAMP;
  44. addressModes_[COORD_W] = ADDRESS_CLAMP;
  45. }
  46. TextureCube::~TextureCube()
  47. {
  48. Release();
  49. }
  50. void TextureCube::RegisterObject(Context* context)
  51. {
  52. context->RegisterFactory<TextureCube>();
  53. }
  54. bool TextureCube::BeginLoad(Deserializer& source)
  55. {
  56. auto* cache = GetSubsystem<ResourceCache>();
  57. // In headless mode, do not actually load the texture, just return success
  58. if (!graphics_)
  59. return true;
  60. // If device is lost, retry later
  61. if (graphics_->IsDeviceLost())
  62. {
  63. URHO3D_LOGWARNING("Texture load while device is lost");
  64. dataPending_ = true;
  65. return true;
  66. }
  67. cache->ResetDependencies(this);
  68. String texPath, texName, texExt;
  69. SplitPath(GetName(), texPath, texName, texExt);
  70. loadParameters_ = (new XMLFile(context_));
  71. if (!loadParameters_->Load(source))
  72. {
  73. loadParameters_.Reset();
  74. return false;
  75. }
  76. loadImages_.Clear();
  77. XMLElement textureElem = loadParameters_->GetRoot();
  78. XMLElement imageElem = textureElem.GetChild("image");
  79. // Single image and multiple faces with layout
  80. if (imageElem)
  81. {
  82. String name = imageElem.GetAttribute("name");
  83. // If path is empty, add the XML file path
  84. if (GetPath(name).Empty())
  85. name = texPath + name;
  86. SharedPtr<Image> image = cache->GetTempResource<Image>(name);
  87. if (!image)
  88. return false;
  89. int faceWidth, faceHeight;
  90. loadImages_.Resize(MAX_CUBEMAP_FACES);
  91. if (image->IsCubemap())
  92. {
  93. loadImages_[FACE_POSITIVE_X] = image;
  94. loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
  95. loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
  96. loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
  97. loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
  98. loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
  99. }
  100. else
  101. {
  102. CubeMapLayout layout =
  103. (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
  104. switch (layout)
  105. {
  106. case CML_HORIZONTAL:
  107. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  108. faceHeight = image->GetHeight();
  109. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  110. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  111. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  112. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
  113. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
  114. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
  115. break;
  116. case CML_HORIZONTALNVIDIA:
  117. faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
  118. faceHeight = image->GetHeight();
  119. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  120. loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
  121. break;
  122. case CML_HORIZONTALCROSS:
  123. faceWidth = image->GetWidth() / 4;
  124. faceHeight = image->GetHeight() / 3;
  125. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  126. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  127. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  128. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  129. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
  130. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  131. break;
  132. case CML_VERTICALCROSS:
  133. faceWidth = image->GetWidth() / 3;
  134. faceHeight = image->GetHeight() / 4;
  135. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  136. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  137. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  138. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  139. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
  140. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
  141. if (loadImages_[FACE_NEGATIVE_Z])
  142. {
  143. loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
  144. loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
  145. }
  146. break;
  147. case CML_BLENDER:
  148. faceWidth = image->GetWidth() / 3;
  149. faceHeight = image->GetHeight() / 2;
  150. loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
  151. loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
  152. loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
  153. loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
  154. loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
  155. loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
  156. break;
  157. }
  158. }
  159. }
  160. // Face per image
  161. else
  162. {
  163. XMLElement faceElem = textureElem.GetChild("face");
  164. while (faceElem)
  165. {
  166. String name = faceElem.GetAttribute("name");
  167. // If path is empty, add the XML file path
  168. if (GetPath(name).Empty())
  169. name = texPath + name;
  170. loadImages_.Push(cache->GetTempResource<Image>(name));
  171. cache->StoreResourceDependency(this, name);
  172. faceElem = faceElem.GetNext("face");
  173. }
  174. }
  175. // Precalculate mip levels if async loading
  176. if (GetAsyncLoadState() == ASYNC_LOADING)
  177. {
  178. for (unsigned i = 0; i < loadImages_.Size(); ++i)
  179. {
  180. if (loadImages_[i])
  181. loadImages_[i]->PrecalculateLevels();
  182. }
  183. }
  184. return true;
  185. }
  186. bool TextureCube::EndLoad()
  187. {
  188. // In headless mode, do not actually load the texture, just return success
  189. if (!graphics_ || graphics_->IsDeviceLost())
  190. return true;
  191. // If over the texture budget, see if materials can be freed to allow textures to be freed
  192. CheckTextureBudget(GetTypeStatic());
  193. SetParameters(loadParameters_);
  194. for (unsigned i = 0; i < loadImages_.Size() && i < MAX_CUBEMAP_FACES; ++i)
  195. SetData((CubeMapFace)i, loadImages_[i]);
  196. loadImages_.Clear();
  197. loadParameters_.Reset();
  198. return true;
  199. }
  200. bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int multiSample)
  201. {
  202. if (size <= 0)
  203. {
  204. URHO3D_LOGERROR("Zero or negative cube texture size");
  205. return false;
  206. }
  207. if (usage == TEXTURE_DEPTHSTENCIL)
  208. {
  209. URHO3D_LOGERROR("Depth-stencil usage not supported for cube textures");
  210. return false;
  211. }
  212. multiSample = Clamp(multiSample, 1, 16);
  213. if (multiSample > 1 && usage < TEXTURE_RENDERTARGET)
  214. {
  215. URHO3D_LOGERROR("Multisampling is only supported for rendertarget cube textures");
  216. return false;
  217. }
  218. // Delete the old rendersurfaces if any
  219. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  220. {
  221. renderSurfaces_[i].Reset();
  222. faceMemoryUse_[i] = 0;
  223. }
  224. usage_ = usage;
  225. if (usage == TEXTURE_RENDERTARGET)
  226. {
  227. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  228. {
  229. renderSurfaces_[i] = new RenderSurface(this);
  230. #ifdef URHO3D_OPENGL
  231. if (Graphics::GetGAPI() == GAPI_OPENGL)
  232. renderSurfaces_[i]->target_ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
  233. #endif
  234. }
  235. // Nearest filtering by default
  236. filterMode_ = FILTER_NEAREST;
  237. }
  238. if (usage == TEXTURE_RENDERTARGET)
  239. SubscribeToEvent(E_RENDERSURFACEUPDATE, URHO3D_HANDLER(TextureCube, HandleRenderSurfaceUpdate));
  240. else
  241. UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
  242. width_ = size;
  243. height_ = size;
  244. depth_ = 1;
  245. format_ = format;
  246. multiSample_ = multiSample;
  247. autoResolve_ = multiSample > 1;
  248. return Create();
  249. }
  250. SharedPtr<Image> TextureCube::GetImage(CubeMapFace face) const
  251. {
  252. if (format_ != Graphics::GetRGBAFormat() && format_ != Graphics::GetRGBFormat())
  253. {
  254. URHO3D_LOGERROR("Unsupported texture format, can not convert to Image");
  255. return SharedPtr<Image>();
  256. }
  257. auto* rawImage = new Image(context_);
  258. if (format_ == Graphics::GetRGBAFormat())
  259. rawImage->SetSize(width_, height_, 4);
  260. else if (format_ == Graphics::GetRGBFormat())
  261. rawImage->SetSize(width_, height_, 3);
  262. else
  263. assert(false);
  264. GetData(face, 0, rawImage->GetData());
  265. return SharedPtr<Image>(rawImage);
  266. }
  267. void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
  268. {
  269. auto* renderer = GetSubsystem<Renderer>();
  270. for (auto& renderSurface : renderSurfaces_)
  271. {
  272. if (renderSurface && (renderSurface->GetUpdateMode() == SURFACE_UPDATEALWAYS || renderSurface->IsUpdateQueued()))
  273. {
  274. if (renderer)
  275. renderer->QueueRenderSurface(renderSurface);
  276. renderSurface->ResetUpdateQueued();
  277. }
  278. }
  279. }
  280. void TextureCube::OnDeviceLost()
  281. {
  282. GAPI gapi = Graphics::GetGAPI();
  283. #ifdef URHO3D_OPENGL
  284. if (gapi == GAPI_OPENGL)
  285. return OnDeviceLost_OGL();
  286. #endif
  287. #ifdef URHO3D_D3D11
  288. if (gapi == GAPI_D3D11)
  289. return OnDeviceLost_D3D11();
  290. #endif
  291. }
  292. void TextureCube::OnDeviceReset()
  293. {
  294. GAPI gapi = Graphics::GetGAPI();
  295. #ifdef URHO3D_OPENGL
  296. if (gapi == GAPI_OPENGL)
  297. return OnDeviceReset_OGL();
  298. #endif
  299. #ifdef URHO3D_D3D11
  300. if (gapi == GAPI_D3D11)
  301. return OnDeviceReset_D3D11();
  302. #endif
  303. }
  304. void TextureCube::Release()
  305. {
  306. GAPI gapi = Graphics::GetGAPI();
  307. #ifdef URHO3D_OPENGL
  308. if (gapi == GAPI_OPENGL)
  309. return Release_OGL();
  310. #endif
  311. #ifdef URHO3D_D3D11
  312. if (gapi == GAPI_D3D11)
  313. return Release_D3D11();
  314. #endif
  315. }
  316. bool TextureCube::SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data)
  317. {
  318. GAPI gapi = Graphics::GetGAPI();
  319. #ifdef URHO3D_OPENGL
  320. if (gapi == GAPI_OPENGL)
  321. return SetData_OGL(face, level, x, y, width, height, data);
  322. #endif
  323. #ifdef URHO3D_D3D11
  324. if (gapi == GAPI_D3D11)
  325. return SetData_D3D11(face, level, x, y, width, height, data);
  326. #endif
  327. return {}; // Prevent warning
  328. }
  329. bool TextureCube::SetData(CubeMapFace face, Deserializer& source)
  330. {
  331. GAPI gapi = Graphics::GetGAPI();
  332. #ifdef URHO3D_OPENGL
  333. if (gapi == GAPI_OPENGL)
  334. return SetData_OGL(face, source);
  335. #endif
  336. #ifdef URHO3D_D3D11
  337. if (gapi == GAPI_D3D11)
  338. return SetData_D3D11(face, source);
  339. #endif
  340. return {}; // Prevent warning
  341. }
  342. bool TextureCube::SetData(CubeMapFace face, Image* image, bool useAlpha)
  343. {
  344. GAPI gapi = Graphics::GetGAPI();
  345. #ifdef URHO3D_OPENGL
  346. if (gapi == GAPI_OPENGL)
  347. return SetData_OGL(face, image, useAlpha);
  348. #endif
  349. #ifdef URHO3D_D3D11
  350. if (gapi == GAPI_D3D11)
  351. return SetData_D3D11(face, image, useAlpha);
  352. #endif
  353. return {}; // Prevent warning
  354. }
  355. bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
  356. {
  357. GAPI gapi = Graphics::GetGAPI();
  358. #ifdef URHO3D_OPENGL
  359. if (gapi == GAPI_OPENGL)
  360. return GetData_OGL(face, level, dest);
  361. #endif
  362. #ifdef URHO3D_D3D11
  363. if (gapi == GAPI_D3D11)
  364. return GetData_D3D11(face, level, dest);
  365. #endif
  366. return {}; // Prevent warning
  367. }
  368. bool TextureCube::Create()
  369. {
  370. GAPI gapi = Graphics::GetGAPI();
  371. #ifdef URHO3D_OPENGL
  372. if (gapi == GAPI_OPENGL)
  373. return Create_OGL();
  374. #endif
  375. #ifdef URHO3D_D3D11
  376. if (gapi == GAPI_D3D11)
  377. return Create_D3D11();
  378. #endif
  379. return {}; // Prevent warning
  380. }
  381. }