324 lines
9.7 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. #if defined HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include "core.h"
  14. #include "lolgl.h"
  15. #if defined _WIN32 && defined USE_D3D9
  16. # define FAR
  17. # define NEAR
  18. # include <d3d9.h>
  19. #endif
  20. using namespace std;
  21. namespace lol
  22. {
  23. //
  24. // The TextureData class
  25. // ---------------------
  26. //
  27. class TextureData
  28. {
  29. friend class Texture;
  30. ivec2 m_size;
  31. PixelFormat m_format;
  32. #if defined USE_D3D9
  33. IDirect3DDevice9 *m_dev;
  34. IDirect3DTexture9 *m_texture;
  35. D3DTEXTUREFILTERTYPE m_mag_filter;
  36. D3DTEXTUREFILTERTYPE m_min_filter;
  37. D3DTEXTUREFILTERTYPE m_mip_filter;
  38. #elif defined _XBOX
  39. D3DDevice9 *m_dev;
  40. D3DTexture *m_texture;
  41. #else
  42. GLuint m_texture;
  43. GLint m_internal_format;
  44. GLenum m_gl_format, m_gl_type;
  45. #endif
  46. int m_bytes_per_elem;
  47. };
  48. //
  49. // The Texture class
  50. // -----------------
  51. //
  52. #define GET_CLAMPED(array, index) \
  53. array[std::max(0, std::min((int)(index), \
  54. (int)sizeof(array) / (int)sizeof(*array)))]
  55. Texture::Texture(ivec2 size, PixelFormat format)
  56. : m_data(new TextureData)
  57. {
  58. m_data->m_size = size;
  59. m_data->m_format = format;
  60. #if defined USE_D3D9 || defined _XBOX
  61. # if defined USE_D3D9
  62. m_data->m_dev = (IDirect3DDevice9 *)g_renderer->GetDevice();
  63. # elif defined _XBOX
  64. m_data->m_dev = (D3DDevice9 *)g_renderer->GetDevice();
  65. # endif
  66. static struct
  67. {
  68. D3DFORMAT format;
  69. int bytes;
  70. }
  71. const d3d_formats[] =
  72. {
  73. /* Unknown */
  74. { D3DFMT_UNKNOWN, 0 },
  75. /* FIXME: this is all mixed up for the RGBA/ARGB combinations */
  76. # if defined USE_D3D9
  77. { D3DFMT_R8G8B8, 3 }, /* RGB_8 */
  78. { D3DFMT_A8R8G8B8, 4 }, /* RGBA_8 */
  79. { D3DFMT_A8R8G8B8, 4 }, /* ARGB_8 */
  80. { D3DFMT_UNKNOWN, 0 }, /* ABGR_8 */
  81. { D3DFMT_L8, 1 }, /* Y8 */
  82. # else
  83. { D3DFMT_UNKNOWN, 0 },
  84. { D3DFMT_UNKNOWN, 0 },
  85. /* By default the X360 will swizzle the texture. Ask for linear. */
  86. { D3DFMT_LIN_A8R8G8B8, 4 },
  87. { D3DFMT_UNKNOWN, 0 },
  88. { D3DFMT_LIN_L8, 1 },
  89. # endif
  90. };
  91. D3DFORMAT d3d_format = GET_CLAMPED(d3d_formats, format).format;
  92. ASSERT(d3d_format != D3DFMT_UNKNOWN,
  93. "unsupported texture format %d\n", format);
  94. # if defined USE_D3D9
  95. int d3d_usage = D3DUSAGE_DYNAMIC;
  96. # else
  97. int d3d_usage = D3DUSAGE_WRITEONLY;
  98. # endif
  99. m_data->m_dev->CreateTexture(m_data->m_size.x, m_data->m_size.y, 1,
  100. d3d_usage, d3d_format,
  101. D3DPOOL_DEFAULT, &m_data->m_texture, nullptr);
  102. m_data->m_bytes_per_elem = GET_CLAMPED(d3d_formats, format).bytes;
  103. #else
  104. static struct
  105. {
  106. GLint internal_format;
  107. GLenum format, type;
  108. int bytes;
  109. }
  110. const gl_formats[] =
  111. {
  112. { 0, 0, 0, 0 }, /* Unknown */
  113. /* FIXME: this is all mixed up for the RGBA/ARGB combinations */
  114. #if __CELLOS_LV2__
  115. { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 },
  116. { GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 },
  117. { GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4 },
  118. { GL_ARGB_SCE, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 4 },
  119. { GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 },
  120. #elif defined __native_client__ || defined HAVE_GLES_2X
  121. { GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 3 },
  122. { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 },
  123. { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 },
  124. /* FIXME: if GL_RGBA is not available, we should advertise
  125. * this format as "not available" on this platform. */
  126. { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 4 },
  127. { GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 },
  128. #else
  129. { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 3 }, /* RGB_8 */
  130. /* Seems efficient for little endian textures */
  131. { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */
  132. { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ARGB_8 */
  133. { GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, /* ABGR_8 */
  134. { GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, /* A8 */
  135. #endif
  136. };
  137. m_data->m_internal_format = GET_CLAMPED(gl_formats, format).internal_format;
  138. m_data->m_gl_format = GET_CLAMPED(gl_formats, format).format;
  139. m_data->m_gl_type = GET_CLAMPED(gl_formats, format).type;
  140. m_data->m_bytes_per_elem = GET_CLAMPED(gl_formats, format).bytes;
  141. glGenTextures(1, &m_data->m_texture);
  142. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  143. # if defined __CELLOS_LV2__
  144. /* We need this hint because by default the storage type is
  145. * GL_TEXTURE_SWIZZLED_GPU_SCE. */
  146. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ALLOCATION_HINT_SCE,
  147. GL_TEXTURE_TILED_GPU_SCE);
  148. # endif
  149. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  150. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  151. #endif
  152. }
  153. ShaderTexture Texture::GetTexture() const
  154. {
  155. ShaderTexture ret;
  156. #if defined USE_D3D9 || defined _XBOX
  157. ret.m_flags = (uint64_t)(uintptr_t)m_data->m_texture;
  158. ret.m_attrib = m_data->m_mag_filter;
  159. ret.m_attrib |= m_data->m_min_filter << 8;
  160. ret.m_attrib |= m_data->m_mip_filter << 16;
  161. #else
  162. ret.m_flags = m_data->m_texture;
  163. #endif
  164. return ret;
  165. }
  166. void Texture::Bind()
  167. {
  168. #if defined _XBOX || defined USE_D3D9
  169. m_data->m_dev->SetTexture(0, m_data->m_texture);
  170. #else
  171. # if !defined HAVE_GLES_2X
  172. glEnable(GL_TEXTURE_2D);
  173. # endif
  174. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  175. #endif
  176. }
  177. void Texture::SetData(void *data)
  178. {
  179. #if defined _XBOX || defined USE_D3D9
  180. D3DLOCKED_RECT rect;
  181. # if defined USE_D3D9
  182. m_data->m_texture->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD);
  183. # else
  184. m_data->m_texture->LockRect(0, &rect, nullptr, 0);
  185. # endif
  186. memcpy(rect.pBits, data, rect.Pitch * m_data->m_size.y);
  187. m_data->m_texture->UnlockRect(0);
  188. #else
  189. glTexImage2D(GL_TEXTURE_2D, 0, m_data->m_internal_format,
  190. m_data->m_size.x, m_data->m_size.y, 0,
  191. m_data->m_gl_format, m_data->m_gl_type, data);
  192. #endif
  193. }
  194. void Texture::SetSubData(ivec2 origin, ivec2 size, void *data)
  195. {
  196. #if defined _XBOX || defined USE_D3D9
  197. D3DLOCKED_RECT rect;
  198. m_data->m_texture->LockRect(0, &rect, nullptr, 0);
  199. int stride = size.x * m_data->m_bytes_per_elem;
  200. for (int j = 0; j < size.y; j++)
  201. {
  202. uint8_t *dst = (uint8_t *)rect.pBits + (origin.y + j) * rect.Pitch;
  203. uint8_t *src = (uint8_t *)data + j * stride;
  204. memcpy(dst, src, stride);
  205. }
  206. m_data->m_texture->UnlockRect(0);
  207. #else
  208. glTexSubImage2D(GL_TEXTURE_2D, 0, origin.x, origin.y, size.x, size.y,
  209. m_data->m_gl_format, m_data->m_gl_type, data);
  210. #endif
  211. }
  212. void Texture::SetMagFiltering(TextureMagFilter filter)
  213. {
  214. #if defined _XBOX || defined USE_D3D9
  215. // In DirectX, texture filtering is a per-texture-unit state
  216. switch (filter)
  217. {
  218. case TextureMagFilter::NEAREST_TEXEL: m_data->m_mag_filter = D3DTEXF_POINT; break;
  219. case TextureMagFilter::LINEAR_TEXEL: m_data->m_mag_filter = D3DTEXF_LINEAR; break;
  220. }
  221. #else
  222. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  223. GLenum gl_filter;
  224. switch (filter)
  225. {
  226. case TextureMagFilter::NEAREST_TEXEL: gl_filter = GL_NEAREST; break;
  227. case TextureMagFilter::LINEAR_TEXEL: gl_filter = GL_LINEAR; break;
  228. }
  229. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter);
  230. #endif
  231. }
  232. void Texture::SetMinFiltering(TextureMinFilter filter)
  233. {
  234. #if defined _XBOX || defined USE_D3D9
  235. // In DirectX, texture filtering is a per-texture-unit state
  236. #define F(x, y) \
  237. m_data->m_min_filter = x; m_data->m_mip_filter = y;
  238. switch (filter)
  239. {
  240. case TextureMinFilter::NEAREST_TEXEL_NO_MIPMAP: F(D3DTEXF_POINT, D3DTEXF_NONE); break;
  241. case TextureMinFilter::LINEAR_TEXEL_NO_MIPMAP: F(D3DTEXF_POINT, D3DTEXF_NONE); break;
  242. case TextureMinFilter::NEAREST_TEXEL_NEAREST_MIPMAP: F(D3DTEXF_POINT, D3DTEXF_POINT); break;
  243. case TextureMinFilter::LINEAR_TEXEL_NEAREST_MIPMAP: F(D3DTEXF_LINEAR, D3DTEXF_POINT); break;
  244. case TextureMinFilter::NEAREST_TEXEL_LINEAR_MIPMAP: F(D3DTEXF_POINT, D3DTEXF_LINEAR); break;
  245. case TextureMinFilter::LINEAR_TEXEL_LINEAR_MIPMAP: F(D3DTEXF_LINEAR, D3DTEXF_LINEAR); break;
  246. }
  247. #undef F
  248. #else
  249. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  250. GLenum gl_filter;
  251. switch (filter)
  252. {
  253. case TextureMinFilter::NEAREST_TEXEL_NO_MIPMAP: gl_filter = GL_NEAREST; break;
  254. case TextureMinFilter::LINEAR_TEXEL_NO_MIPMAP: gl_filter = GL_LINEAR; break;
  255. case TextureMinFilter::NEAREST_TEXEL_NEAREST_MIPMAP: gl_filter = GL_NEAREST_MIPMAP_NEAREST; break;
  256. case TextureMinFilter::NEAREST_TEXEL_LINEAR_MIPMAP: gl_filter = GL_NEAREST_MIPMAP_LINEAR; break;
  257. case TextureMinFilter::LINEAR_TEXEL_NEAREST_MIPMAP: gl_filter = GL_LINEAR_MIPMAP_NEAREST; break;
  258. case TextureMinFilter::LINEAR_TEXEL_LINEAR_MIPMAP: gl_filter = GL_LINEAR_MIPMAP_LINEAR; break;
  259. }
  260. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter);
  261. #endif
  262. }
  263. void Texture::GenerateMipmaps()
  264. {
  265. #if defined _XBOX || defined USE_D3D9
  266. m_data->m_texture->GenerateMipSubLevels();
  267. #elif defined __CELLOS_LV2__
  268. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  269. glGenerateMipmapOES(GL_TEXTURE_2D);
  270. #else
  271. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  272. glGenerateMipmap(GL_TEXTURE_2D);
  273. #endif
  274. }
  275. Texture::~Texture()
  276. {
  277. #if defined USE_D3D9 || defined _XBOX
  278. m_data->m_texture->Release();
  279. #else
  280. glDeleteTextures(1, &m_data->m_texture);
  281. #endif
  282. delete m_data;
  283. }
  284. } /* namespace lol */