OGLTextureCube.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Context.h"
  25. #include "FileSystem.h"
  26. #include "Graphics.h"
  27. #include "Log.h"
  28. #include "Profiler.h"
  29. #include "Renderer.h"
  30. #include "ResourceCache.h"
  31. #include "TextureCube.h"
  32. #include "XMLFile.h"
  33. #include <GLee.h>
  34. #include "DebugNew.h"
  35. #ifdef _MSC_VER
  36. #pragma warning(disable:4355)
  37. #endif
  38. OBJECTTYPESTATIC(TextureCube);
  39. TextureCube::TextureCube(Context* context) :
  40. Texture(context)
  41. {
  42. target_ = GL_TEXTURE_CUBE_MAP;
  43. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  44. faceMemoryUse_[i] = 0;
  45. }
  46. TextureCube::~TextureCube()
  47. {
  48. Release();
  49. }
  50. void TextureCube::RegisterObject(Context* context)
  51. {
  52. context->RegisterFactory<TextureCube>();
  53. }
  54. void TextureCube::OnDeviceReset()
  55. {
  56. if (!object_)
  57. {
  58. // Reload from the original file if possible
  59. ResourceCache* cache = GetSubsystem<ResourceCache>();
  60. const String& name = GetName();
  61. if ((cache) && (!name.Empty()) && (cache->Exists(name)))
  62. cache->ReloadResource(this);
  63. }
  64. }
  65. void TextureCube::Release()
  66. {
  67. if (object_)
  68. {
  69. if (!graphics_)
  70. return;
  71. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  72. {
  73. if (graphics_->GetTexture(i) == this)
  74. graphics_->SetTexture(i, 0);
  75. }
  76. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  77. {
  78. if (renderSurfaces_[i])
  79. renderSurfaces_[i]->Release();
  80. }
  81. glDeleteTextures(1, &object_);
  82. object_ = 0;
  83. dataLost_ = true;
  84. }
  85. }
  86. bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
  87. {
  88. if (size <= 0)
  89. {
  90. LOGERROR("Zero or negative cube texture size");
  91. return false;
  92. }
  93. if (usage == TEXTURE_DEPTHSTENCIL)
  94. {
  95. LOGERROR("Depth stencil usage not supported for cube maps");
  96. return false;
  97. }
  98. // Delete the old rendersurfaces if any
  99. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  100. {
  101. renderSurfaces_[i].Reset();
  102. faceMemoryUse_[i] = 0;
  103. }
  104. if (usage == TEXTURE_RENDERTARGET)
  105. {
  106. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  107. renderSurfaces_[i] = new RenderSurface(this, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i);
  108. // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
  109. addressMode_[COORD_U] = ADDRESS_CLAMP;
  110. addressMode_[COORD_V] = ADDRESS_CLAMP;
  111. addressMode_[COORD_W] = ADDRESS_CLAMP;
  112. filterMode_ = FILTER_NEAREST;
  113. requestedLevels_ = 1;
  114. dynamic_ = true;
  115. }
  116. else if (usage == TEXTURE_DYNAMIC)
  117. dynamic_ = true;
  118. else
  119. dynamic_ = false;
  120. width_ = size;
  121. height_ = size;
  122. format_ = format;
  123. return Create();
  124. }
  125. bool TextureCube::Load(Deserializer& source)
  126. {
  127. PROFILE(LoadTextureCube);
  128. ResourceCache* cache = GetSubsystem<ResourceCache>();
  129. if (!graphics_)
  130. return false;
  131. // If over the texture budget, see if materials can be freed to allow textures to be freed
  132. CheckTextureBudget(GetTypeStatic());
  133. String texPath, texName, texExt;
  134. SplitPath(GetName(), texPath, texName, texExt);
  135. SharedPtr<XMLFile> xml(new XMLFile(context_));
  136. if (!xml->Load(source))
  137. return false;
  138. LoadParameters(xml);
  139. XMLElement textureElem = xml->GetRootElement();
  140. XMLElement faceElem = textureElem.GetChildElement("face");
  141. unsigned faces = 0;
  142. while ((faceElem) && (faces < MAX_CUBEMAP_FACES))
  143. {
  144. String name = faceElem.GetString("name");
  145. String faceTexPath, faceTexName, faceTexExt;
  146. SplitPath(name, faceTexPath, faceTexName, faceTexExt);
  147. // If path is empty, add the XML file path
  148. if (faceTexPath.Empty())
  149. name = texPath + name;
  150. SharedPtr<Image> image(cache->GetResource<Image>(name));
  151. Load((CubeMapFace)faces, image);
  152. faces++;
  153. faceElem = faceElem.GetNextElement("face");
  154. }
  155. ClearDataLost();
  156. return true;
  157. }
  158. bool TextureCube::Load(CubeMapFace face, Deserializer& source)
  159. {
  160. PROFILE(LoadTextureCube);
  161. SharedPtr<Image> image(new Image(context_));
  162. if (!image->Load(source))
  163. return false;
  164. return Load(face, image);
  165. }
  166. bool TextureCube::Load(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
  167. {
  168. if (!image)
  169. {
  170. LOGERROR("Null image, can not load texture");
  171. return false;
  172. }
  173. unsigned memoryUse = 0;
  174. int quality = QUALITY_HIGH;
  175. Renderer* renderer = GetSubsystem<Renderer>();
  176. if (renderer)
  177. quality = renderer->GetTextureQuality();
  178. if (!image->IsCompressed())
  179. {
  180. unsigned char* levelData = image->GetData();
  181. int levelWidth = image->GetWidth();
  182. int levelHeight = image->GetHeight();
  183. unsigned components = image->GetComponents();
  184. unsigned format = 0;
  185. if (levelWidth != levelHeight)
  186. {
  187. LOGERROR("Cube texture width not equal to height");
  188. return false;
  189. }
  190. // Discard unnecessary mip levels
  191. for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
  192. {
  193. image = image->GetNextLevel();
  194. levelWidth = image->GetWidth();
  195. levelHeight = image->GetHeight();
  196. }
  197. switch (components)
  198. {
  199. case 1:
  200. format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
  201. break;
  202. case 2:
  203. format = Graphics::GetLuminanceAlphaFormat();
  204. break;
  205. case 3:
  206. format = Graphics::GetRGBFormat();
  207. break;
  208. case 4:
  209. format = Graphics::GetRGBAFormat();
  210. break;
  211. }
  212. // Create the texture when face 0 is being loaded, check that rest of the faces are same size & format
  213. if (!face)
  214. SetSize(levelWidth, format);
  215. else
  216. {
  217. if (!object_)
  218. {
  219. LOGERROR("Cube texture face 0 must be loaded first");
  220. return false;
  221. }
  222. if ((levelWidth != width_) || (format != format_))
  223. {
  224. LOGERROR("Cube texture face does not match size or format of face 0");
  225. return false;
  226. }
  227. }
  228. graphics_->SetTextureForUpdate(this);
  229. for (unsigned i = 0; i < levels_; ++i)
  230. {
  231. glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, format_, levelWidth, levelHeight, 0,
  232. GetExternalFormat(format_), GL_UNSIGNED_BYTE, levelData);
  233. if (i < levels_ - 1)
  234. {
  235. image = image->GetNextLevel();
  236. levelData = image->GetData();
  237. levelWidth = image->GetWidth();
  238. levelHeight = image->GetHeight();
  239. }
  240. memoryUse += levelWidth * levelHeight * components;
  241. }
  242. graphics_->SetTexture(0, 0);
  243. }
  244. else
  245. {
  246. int width = image->GetWidth();
  247. int height = image->GetHeight();
  248. unsigned levels = image->GetNumCompressedLevels();
  249. unsigned format = GetDXTFormat(image->GetCompressedFormat());
  250. if (width != height)
  251. {
  252. LOGERROR("Cube texture width not equal to height");
  253. return false;
  254. }
  255. unsigned mipsToSkip = mipsToSkip_[quality];
  256. if (mipsToSkip >= levels)
  257. mipsToSkip = levels - 1;
  258. while ((mipsToSkip) && ((width / (1 << mipsToSkip) < 4) || (height / (1 << mipsToSkip) < 4)))
  259. --mipsToSkip;
  260. width /= (1 << mipsToSkip);
  261. height /= (1 << mipsToSkip);
  262. // Create the texture when face 0 is being loaded, assume rest of the faces are same size & format
  263. if (!face)
  264. {
  265. SetNumLevels(Max((int)(levels - mipsToSkip), 1));
  266. SetSize(width, format);
  267. }
  268. else
  269. {
  270. if (!object_)
  271. {
  272. LOGERROR("Cube texture face 0 must be loaded first");
  273. return false;
  274. }
  275. if ((width != width_) || (format != format_))
  276. {
  277. LOGERROR("Cube texture face does not match size or format of face 0");
  278. return false;
  279. }
  280. }
  281. graphics_->SetTextureForUpdate(this);
  282. for (unsigned i = 0; (i < levels_) && (i < levels - mipsToSkip); ++i)
  283. {
  284. CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
  285. glCompressedTexImage2D(target_, i, format_, level.width_, level.height_, 0, level.dataSize_, level.data_);
  286. memoryUse += level.rows_ * level.rowSize_;
  287. }
  288. graphics_->SetTexture(0, 0);
  289. }
  290. faceMemoryUse_[face] = memoryUse;
  291. unsigned totalMemoryUse = 0;
  292. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  293. totalMemoryUse += faceMemoryUse_[i];
  294. SetMemoryUse(totalMemoryUse);
  295. return true;
  296. }
  297. bool TextureCube::Create()
  298. {
  299. Release();
  300. if (!graphics_)
  301. return false;
  302. if ((!width_) || (!height_))
  303. return false;
  304. glGenTextures(1, &object_);
  305. // Ensure that our texture is bound to OpenGL texture unit 0
  306. graphics_->SetTextureForUpdate(this);
  307. // If not compressed, create the initial level 0 texture with null data
  308. unsigned externalFormat = GetExternalFormat(format_);
  309. unsigned dataType = GetDataType(format_);
  310. if ((format_ != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) && (format_ != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) &&
  311. (format_ != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT))
  312. {
  313. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  314. glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format_, width_, height_, 0, externalFormat, dataType, 0);
  315. }
  316. // Set mipmapping
  317. levels_ = requestedLevels_;
  318. if (!levels_)
  319. {
  320. unsigned maxSize = max((int)width_, (int)height_);
  321. while (maxSize)
  322. {
  323. maxSize >>= 1;
  324. ++levels_;
  325. }
  326. }
  327. glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);
  328. glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, levels_ - 1);
  329. // Set initial parameters, then unbind the texture
  330. UpdateParameters();
  331. graphics_->SetTexture(0, 0);
  332. return true;
  333. }