Framebuffer.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. #include "Framebuffer.h"
  2. #include <common/Matrix.h>
  3. #include <cstring> // For memcpy
  4. namespace love
  5. {
  6. namespace graphics
  7. {
  8. namespace opengl
  9. {
  10. // strategy for fbo creation, interchangable at runtime:
  11. // none, opengl >= 3.0, extensions
  12. struct FramebufferStrategy {
  13. /// create a new framebuffer, depthbuffer and texture
  14. /**
  15. * @param[out] framebuffer Framebuffer name
  16. * @param[out] depthbuffer Depthbuffer name
  17. * @param[out] img Texture name
  18. * @param[in] width Width of framebuffer
  19. * @param[in] height Height of framebuffer
  20. * @return Creation status
  21. */
  22. virtual GLenum createFBO(GLuint&, GLuint&, GLuint&, int, int)
  23. { return GL_FRAMEBUFFER_UNSUPPORTED; }
  24. /// remove objects
  25. /**
  26. * @param[in] framebuffer Framebuffer name
  27. * @param[in] depthbuffer Depthbuffer name
  28. * @param[in] img Texture name
  29. */
  30. virtual void deleteFBO(GLuint, GLuint, GLuint) {}
  31. virtual void bindFBO(GLuint) {}
  32. };
  33. struct FramebufferStrategyGL3 : public FramebufferStrategy {
  34. virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height)
  35. {
  36. // get currently bound fbo to reset to it later
  37. GLint current_fbo;
  38. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
  39. // generate depth buffer
  40. glGenRenderbuffers(1, &depthbuffer);
  41. glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer);
  42. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
  43. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  44. // generate texture save target
  45. glGenTextures(1, &img);
  46. glBindTexture(GL_TEXTURE_2D, img);
  47. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  48. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  49. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
  50. 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  51. glBindTexture(GL_TEXTURE_2D, 0);
  52. // create framebuffer
  53. glGenFramebuffers(1, &framebuffer);
  54. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  55. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  56. GL_TEXTURE_2D, img, 0);
  57. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  58. GL_RENDERBUFFER, depthbuffer);
  59. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  60. // unbind framebuffer
  61. glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)current_fbo);
  62. return status;
  63. }
  64. virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img)
  65. {
  66. glDeleteTextures(1, &img);
  67. glDeleteRenderbuffers(1, &depthbuffer);
  68. glDeleteFramebuffers(1, &framebuffer);
  69. }
  70. virtual void bindFBO(GLuint framebuffer)
  71. {
  72. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  73. }
  74. };
  75. struct FramebufferStrategyEXT : public FramebufferStrategy {
  76. virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height)
  77. {
  78. GLint current_fbo;
  79. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
  80. // generate depth buffer
  81. glGenRenderbuffersEXT(1, &depthbuffer);
  82. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);
  83. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, width, height);
  84. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  85. // generate texture save target
  86. glGenTextures(1, &img);
  87. glBindTexture(GL_TEXTURE_2D, img);
  88. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  89. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  90. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
  91. 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  92. glBindTexture(GL_TEXTURE_2D, 0);
  93. // create framebuffer
  94. glGenFramebuffersEXT(1, &framebuffer);
  95. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  96. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
  97. GL_TEXTURE_2D, img, 0);
  98. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
  99. GL_RENDERBUFFER_EXT, depthbuffer);
  100. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  101. // unbind framebuffer
  102. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)current_fbo);
  103. return status;
  104. }
  105. virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img)
  106. {
  107. glDeleteTextures(1, &img);
  108. glDeleteRenderbuffersEXT(1, &depthbuffer);
  109. glDeleteFramebuffersEXT(1, &framebuffer);
  110. }
  111. virtual void bindFBO(GLuint framebuffer)
  112. {
  113. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  114. }
  115. };
  116. FramebufferStrategy* strategy;
  117. FramebufferStrategy strategyNone;
  118. FramebufferStrategyGL3 strategyGL3;
  119. FramebufferStrategyEXT strategyEXT;
  120. Framebuffer* Framebuffer::current = NULL;
  121. Framebuffer::Framebuffer(int width, int height) :
  122. width(width), height(height)
  123. {
  124. strategy = NULL;
  125. float w = static_cast<float>(width);
  126. float h = static_cast<float>(height);
  127. // world coordinates
  128. vertices[0].x = 0; vertices[0].y = 0;
  129. vertices[1].x = 0; vertices[1].y = h;
  130. vertices[2].x = w; vertices[2].y = h;
  131. vertices[3].x = w; vertices[3].y = 0;
  132. // texture coordinates
  133. vertices[0].s = 0; vertices[0].t = 1;
  134. vertices[1].s = 0; vertices[1].t = 0;
  135. vertices[2].s = 1; vertices[2].t = 0;
  136. vertices[3].s = 1; vertices[3].t = 1;
  137. if (!strategy) {
  138. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
  139. strategy = &strategyGL3;
  140. else if (GLEE_EXT_framebuffer_object)
  141. strategy = &strategyEXT;
  142. else
  143. strategy = &strategyNone;
  144. }
  145. loadVolatile();
  146. }
  147. Framebuffer::~Framebuffer()
  148. {
  149. // reset framebuffer if still using this one
  150. if (current == this)
  151. stopGrab();
  152. unloadVolatile();
  153. }
  154. void Framebuffer::bindDefaultBuffer()
  155. {
  156. if (current != NULL)
  157. current->stopGrab();
  158. }
  159. void Framebuffer::startGrab()
  160. {
  161. // already grabbing
  162. if (current == this)
  163. return;
  164. // cleanup after previous fbo
  165. if (current != NULL)
  166. current->stopGrab();
  167. // bind buffer and clear screen
  168. glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_TRANSFORM_BIT);
  169. strategy->bindFBO(fbo);
  170. glClearColor(.0f, .0f, .0f, .0f);
  171. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  172. glViewport(0, 0, width, height);
  173. // Reset the projection matrix
  174. glMatrixMode(GL_PROJECTION);
  175. glPushMatrix();
  176. glLoadIdentity();
  177. // Set up orthographic view (no depth)
  178. glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
  179. // Switch back to modelview matrix
  180. glMatrixMode(GL_MODELVIEW);
  181. // indicate we are using this fbo
  182. current = this;
  183. }
  184. void Framebuffer::stopGrab()
  185. {
  186. // i am not grabbing. leave me alone
  187. if (current != this)
  188. return;
  189. // bind default
  190. strategy->bindFBO( 0 );
  191. glMatrixMode(GL_PROJECTION);
  192. glPopMatrix();
  193. glPopAttrib();
  194. current = NULL;
  195. }
  196. void Framebuffer::draw(float x, float y, float angle, float sx, float sy, float ox, float oy) const
  197. {
  198. static Matrix t;
  199. t.setTransformation(x, y, angle, sx, sy, ox, oy);
  200. glPushMatrix();
  201. glMultMatrixf((const GLfloat*)t.getElements());
  202. glBindTexture(GL_TEXTURE_2D, img);
  203. glEnableClientState(GL_VERTEX_ARRAY);
  204. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  205. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].x);
  206. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].s);
  207. glDrawArrays(GL_QUADS, 0, 4);
  208. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  209. glDisableClientState(GL_VERTEX_ARRAY);
  210. glPopMatrix();
  211. }
  212. love::image::ImageData * Framebuffer::getImageData(love::image::Image * image)
  213. {
  214. int row = 4 * width;
  215. int size = row * height;
  216. // see Graphics::newScreenshot. OpenGL reads from lower-left,
  217. // but we need the pixels from upper-left.
  218. GLubyte* pixels = new GLubyte[size];
  219. GLubyte* screenshot = new GLubyte[size];
  220. strategy->bindFBO( fbo );
  221. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  222. if (current)
  223. strategy->bindFBO( current->fbo );
  224. else
  225. strategy->bindFBO( 0 );
  226. GLubyte* src = pixels - row; // second line of buffer
  227. GLubyte* dst = screenshot + size; // end of buffer
  228. for (int i = 0; i < height; ++i)
  229. memcpy(dst -= row, src += row, row);
  230. love::image::ImageData * img = image->newImageData(width, height, (void*)screenshot);
  231. delete[] screenshot;
  232. delete[] pixels;
  233. return img;
  234. }
  235. void Framebuffer::setFilter(Image::Filter f)
  236. {
  237. GLint gmin = (f.min == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
  238. GLint gmag = (f.mag == Image::FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR;
  239. glBindTexture(GL_TEXTURE_2D, img);
  240. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
  241. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
  242. }
  243. Image::Filter Framebuffer::getFilter() const
  244. {
  245. GLint gmin, gmag;
  246. glBindTexture(GL_TEXTURE_2D, img);
  247. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
  248. glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
  249. Image::Filter f;
  250. f.min = (gmin == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
  251. f.mag = (gmag == GL_NEAREST) ? Image::FILTER_NEAREST : Image::FILTER_LINEAR;
  252. return f;
  253. }
  254. bool Framebuffer::loadVolatile()
  255. {
  256. status = strategy->createFBO(fbo, depthbuffer, img, width, height);
  257. if (status == GL_FRAMEBUFFER_COMPLETE)
  258. setFilter(settings.filter);
  259. return (status == GL_FRAMEBUFFER_COMPLETE);
  260. }
  261. void Framebuffer::unloadVolatile()
  262. {
  263. settings.filter = getFilter();
  264. strategy->deleteFBO(fbo, depthbuffer, img);
  265. }
  266. } // opengl
  267. } // graphics
  268. } // love