/src/org/mt4j/util/opengl/GLFBO.java

http://mt4j.googlecode.com/ · Java · 562 lines · 259 code · 101 blank · 202 comment · 10 complexity · 99482506a3cfca6eebf7d89414b2a4dc MD5 · raw file

  1. /***********************************************************************
  2. * mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. ***********************************************************************/
  18. package org.mt4j.util.opengl;
  19. import java.nio.IntBuffer;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import javax.media.opengl.GL;
  23. import org.mt4j.MTApplication;
  24. import org.mt4j.util.logging.ILogger;
  25. import org.mt4j.util.logging.MTLoggerFactory;
  26. import org.mt4j.util.math.Tools3D;
  27. import org.mt4j.util.math.ToolsBuffers;
  28. import org.mt4j.util.math.ToolsMath;
  29. import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER;
  30. import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER;
  31. import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET;
  32. import org.mt4j.util.opengl.GLTexture.WRAP_MODE;
  33. import processing.core.PApplet;
  34. import processing.opengl.PGraphicsOpenGL;
  35. /**
  36. * This class abstracts a opengl frame buffer object for easier usage.
  37. * This can mainly be used to draw to an offscreen buffer and use that buffer
  38. * as a texture later.
  39. *
  40. * @author Christopher Ruff
  41. */
  42. public class GLFBO {
  43. /** The Constant logger. */
  44. private static final ILogger logger = MTLoggerFactory.getLogger(GLFBO.class.getName());
  45. static{
  46. // logger.setLevel(ILogger.ERROR);
  47. // logger.setLevel(ILogger.WARN);
  48. // logger.setLevel(ILogger.DEBUG);
  49. logger.setLevel(ILogger.INFO);
  50. }
  51. private GL gl;
  52. // private int[] fboID;
  53. private int fboID;
  54. private int depthRBID;
  55. private int width;
  56. private int height;
  57. private PApplet pa;
  58. private List<GLTexture> textures;
  59. private int viewportX;
  60. private int viewportY;
  61. private int viewportWidth;
  62. private int viewportHeight;
  63. private boolean stencilBufferAttached;
  64. private GLFboStack fboStack;
  65. /**
  66. * Instantiates a new GL FBO.
  67. *
  68. * @param pa the pa
  69. * @param width the width
  70. * @param height the height
  71. */
  72. public GLFBO(PApplet pa, int width, int height) {
  73. this(pa, width, height, true);
  74. }
  75. /**
  76. * Instantiates a new GL FBO.
  77. *
  78. * @param pa the pa
  79. * @param width the width
  80. * @param height the height
  81. * @param attachStencilBuffer the attach stencil buffer
  82. */
  83. public GLFBO(PApplet pa, int width, int height, boolean attachStencilBuffer) {
  84. super();
  85. this.pa = pa;
  86. this.gl = ((PGraphicsOpenGL)pa.g).gl;
  87. this.stencilBufferAttached = attachStencilBuffer;
  88. this.fboID = 0;
  89. this.depthRBID = 0;
  90. this.viewportX = 0;
  91. this.viewportY = 0;
  92. this.viewportWidth = width;
  93. this.viewportHeight = height;
  94. this.width = width;
  95. this.height = height;
  96. this.textures = new ArrayList<GLTexture>();
  97. //FIXME FBO STACK TEST!!
  98. this.fboStack = GLFboStack.getInstance();
  99. this.initFBO();
  100. }
  101. private void initFBO(){
  102. IntBuffer buffer = ToolsBuffers.createIntBuffer(1);
  103. gl.glGenFramebuffersEXT(1, buffer);
  104. this.fboID = buffer.get(0);
  105. gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboID);
  106. //Create depth buffer
  107. IntBuffer buffer2 = ToolsBuffers.createIntBuffer(1);
  108. gl.glGenRenderbuffersEXT(1, buffer2);
  109. this.depthRBID = buffer2.get(0);
  110. gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, depthRBID);
  111. if (this.isStencilBufferAttached()){
  112. //THIS CREATES A FBO WITH A STENCIL BUFFER! HAS TO BE SUPPORTED ON THE PLATFORM!
  113. gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH24_STENCIL8_EXT, this.width, this.height);
  114. }else{
  115. //Creates a fbo with a depth but without a stencil buffer
  116. gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT, this.width, this.height); //orginal
  117. }
  118. //Attach depth buffer to FBO
  119. gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthRBID);
  120. if (this.isStencilBufferAttached()){
  121. //Attach stencil buffer to FBO - HAS TO BE SUPPORTED ON THE PLATFORM!
  122. gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_STENCIL_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthRBID);
  123. }
  124. gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
  125. }
  126. /**
  127. * Attaches a gl texture object to the frame buffer object.
  128. * The texture will contain everything drawn when the fbo is bound.
  129. *
  130. * @return the gL texture
  131. */
  132. public GLTexture addNewTexture(){
  133. return this.addNewTexture(false);
  134. }
  135. /**
  136. * This creates a GLTexture object with the FBO dimensions and
  137. * attaches it to this frame buffer object.
  138. * The texture can later be used to texture a component.
  139. *
  140. * @return the gL texture
  141. */
  142. public GLTexture addNewTexture(boolean useMipMap){
  143. this.bind();
  144. boolean isPowerOfTwoDimension = ToolsMath.isPowerOfTwo(this.width) && ToolsMath.isPowerOfTwo(this.height);
  145. GLTextureSettings texSettings = new GLTextureSettings();
  146. texSettings.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE;
  147. texSettings.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE;
  148. texSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
  149. texSettings.expansionFilter = EXPANSION_FILTER.Bilinear;
  150. // GLTextureParameters tp = new GLTextureParameters();
  151. // //Set texture FILTER MODE
  152. //// tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_LINEAR;
  153. //// tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_NEAREST;
  154. // tp.minFilter = GLTextureParameters.LINEAR;
  155. //// tp.minFilter = GLTextureParameters.NEAREST;
  156. //// if (useMipMap)
  157. //// tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_NEAREST; //Seems to not display the fbo texture when POT
  158. //// else{
  159. //// tp.minFilter = GLTextureParameters.LINEAR;
  160. //// }
  161. // tp.magFilter = GLTextureParameters.LINEAR;
  162. //
  163. // //Set texture WRAP MODE
  164. // tp.wrap_s = GL.GL_CLAMP_TO_EDGE;
  165. // tp.wrap_t = GL.GL_CLAMP_TO_EDGE;
  166. //// tp.wrap_s = GL.GL_CLAMP;
  167. //// tp.wrap_t = GL.GL_CLAMP;
  168. //Set texture TARGET
  169. if (isPowerOfTwoDimension){
  170. // tp.target = GLTextureParameters.NORMAL;
  171. texSettings.target = TEXTURE_TARGET.TEXTURE_2D;
  172. logger.debug("Power of 2 FBO texture created");
  173. }else{
  174. // tp.target = GLTextureParameters.RECTANGULAR;
  175. texSettings.target = TEXTURE_TARGET.RECTANGULAR;
  176. logger.debug("Rectangular FBO texture created");
  177. }
  178. // GLTexture tex = new GLTexture(pa, this.width, this.height, tp, true, 0);
  179. GLTexture tex = new GLTexture(this.pa, texSettings);
  180. tex.setupGLTexture(this.width, this.height);
  181. tex.width = this.width;
  182. tex.height = this.height; //To prevent init() call in loadGLTexture().. not even neccessary with fbo usage?
  183. gl.glBindTexture(tex.getTextureTarget(), tex.getTextureID());
  184. //Use extension to automatically generate mipmaps for the fbo textures
  185. //TODO Is this always needed? supported? only working in power of two dimensions!?
  186. // if (useMipMap && isPowerOfTwoDimension) //only for target GL_TEXTURE_2D allowed
  187. // gl.glGenerateMipmapEXT(tex.getTextureTarget()); //FIXME seems to crash JVM after app close! //FIXME do this only after drawing finished to fbo texture
  188. //Attach texture to FBO
  189. gl.glFramebufferTexture2DEXT(
  190. GL.GL_FRAMEBUFFER_EXT,
  191. GL.GL_COLOR_ATTACHMENT0_EXT,
  192. tex.getTextureTarget(), tex.getTextureID(), 0);
  193. gl.glBindTexture(tex.getTextureTarget(), 0);
  194. this.checkFBOComplete(gl, fboID);
  195. this.unBind();
  196. //Add to list
  197. this.textures.add(tex);
  198. return tex;
  199. }
  200. //TEXTURE HAS TO HAVE FBO DIMENSIONS TO WORK! THIS REPLACED THE OLD ATTACHED TEXTURE!
  201. public boolean add(GLTexture tex) {
  202. this.bind();
  203. gl.glBindTexture(tex.getTextureTarget(), tex.getTextureID());
  204. /*
  205. //F?r OHNE mipmapping
  206. gl.glTexParameteri(tex.getTextureTarget(),GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
  207. gl.glTexParameteri(tex.getTextureTarget(),GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
  208. */
  209. // /*
  210. //F?r MIIPMAPPING
  211. // gl.glTexParameteri(tex.getTextureTarget(), GL.GL_GENERATE_MIPMAP, GL.GL_TRUE); // automatic mipmap
  212. gl.glTexParameterf(tex.getTextureTarget(), GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
  213. gl.glTexParameterf(tex.getTextureTarget(), GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
  214. gl.glTexParameteri(tex.getTextureTarget(), GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
  215. gl.glTexParameteri(tex.getTextureTarget(), GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
  216. gl.glGenerateMipmapEXT(tex.getTextureTarget());
  217. // */
  218. //Attach texture to FBO
  219. gl.glFramebufferTexture2DEXT(
  220. GL.GL_FRAMEBUFFER_EXT,
  221. GL.GL_COLOR_ATTACHMENT0_EXT,
  222. tex.getTextureTarget(), tex.getTextureID(), 0);
  223. gl.glBindTexture(tex.getTextureTarget(), 0);
  224. this.checkFBOComplete(gl, fboID);
  225. this.unBind();
  226. return textures.add(tex);
  227. }
  228. public boolean contains(GLTexture arg0) {
  229. return textures.contains(arg0);
  230. }
  231. public boolean remove(GLTexture arg0) {
  232. return textures.remove(arg0);
  233. }
  234. /*
  235. In this instance we are creating a normal RGBA image of the same width and height as the renderbuffer we created earlier;
  236. this is important as ALL attachments to a FBO have to be the same width and height.
  237. Note that we don?t upload any data, the space is just reserved by OpenGL so we can use it later.
  238. */
  239. /**
  240. * If clearColorBuffer is set to true this clears the colorbuffer with the specified color values.
  241. * <br>If clearDepthBuffer is set to true this clears the depth buffer of the framebuffer object.
  242. * <br>NOTE: It seems that a texture has to be attached to the FBO first, for this to work as expected.
  243. * <br>NOTE: This method will bind and unbind the FBO! So use this method only outside of startRenderToTexture() or bind()
  244. * @param clearColorBuffer the color to clear the color buffer with
  245. * @param r the r
  246. * @param g the g
  247. * @param b the b
  248. * @param a the a
  249. * @param clearDepthBuffer clear the depth buffer
  250. */
  251. public void clear(boolean clearColorBuffer, float r, float g, float b, float a, boolean clearDepthBuffer){
  252. //FIXME make it so we can specify 0..255 colors, and not openGL 0..1 !
  253. //GL gl = Tools3D.getGL(app);
  254. this.bind();
  255. if (clearColorBuffer){
  256. gl.glClearColor(r, g, b, a);
  257. gl.glClear(GL.GL_COLOR_BUFFER_BIT);
  258. }
  259. if (clearDepthBuffer){
  260. gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
  261. }
  262. this.unBind();
  263. }
  264. protected void bind(){
  265. gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboID);
  266. //gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, depthRBID);
  267. }
  268. /**
  269. * Sets up this frame buffer object to render all following rendering commands
  270. * into the offscreen textures which are attached to the frame buffer object.
  271. */
  272. public void startRenderToTexture(){
  273. //FIXME FBO STACK TEST
  274. // this.bind();
  275. this.fboStack.pushFBO();
  276. this.fboStack.useFBO(this);
  277. /*
  278. //TODO f?r mehrere texturen,
  279. - check wieviele buffers (color attachments) m?glich
  280. - f?r jede texture ein color attachment binden
  281. => gl.glFramebufferTexture2DEXT(
  282. GL.GL_FRAMEBUFFER_EXT,
  283. GL.GL_COLOR_ATTACHMENT0_EXT, //GL.GL_COLOR_ATTACHMENT1_EXT, ..
  284. tex.getTextureTarget(), tex.getTextureID(), 0);
  285. - glDrawBuffers aufrufen //und glReadBuffers
  286. setDrawBuffer(GL.GL_NONE); //wenn kein mutliple texture draw m?glich
  287. setReadBuffer(GL.GL_NONE);
  288. */
  289. gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
  290. // gl.glDrawBuffer(GL.GL_NONE);
  291. // gl.glViewport(0, 0, width, height);
  292. // gl.glViewport(-50,-50, pa.width+100, pa.height+100);
  293. gl.glViewport(this.viewportX,this.viewportY, this.viewportWidth, this.viewportHeight);
  294. }
  295. /**
  296. * Stops this FBO from rendering in to the attached texture(s).
  297. * <p>NOTE: To use the texture we rendered into, it might me necessary to bind the
  298. * texture and then call gl.glGenerateMipmapEXT(texture.getTextureTarget());
  299. * for mipmap generation. Else we might get a black texture! (not yet confirmed)
  300. *
  301. */
  302. public void stopRenderToTexture(){
  303. gl.glPopAttrib();
  304. //FIXME FBO STACK TEST
  305. // this.unBind();
  306. this.fboStack.popFBO();
  307. }
  308. protected void unBind() {
  309. //gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, 0);
  310. gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
  311. }
  312. private void setReadBuffer(int attachVal) {
  313. gl.glReadBuffer(attachVal);
  314. }
  315. private void setDrawBuffer(int attachVal) {
  316. gl.glDrawBuffer(attachVal);
  317. }
  318. /**
  319. * Destroys and deallocates this FBO.
  320. */
  321. public void destroy() {
  322. if (fboID > 0) {
  323. final IntBuffer id = ToolsBuffers.createIntBuffer(1);
  324. id.put(fboID);
  325. id.rewind();
  326. gl.glDeleteFramebuffersEXT(id.limit(), id);
  327. fboID = 0;
  328. }
  329. if (depthRBID > 0) {
  330. final IntBuffer id = ToolsBuffers.createIntBuffer(1);
  331. id.put(depthRBID);
  332. id.rewind();
  333. gl.glDeleteRenderbuffersEXT(id.limit(), id);
  334. depthRBID = 0;
  335. }
  336. this.textures.clear();
  337. }
  338. @Override
  339. protected void finalize() throws Throwable {
  340. logger.debug("Finalizing - " + this);
  341. if (this.pa instanceof MTApplication) {
  342. MTApplication mtApp = (MTApplication) this.pa;
  343. mtApp.invokeLater(new Runnable() {
  344. public void run() {
  345. destroy();
  346. }
  347. });
  348. }else{
  349. //TODO use registerPre()?
  350. //is the object even valid after finalize() is called??
  351. }
  352. super.finalize();
  353. }
  354. public boolean isStencilBufferAttached() {
  355. return stencilBufferAttached;
  356. }
  357. public int getWidth() {
  358. return width;
  359. }
  360. public int getHeight() {
  361. return height;
  362. }
  363. public int getName(){
  364. return this.fboID;
  365. }
  366. //TODO for several render targets IMPLEMENT!
  367. /*
  368. public void setDrawBuffers(GLTexture[] drawTextures, int n){
  369. numDrawBuffersInUse = PApplet.min(n, drawTextures.length);
  370. colorDrawBuffers = new int[numDrawBuffersInUse];
  371. textureIDs = new int[numDrawBuffersInUse];
  372. textureTargets = new int[numDrawBuffersInUse];
  373. for (int i = 0; i < numDrawBuffersInUse; i++)
  374. {
  375. colorDrawBuffers[i] = GL.GL_COLOR_ATTACHMENT0_EXT + i;
  376. textureTargets[i] = drawTextures[i].getTextureTarget();
  377. textureIDs[i] = drawTextures[i].getTextureID();
  378. gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, colorDrawBuffers[i], textureTargets[i], textureIDs[i], 0);
  379. }
  380. checkFBO();
  381. gl.glDrawBuffers(numDrawBuffersInUse, IntBuffer.wrap(colorDrawBuffers));
  382. }
  383. */
  384. //TODO for stencil enabled fbo! IMPLEMENT!
  385. /*
  386. // Allocating space for multisampled depth buffer
  387. gl.glRenderbufferStorageMultisampleEXT(GL.GL_RENDERBUFFER_EXT, multisampleLevel, GL_DEPTH24_STENCIL8, width, height);
  388. // Creating handle for multisampled FBO
  389. glstate.pushFramebuffer();
  390. glstate.setFramebuffer(multisampleFBO);
  391. gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_RENDERBUFFER_EXT, colorBufferMulti[0]);
  392. gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthStencilBuffer[0]);
  393. gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_STENCIL_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthStencilBuffer[0]);
  394. */
  395. public void checkFBOComplete(GL gl, int fboID) {
  396. final int framebuffer = gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT);
  397. switch (framebuffer) {
  398. case GL.GL_FRAMEBUFFER_COMPLETE_EXT:
  399. logger.debug("FRAMEBUFFER STATUS COMPLETE!");
  400. break;
  401. case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
  402. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception", fboID);
  403. break;
  404. case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
  405. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception", fboID);
  406. break;
  407. case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
  408. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception", fboID);
  409. break;
  410. case GL.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
  411. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception", fboID);
  412. break;
  413. case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
  414. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception", fboID);
  415. break;
  416. case GL.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
  417. doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception", fboID);
  418. break;
  419. case GL.GL_FRAMEBUFFER_UNSUPPORTED_EXT:
  420. doError(", has caused a GL_FRAMEBUFFER_UNSUPPORTED_EXT exception", fboID);
  421. break;
  422. default:
  423. doError(", Unexpected reply from glCheckFramebufferStatusEXT: ", fboID);
  424. break;
  425. }
  426. }
  427. private void doError(String msg, int fboID){
  428. // throw new RuntimeException("FrameBuffer: " + fboID + msg);
  429. logger.error("FrameBuffer: " + fboID + msg);
  430. }
  431. public void setViewportX(int viewportX,int viewportY, int viewportWidth, int viewportHeight) {
  432. this.viewportX = viewportX;
  433. this.viewportY = viewportY;
  434. this.viewportWidth = viewportWidth;
  435. this.viewportHeight = viewportHeight;
  436. }
  437. public int[] getViewport(){
  438. return new int[]{
  439. this.viewportX,
  440. this.viewportY,
  441. this.viewportWidth,
  442. this.viewportHeight};
  443. }
  444. /**
  445. * Checks if the FrameBufferObject is supported on this platform.
  446. *
  447. * @param app the PApplet
  448. *
  449. * @return true, if is supported
  450. */
  451. public static boolean isSupported(PApplet app){
  452. return Tools3D.isGLExtensionSupported(app, "GL_EXT_framebuffer_object");
  453. }
  454. }