Texture.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. * Texture.cpp
  3. */
  4. #include "Base.h"
  5. #include "Texture.h"
  6. #include "FileSystem.h"
  7. namespace gameplay
  8. {
  9. static std::vector<Texture*> __textureCache;
  10. Texture::Texture() : _handle(0), _mipmapped(false), _cached(false)
  11. {
  12. }
  13. Texture::Texture(const Texture& copy)
  14. {
  15. }
  16. Texture::~Texture()
  17. {
  18. if (_handle)
  19. {
  20. glDeleteTextures(1, &_handle);
  21. _handle = 0;
  22. }
  23. // Remove ourself from the texture cache.
  24. if (_cached)
  25. {
  26. std::vector<Texture*>::iterator itr = std::find(__textureCache.begin(), __textureCache.end(), this);
  27. if (itr != __textureCache.end())
  28. {
  29. __textureCache.erase(itr);
  30. }
  31. }
  32. }
  33. Texture* Texture::create(const char* path, bool generateMipmaps)
  34. {
  35. // Search texture cache first.
  36. for (unsigned int i = 0, count = __textureCache.size(); i < count; ++i)
  37. {
  38. Texture* t = __textureCache[i];
  39. if (t->_path == path)
  40. {
  41. // If 'generateMipmaps' is true, call Texture::generateMipamps() to force the
  42. // texture to generate its mipmap chain if it hasn't already done so.
  43. if (generateMipmaps)
  44. {
  45. t->generateMipmaps();
  46. }
  47. // Found a match.
  48. t->addRef();
  49. return t;
  50. }
  51. }
  52. Texture* texture = NULL;
  53. // Filter loading based on file extension.
  54. const char* ext = strrchr(path, '.');
  55. if (ext)
  56. {
  57. switch (strlen(ext))
  58. {
  59. case 4:
  60. if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'n' && tolower(ext[3]) == 'g')
  61. {
  62. texture = loadPNG(path, generateMipmaps);
  63. }
  64. break;
  65. }
  66. }
  67. if (texture)
  68. {
  69. texture->_path = path;
  70. texture->_cached = true;
  71. // Add to texture cache.
  72. __textureCache.push_back(texture);
  73. return texture;
  74. }
  75. LOG_ERROR_VARG("Failed to load texture: %s", path);
  76. return NULL;
  77. }
  78. Texture* Texture::loadPNG(const char* path, bool generateMipmaps)
  79. {
  80. // Open the file.
  81. FILE* fp = FileSystem::openFile(path, "rb");
  82. if (fp == NULL)
  83. {
  84. return NULL;
  85. }
  86. // Verify PNG signature.
  87. unsigned char sig[8];
  88. if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
  89. {
  90. LOG_ERROR_VARG("Texture is not a valid PNG: %s", path);
  91. fclose(fp);
  92. return NULL;
  93. }
  94. // Initialize png read struct (last three parameters use stderr+longjump if NULL).
  95. png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  96. if (png == NULL)
  97. {
  98. fclose(fp);
  99. return NULL;
  100. }
  101. // Initialize info struct.
  102. png_infop info = png_create_info_struct(png);
  103. if (info == NULL)
  104. {
  105. fclose(fp);
  106. png_destroy_read_struct(&png, NULL, NULL);
  107. return NULL;
  108. }
  109. // Set up error handling (required without using custom error handlers above).
  110. if (setjmp(png_jmpbuf(png)))
  111. {
  112. fclose(fp);
  113. png_destroy_read_struct(&png, &info, NULL);
  114. return NULL;
  115. }
  116. // Initialize file io.
  117. png_init_io(png, fp);
  118. // Indicate that we already read the first 8 bytes (signature).
  119. png_set_sig_bytes(png, 8);
  120. // Read the entire image into memory.
  121. png_read_png(png, info, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
  122. unsigned int width = png_get_image_width(png, info);
  123. unsigned int height = png_get_image_height(png, info);
  124. png_byte colorType = png_get_color_type(png, info);
  125. Format format;
  126. switch (colorType)
  127. {
  128. case PNG_COLOR_TYPE_RGBA:
  129. format = RGBA8888;
  130. break;
  131. case PNG_COLOR_TYPE_RGB:
  132. format = RGB888;
  133. break;
  134. default:
  135. LOG_ERROR_VARG("Unsupported PNG color type (%d) for texture: %s", (int)colorType, path);
  136. fclose(fp);
  137. png_destroy_read_struct(&png, &info, NULL);
  138. return NULL;
  139. }
  140. unsigned int stride = png_get_rowbytes(png, info);
  141. // Allocate image data.
  142. unsigned char* data = new unsigned char[stride * height];
  143. // Read rows into image data.
  144. png_bytepp rows = png_get_rows(png, info);
  145. for (unsigned int i = 0; i < height; ++i)
  146. {
  147. memcpy(data+(stride * (height-1-i)), rows[i], stride);
  148. }
  149. // Clean up.
  150. png_destroy_read_struct(&png, &info, NULL);
  151. fclose(fp);
  152. // Create texture.
  153. Texture* texture = create(format, width, height, data, generateMipmaps);
  154. // Free temporary data.
  155. SAFE_DELETE_ARRAY(data);
  156. return texture;
  157. }
  158. Texture* Texture::create(Format format, unsigned int width, unsigned int height, unsigned char* data, bool generateMipmaps)
  159. {
  160. // Load our texture.
  161. GLuint textureId;
  162. GL_ASSERT( glGenTextures(1, &textureId) );
  163. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
  164. if (format == DEPTH)
  165. {
  166. // <type> must be UNSIGNED_SHORT or UNSIGNED_INT for a format of DEPTH_COMPONENT.
  167. GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_INT, data) );
  168. }
  169. else
  170. {
  171. GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, data) );
  172. }
  173. // Set initial minification filter based on whether or not mipmaping was enabled
  174. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generateMipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
  175. Texture* texture = new Texture();
  176. texture->_handle = textureId;
  177. texture->_width = width;
  178. texture->_height = height;
  179. if (generateMipmaps)
  180. {
  181. texture->generateMipmaps();
  182. }
  183. return texture;
  184. }
  185. unsigned int Texture::getWidth() const
  186. {
  187. return _width;
  188. }
  189. unsigned int Texture::getHeight() const
  190. {
  191. return _height;
  192. }
  193. TextureHandle Texture::getHandle() const
  194. {
  195. return _handle;
  196. }
  197. void Texture::setWrapMode(Wrap wrapS, Wrap wrapT)
  198. {
  199. GLint currentTextureId;
  200. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  201. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  202. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)wrapS) );
  203. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)wrapT) );
  204. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  205. }
  206. void Texture::setFilterMode(Filter minificationFilter, Filter magnificationFilter)
  207. {
  208. GLint currentTextureId;
  209. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  210. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  211. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLenum)minificationFilter) );
  212. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLenum)magnificationFilter) );
  213. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  214. }
  215. void Texture::generateMipmaps()
  216. {
  217. if (!_mipmapped)
  218. {
  219. GLint currentTextureId;
  220. GL_ASSERT( glGetIntegerv(GL_TEXTURE_BINDING_2D, &currentTextureId) );
  221. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
  222. GL_ASSERT( glGenerateMipmap(GL_TEXTURE_2D) );
  223. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, (GLuint)currentTextureId) );
  224. _mipmapped = true;
  225. }
  226. }
  227. bool Texture::isMipmapped() const
  228. {
  229. return _mipmapped;
  230. }
  231. Texture::Sampler::Sampler(Texture* texture)
  232. : _texture(texture), _wrapS(Texture::REPEAT), _wrapT(Texture::REPEAT), _magFilter(Texture::LINEAR)
  233. {
  234. _minFilter = texture->isMipmapped() ? Texture::NEAREST_MIPMAP_LINEAR : Texture::LINEAR;
  235. }
  236. Texture::Sampler::~Sampler()
  237. {
  238. SAFE_RELEASE(_texture);
  239. }
  240. Texture::Sampler* Texture::Sampler::create(Texture* texture)
  241. {
  242. assert(texture != NULL);
  243. texture->addRef();
  244. return new Sampler(texture);
  245. }
  246. Texture::Sampler* Texture::Sampler::create(const char* path, bool generateMipmaps)
  247. {
  248. Texture* texture = Texture::create(path, generateMipmaps);
  249. return texture ? new Sampler(texture) : NULL;
  250. }
  251. void Texture::Sampler::setWrapMode(Wrap wrapS, Wrap wrapT)
  252. {
  253. _wrapS = wrapS;
  254. _wrapT = wrapT;
  255. }
  256. void Texture::Sampler::setFilterMode(Filter minificationFilter, Filter magnificationFilter)
  257. {
  258. _minFilter = minificationFilter;
  259. _magFilter = magnificationFilter;
  260. }
  261. Texture* Texture::Sampler::getTexture() const
  262. {
  263. return _texture;
  264. }
  265. void Texture::Sampler::bind()
  266. {
  267. GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _texture->_handle) );
  268. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLenum)_wrapS) );
  269. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLenum)_wrapT) );
  270. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLenum)_minFilter) );
  271. GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLenum)_magFilter) );
  272. }
  273. }