Texture.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #include "Base.h"
  2. #include "Image.h"
  3. #include "Texture.h"
  4. #include "FileSystem.h"
  5. namespace gameplay
  6. {
  7. static std::vector<Texture*> __textureCache;
  8. Texture::Texture() : _handle(0), _mipmapped(false), _cached(false)
  9. {
  10. }
  11. Texture::Texture(const Texture& copy)
  12. {
  13. }
  14. Texture::~Texture()
  15. {
  16. if (_handle)
  17. {
  18. glDeleteTextures(1, &_handle);
  19. _handle = 0;
  20. }
  21. // Remove ourself from the texture cache.
  22. if (_cached)
  23. {
  24. std::vector<Texture*>::iterator itr = std::find(__textureCache.begin(), __textureCache.end(), this);
  25. if (itr != __textureCache.end())
  26. {
  27. __textureCache.erase(itr);
  28. }
  29. }
  30. }
  31. Texture* Texture::create(const char* path, bool generateMipmaps)
  32. {
  33. // Search texture cache first.
  34. for (unsigned int i = 0, count = __textureCache.size(); i < count; ++i)
  35. {
  36. Texture* t = __textureCache[i];
  37. if (t->_path == path)
  38. {
  39. // If 'generateMipmaps' is true, call Texture::generateMipamps() to force the
  40. // texture to generate its mipmap chain if it hasn't already done so.
  41. if (generateMipmaps)
  42. {
  43. t->generateMipmaps();
  44. }
  45. // Found a match.
  46. t->addRef();
  47. return t;
  48. }
  49. }
  50. Texture* texture = NULL;
  51. // Filter loading based on file extension.
  52. const char* ext = strrchr(path, '.');
  53. if (ext)
  54. {
  55. switch (strlen(ext))
  56. {
  57. case 4:
  58. if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'n' && tolower(ext[3]) == 'g')
  59. {
  60. Image* image = Image::create(path);
  61. if (image)
  62. texture = create(image, generateMipmaps);
  63. SAFE_RELEASE(image);
  64. }
  65. else if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'v' && tolower(ext[3]) == 'r')
  66. {
  67. // PowerVR Compressed RGBA
  68. texture = createCompressedPVR(path);
  69. }
  70. break;
  71. }
  72. }
  73. if (texture)
  74. {
  75. texture->_path = path;
  76. texture->_cached = true;
  77. // Add to texture cache.
  78. __textureCache.push_back(texture);
  79. return texture;
  80. }
  81. LOG_ERROR_VARG("Failed to load texture: %s", path);
  82. return NULL;
  83. }
  84. Texture* Texture::create(Image* image, bool generateMipmaps)
  85. {
  86. switch (image->getFormat())
  87. {
  88. case Image::RGB:
  89. return create(Texture::RGB, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
  90. case Image::RGBA:
  91. return create(Texture::RGBA, image->getWidth(), image->getHeight(), image->getData(), generateMipmaps);
  92. }
  93. return NULL;
  94. }
  95. Texture* Texture::create(Format format, unsigned int width, unsigned int height, unsigned char* data, bool generateMipmaps)
  96. {
  97. // Load our texture.
  98. GLuint textureId;
  99. GL_ASSERT( glGenTextures(1, &textureId) );
  100. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
  101. if (format == DEPTH)
  102. {
  103. // <type> must be UNSIGNED_SHORT or UNSIGNED_INT for a format of DEPTH_COMPONENT.
  104. GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_INT, data) );
  105. }
  106. else
  107. {
  108. GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, data) );
  109. }
  110. // Set initial minification filter based on whether or not mipmaping was enabled
  111. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generateMipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
  112. Texture* texture = new Texture();
  113. texture->_handle = textureId;
  114. texture->_width = width;
  115. texture->_height = height;
  116. if (generateMipmaps)
  117. {
  118. texture->generateMipmaps();
  119. }
  120. return texture;
  121. }
  122. Texture* Texture::createCompressedPVR(const char* path)
  123. {
  124. char PVRTexIdentifier[] = "PVR!";
  125. enum
  126. {
  127. PVRTextureFlagTypePVRTC_2 = 24,
  128. PVRTextureFlagTypePVRTC_4
  129. };
  130. struct pvr_file_header
  131. {
  132. unsigned int size; // size of the structure
  133. unsigned int height; // height of surface to be created
  134. unsigned int width; // width of input surface
  135. unsigned int mipmapCount; // number of mip-map levels requested
  136. unsigned int formatflags; // pixel format flags
  137. unsigned int dataSize; // total size in bytes
  138. unsigned int bpp; // number of bits per pixel
  139. unsigned int redBitMask; // mask for red bit
  140. unsigned int greenBitMask; // mask for green bits
  141. unsigned int blueBitMask; // mask for blue bits
  142. unsigned int alphaBitMask; // mask for alpha channel
  143. unsigned int pvrTag; // magic number identifying pvr file
  144. unsigned int surfaceCount; // number of surfaces present in the pvr
  145. } ;
  146. FILE* file = FileSystem::openFile(path, "rb");
  147. if (file == NULL)
  148. {
  149. LOG_ERROR_VARG("Failed to load file: %s", path);
  150. return NULL;
  151. }
  152. // Read the file header
  153. unsigned int size = sizeof(pvr_file_header);
  154. pvr_file_header header;
  155. unsigned int read = (int)fread(&header, 1, size, file);
  156. assert(read == size);
  157. if (read != size)
  158. {
  159. LOG_ERROR_VARG("Read file header error for pvr file: %s (%d < %d)", path, (int)read, (int)size);
  160. fclose(file);
  161. return NULL;
  162. }
  163. // Proper file header identifier
  164. if (PVRTexIdentifier[0] != (char)((header.pvrTag >> 0) & 0xff) ||
  165. PVRTexIdentifier[1] != (char)((header.pvrTag >> 8) & 0xff) ||
  166. PVRTexIdentifier[2] != (char)((header.pvrTag >> 16) & 0xff) ||
  167. PVRTexIdentifier[3] != (char)((header.pvrTag >> 24) & 0xff))
  168. {
  169. LOG_ERROR_VARG("Invalid PVR texture file: %s", path);
  170. fclose(file);
  171. return NULL;
  172. }
  173. // Format flags for GLenum format
  174. GLenum format;
  175. unsigned int formatFlags = header.formatflags & 0xff;
  176. if (formatFlags == PVRTextureFlagTypePVRTC_4)
  177. {
  178. format = header.alphaBitMask ? COMPRESSED_RGBA_PVRTC_4BPP : COMPRESSED_RGB_PVRTC_4BPP;
  179. }
  180. else if (formatFlags == PVRTextureFlagTypePVRTC_2)
  181. {
  182. format = header.alphaBitMask ? COMPRESSED_RGBA_PVRTC_2BPP : COMPRESSED_RGB_PVRTC_2BPP;
  183. }
  184. else
  185. {
  186. LOG_ERROR_VARG("Invalid PVR texture format flags for file: %s", path);
  187. fclose(file);
  188. return NULL;
  189. }
  190. unsigned char* data = new unsigned char[header.dataSize];
  191. read = (int)fread(data, 1, header.dataSize, file);
  192. assert(read == header.dataSize);
  193. if (read != header.dataSize)
  194. {
  195. LOG_ERROR_VARG("Read file data error for pvr file: %s (%d < %d)", path, (int)read, (int)header.dataSize);
  196. SAFE_DELETE_ARRAY(data);
  197. fclose(file);
  198. return NULL;
  199. }
  200. // Close file
  201. fclose(file);
  202. // Load our texture.
  203. GLuint textureId;
  204. GL_ASSERT( glGenTextures(1, &textureId) );
  205. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
  206. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, header.mipmapCount > 0 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) );
  207. Texture* texture = new Texture();
  208. texture->_handle = textureId;
  209. texture->_width = header.width;
  210. texture->_height = header.height;
  211. // Load the data for each level
  212. unsigned int width = header.width;
  213. unsigned int height = header.height;
  214. unsigned int blockSize = 0;
  215. unsigned int widthBlocks = 0;
  216. unsigned int heightBlocks = 0;
  217. unsigned int bpp = 0;
  218. unsigned int dataSize = 0;
  219. unsigned char* dataOffset = data;
  220. for (unsigned int level = 0; level <= header.mipmapCount; level++)
  221. {
  222. if (formatFlags == PVRTextureFlagTypePVRTC_4)
  223. {
  224. /*
  225. blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
  226. widthBlocks = width / 4;
  227. heightBlocks = height / 4;
  228. bpp = 4;
  229. */
  230. dataSize = ( max((int)width, 8) * max((int)height, 8) * 4 + 7) / 8;
  231. }
  232. else
  233. {
  234. /*
  235. blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
  236. widthBlocks = width / 8;
  237. heightBlocks = height / 4;
  238. bpp = 2;
  239. */
  240. dataSize = ( max((int)width, 16) * max((int)height, 8) * 2 + 7) / 8;
  241. }
  242. /* Clamp to minimum number of blocks
  243. if (widthBlocks < 2)
  244. widthBlocks = 2;
  245. if (heightBlocks < 2)
  246. heightBlocks = 2;
  247. */
  248. //dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
  249. GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, level, (GLenum)format, width, height, 0, dataSize, dataOffset) );
  250. dataOffset += dataSize;
  251. width = max((int)width >> 1, 1);
  252. height = max((int)height >> 1, 1);
  253. }
  254. SAFE_DELETE_ARRAY(data);
  255. return texture;
  256. }
  257. unsigned int Texture::getWidth() const
  258. {
  259. return _width;
  260. }
  261. unsigned int Texture::getHeight() const
  262. {
  263. return _height;
  264. }
  265. TextureHandle Texture::getHandle() const
  266. {
  267. return _handle;
  268. }
  269. void Texture::setWrapMode(Wrap wrapS, Wrap wrapT)
  270. {
  271. GLint currentTextureId;
  272. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  273. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  274. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)wrapS) );
  275. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)wrapT) );
  276. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  277. }
  278. void Texture::setFilterMode(Filter minificationFilter, Filter magnificationFilter)
  279. {
  280. GLint currentTextureId;
  281. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  282. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  283. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLenum)minificationFilter) );
  284. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLenum)magnificationFilter) );
  285. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  286. }
  287. void Texture::generateMipmaps()
  288. {
  289. if (!_mipmapped)
  290. {
  291. GLint currentTextureId;
  292. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  293. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  294. GL_ASSERT( glGenerateMipmap(GL_TEXTURE_2D) );
  295. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  296. _mipmapped = true;
  297. }
  298. }
  299. bool Texture::isMipmapped() const
  300. {
  301. return _mipmapped;
  302. }
  303. Texture::Sampler::Sampler(Texture* texture)
  304. : _texture(texture), _wrapS(Texture::REPEAT), _wrapT(Texture::REPEAT), _magFilter(Texture::LINEAR)
  305. {
  306. _minFilter = texture->isMipmapped() ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR;
  307. }
  308. Texture::Sampler::~Sampler()
  309. {
  310. SAFE_RELEASE(_texture);
  311. }
  312. Texture::Sampler* Texture::Sampler::create(Texture* texture)
  313. {
  314. assert(texture != NULL);
  315. texture->addRef();
  316. return new Sampler(texture);
  317. }
  318. Texture::Sampler* Texture::Sampler::create(const char* path, bool generateMipmaps)
  319. {
  320. Texture* texture = Texture::create(path, generateMipmaps);
  321. return texture ? new Sampler(texture) : NULL;
  322. }
  323. void Texture::Sampler::setWrapMode(Wrap wrapS, Wrap wrapT)
  324. {
  325. _wrapS = wrapS;
  326. _wrapT = wrapT;
  327. }
  328. void Texture::Sampler::setFilterMode(Filter minificationFilter, Filter magnificationFilter)
  329. {
  330. _minFilter = minificationFilter;
  331. _magFilter = magnificationFilter;
  332. }
  333. Texture* Texture::Sampler::getTexture() const
  334. {
  335. return _texture;
  336. }
  337. void Texture::Sampler::bind()
  338. {
  339. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _texture->_handle) );
  340. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)_wrapS) );
  341. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)_wrapT) );
  342. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLenum)_minFilter) );
  343. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLenum)_magFilter) );
  344. }
  345. }