Package org.mt4j.util.opengl

Source Code of org.mt4j.util.opengl.GLFBO

/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.util.opengl;

import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.mt4j.MTApplication;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.ToolsBuffers;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER;
import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER;
import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET;
import org.mt4j.util.opengl.GLTexture.WRAP_MODE;

import processing.core.PApplet;
import processing.opengl.PGraphicsOpenGL;

/**
* This class abstracts a opengl frame buffer object for easier usage.
* This can mainly be used to draw to an offscreen buffer and use that buffer
* as a texture later.
*
* @author Christopher Ruff
*/
public class GLFBO {
  /** The Constant logger. */
  private static final Logger logger = Logger.getLogger(GLFBO.class.getName());
  static{
//    logger.setLevel(Level.ERROR);
//    logger.setLevel(Level.WARN);
//    logger.setLevel(Level.DEBUG);
    logger.setLevel(Level.INFO);
    SimpleLayout l = new SimpleLayout();
    ConsoleAppender ca = new ConsoleAppender(l);
    logger.addAppender(ca);
  }
 
  private GL gl;
 
//  private int[] fboID;
  private int fboID;

  private int depthRBID;

  private int width;
  private int height;
 
  private PApplet pa;
 
  private List<GLTexture> textures;
 
  private int viewportX;
  private int viewportY;
  private int viewportWidth;
  private int viewportHeight;
 
  private boolean stencilBufferAttached;

  private GLFboStack fboStack;
 
 
  /**
   * Instantiates a new GL FBO.
   *
   * @param pa the pa
   * @param width the width
   * @param height the height
   */
  public GLFBO(PApplet pa, int width, int height) {
    this(pa, width, height, true);
  }
 
 
  /**
   * Instantiates a new GL FBO.
   *
   * @param pa the pa
   * @param width the width
   * @param height the height
   * @param attachStencilBuffer the attach stencil buffer
   */
  public GLFBO(PApplet pa, int width, int height, boolean attachStencilBuffer) {
    super();
    this.pa = pa;
    this.gl = ((PGraphicsOpenGL)pa.g).gl;
   
    this.stencilBufferAttached = attachStencilBuffer;
   
    this.fboID = 0;
    this.depthRBID = 0;
   
    this.viewportX     = 0;
    this.viewportY     = 0;
    this.viewportWidth   = width;
    this.viewportHeight = height;
   
    this.width = width;
    this.height = height;

    this.textures = new ArrayList<GLTexture>();
   
    //FIXME FBO STACK TEST!!
    this.fboStack = GLFboStack.getInstance();
   
    this.initFBO();
  }
 
 
  private void initFBO(){
    IntBuffer buffer = ToolsBuffers.createIntBuffer(1);
    gl.glGenFramebuffersEXT(1, buffer);
    this.fboID = buffer.get(0);
    gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboID);
   
    //Create depth buffer
    IntBuffer buffer2 = ToolsBuffers.createIntBuffer(1);
    gl.glGenRenderbuffersEXT(1, buffer2);
    this.depthRBID = buffer2.get(0);
    gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, depthRBID);
   
    if (this.isStencilBufferAttached()){
      //THIS CREATES A FBO WITH A STENCIL BUFFER! HAS TO BE SUPPORTED ON THE PLATFORM!
      gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH24_STENCIL8_EXT, this.width, this.height);
    }else{
      //Creates a fbo with a depth but without a stencil buffer
      gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT, this.width, this.height); //orginal 
    }
   
    //Attach depth buffer to FBO
    gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthRBID);
   
    if (this.isStencilBufferAttached()){
      //Attach stencil buffer to FBO - HAS TO BE SUPPORTED ON THE PLATFORM!
      gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_STENCIL_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthRBID);     
    }
   
    gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
  }
 
 

  /**
   * Attaches a gl texture object to the frame buffer object.
   * The texture will contain everything drawn when the fbo is bound.
   *
   * @return the gL texture
   */
  public GLTexture addNewTexture(){
    return this.addNewTexture(false);
  }
 
  /**
   * This creates a GLTexture object with the FBO dimensions and
   * attaches it to this frame buffer object.
   * The texture can later be used to texture a component.
   *
   * @return the gL texture
   */
  public GLTexture addNewTexture(boolean useMipMap){
    this.bind();

    boolean isPowerOfTwoDimension = ToolsMath.isPowerOfTwo(this.width) && ToolsMath.isPowerOfTwo(this.height);
   
    GLTextureSettings texSettings = new GLTextureSettings();
    texSettings.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE;
    texSettings.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE;
    texSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
    texSettings.expansionFilter = EXPANSION_FILTER.Bilinear;
   
//    GLTextureParameters tp = new GLTextureParameters();
//    //Set texture FILTER MODE
////    tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_LINEAR;
////    tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_NEAREST;
//    tp.minFilter = GLTextureParameters.LINEAR;
////    tp.minFilter = GLTextureParameters.NEAREST;
////    if (useMipMap)
////      tp.minFilter = GLTextureParameters.LINEAR_MIPMAP_NEAREST; //Seems to not display the fbo texture when POT
////    else{
////      tp.minFilter = GLTextureParameters.LINEAR;     
////    }
//    tp.magFilter = GLTextureParameters.LINEAR; 
//   
//    //Set texture WRAP MODE
//    tp.wrap_s = GL.GL_CLAMP_TO_EDGE;
//    tp.wrap_t = GL.GL_CLAMP_TO_EDGE;
////    tp.wrap_s = GL.GL_CLAMP;
////    tp.wrap_t = GL.GL_CLAMP;
   
    //Set texture TARGET
    if (isPowerOfTwoDimension){
//      tp.target = GLTextureParameters.NORMAL;
      texSettings.target = TEXTURE_TARGET.TEXTURE_2D;
      logger.debug("Power of 2 FBO texture created");
    }else{
//      tp.target = GLTextureParameters.RECTANGULAR; 
      texSettings.target = TEXTURE_TARGET.RECTANGULAR;
      logger.debug("Rectangular FBO texture created");
    }
   
//    GLTexture tex = new GLTexture(pa, this.width, this.height, tp, true, 0);
    GLTexture tex = new GLTexture(this.pa, texSettings);
    tex.setupGLTexture(this.width, this.height);
    tex.width = this.width;
    tex.height = this.height; //To prevent init() call in loadGLTexture().. not even neccessary with fbo usage?
   
    gl.glBindTexture(tex.getTextureTarget(), tex.getTextureID());
   
    //Use extension to automatically generate mipmaps for the fbo textures
    //TODO Is this always needed? supported? only working in power of two dimensions!?
//    if (useMipMap && isPowerOfTwoDimension) //only for target GL_TEXTURE_2D allowed
//      gl.glGenerateMipmapEXT(tex.getTextureTarget()); //FIXME seems to crash JVM after app close! //FIXME do this only after drawing finished to fbo texture

    //Attach texture to FBO
    gl.glFramebufferTexture2DEXT(
        GL.GL_FRAMEBUFFER_EXT,
        GL.GL_COLOR_ATTACHMENT0_EXT,
        tex.getTextureTarget(), tex.getTextureID(), 0);

    gl.glBindTexture(tex.getTextureTarget(), 0);

    this.checkFBOComplete(gl, fboID);

    this.unBind();
   
    //Add to list
    this.textures.add(tex);
    return tex;
  }
 
 
  //TEXTURE HAS TO HAVE FBO DIMENSIONS TO WORK! THIS REPLACED THE OLD ATTACHED TEXTURE!
  public boolean add(GLTexture tex) {
    this.bind();
    gl.glBindTexture(tex.getTextureTarget(), tex.getTextureID());
    /*
    //F�r OHNE mipmapping
    gl.glTexParameteri(tex.getTextureTarget(),GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
    gl.glTexParameteri(tex.getTextureTarget(),GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
    */
//    /*
    //F�r MIIPMAPPING
//    gl.glTexParameteri(tex.getTextureTarget(), GL.GL_GENERATE_MIPMAP, GL.GL_TRUE); // automatic mipmap
    gl.glTexParameterf(tex.getTextureTarget(), GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
    gl.glTexParameterf(tex.getTextureTarget(), GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(tex.getTextureTarget(), GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
    gl.glTexParameteri(tex.getTextureTarget(), GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
    gl.glGenerateMipmapEXT(tex.getTextureTarget());
//     */
    //Attach texture to FBO
    gl.glFramebufferTexture2DEXT(
        GL.GL_FRAMEBUFFER_EXT,
        GL.GL_COLOR_ATTACHMENT0_EXT,
        tex.getTextureTarget(), tex.getTextureID(), 0);
   
    gl.glBindTexture(tex.getTextureTarget(), 0);
   
    this.checkFBOComplete(gl, fboID);
    this.unBind();
    return textures.add(tex);
  }
 
 
  public boolean contains(GLTexture arg0) {
    return textures.contains(arg0);
  }

  public boolean remove(GLTexture arg0) {
    return textures.remove(arg0);
  }

  /*
   In this instance we are creating a normal RGBA image of the same width and height as the renderbuffer we created earlier;
   this is important as ALL attachments to a FBO have to be the same width and height.
   Note that we don�t upload any data, the space is just reserved by OpenGL so we can use it later.
   */
 
  /**
   * If clearColorBuffer is set to true this clears the colorbuffer with the specified color values.
   * <br>If clearDepthBuffer is set to true this clears the depth buffer of the framebuffer object.
   * <br>NOTE: It seems that a texture has to be attached to the FBO first, for this to work as expected.
   * <br>NOTE: This method will bind and unbind the FBO! So use this method only outside of startRenderToTexture() or bind()
   * @param clearColorBuffer the color to clear the color buffer with
   * @param r the r
   * @param g the g
   * @param b the b
   * @param a the a
   * @param clearDepthBuffer clear the depth buffer
   */
  public void clear(boolean clearColorBuffer, float r, float g, float b, float a, boolean clearDepthBuffer){
    //FIXME make it so we can specify 0..255 colors, and not openGL 0..1 !
   
    //GL gl = Tools3D.getGL(app);
    this.bind();
    if (clearColorBuffer){
      gl.glClearColor(r, g, b, a);
      gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    }
    if (clearDepthBuffer){
      gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
    }
    this.unBind();
  }
 
 
  protected void bind(){
    gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboID);
    //gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, depthRBID);
  }
 
  /**
   * Sets up this frame buffer object to render all following rendering commands
   * into the offscreen textures which are attached to the frame buffer object.
   */
  public void startRenderToTexture(){
    //FIXME FBO STACK TEST
//    this.bind();
    this.fboStack.pushFBO();
    this.fboStack.useFBO(this);
   
    /*
    //TODO f�r mehrere texturen,
     - check wieviele buffers (color attachments) m�glich
     - f�r jede texture ein color attachment binden
      => gl.glFramebufferTexture2DEXT(
        GL.GL_FRAMEBUFFER_EXT,
        GL.GL_COLOR_ATTACHMENT0_EXT, //GL.GL_COLOR_ATTACHMENT1_EXT, ..
        tex.getTextureTarget(), tex.getTextureID(), 0);
     - glDrawBuffers aufrufen //und glReadBuffers
        setDrawBuffer(GL.GL_NONE); //wenn kein mutliple texture draw m�glich
        setReadBuffer(GL.GL_NONE);
    */
   
    gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
//    gl.glDrawBuffer(GL.GL_NONE);
//    gl.glViewport(0, 0, width, height);
//    gl.glViewport(-50,-50, pa.width+100, pa.height+100);
   
    gl.glViewport(this.viewportX,this.viewportY, this.viewportWidth, this.viewportHeight);
  }
 

 
  /**
   * Stops this FBO from rendering in to the attached texture(s).
   * <p>NOTE: To use the texture we rendered into, it might me necessary to bind the
   * texture and then call gl.glGenerateMipmapEXT(texture.getTextureTarget());
   * for mipmap generation. Else we might get a black texture! (not yet confirmed)
   *
   */
  public void stopRenderToTexture(){
    gl.glPopAttrib();

    //FIXME FBO STACK TEST
//    this.unBind();
    this.fboStack.popFBO();
  }
 
 
  protected void unBind() {
    //gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, 0);
    gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
  }
 
 

  private void setReadBuffer(int attachVal) {
    gl.glReadBuffer(attachVal);
  }

  private void setDrawBuffer(int attachVal) {
    gl.glDrawBuffer(attachVal);
  }

 
 
  /**
   * Destroys and deallocates this FBO.
   */
  public void destroy() {
    if (fboID > 0) {
      final IntBuffer id = ToolsBuffers.createIntBuffer(1);
      id.put(fboID);
      id.rewind();
      gl.glDeleteFramebuffersEXT(id.limit(), id);
      fboID = 0;
    }

    if (depthRBID > 0) {
      final IntBuffer id = ToolsBuffers.createIntBuffer(1);
      id.put(depthRBID);
      id.rewind();
      gl.glDeleteRenderbuffersEXT(id.limit(), id);
      depthRBID = 0;
    }
   
    this.textures.clear();
  }
 
  @Override
  protected void finalize() throws Throwable {
    logger.debug("Finalizing - " + this);
    if (this.pa instanceof MTApplication) {
      MTApplication mtApp = (MTApplication) this.pa;
      mtApp.invokeLater(new Runnable() {
        public void run() {
          destroy();
        }
      });
    }else{
      //TODO use registerPre()?
      //is the object even valid after finalize() is called??
    }
    super.finalize();
  }

  public boolean isStencilBufferAttached() {
    return stencilBufferAttached;
  }

  public int getWidth() {
    return width;
  }


  public int getHeight() {
    return height;
  }
 
  public int getName(){
    return this.fboID;
  }

  //TODO for several render targets IMPLEMENT!
  /*
  public void setDrawBuffers(GLTexture[] drawTextures, int n){
    numDrawBuffersInUse = PApplet.min(n, drawTextures.length);

    colorDrawBuffers = new int[numDrawBuffersInUse];
    textureIDs = new int[numDrawBuffersInUse];
    textureTargets = new int[numDrawBuffersInUse];

    for (int i = 0; i < numDrawBuffersInUse; i++)
    {
      colorDrawBuffers[i] = GL.GL_COLOR_ATTACHMENT0_EXT + i;
      textureTargets[i] = drawTextures[i].getTextureTarget();
      textureIDs[i] = drawTextures[i].getTextureID();

      gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, colorDrawBuffers[i], textureTargets[i], textureIDs[i], 0);
    }

    checkFBO();

    gl.glDrawBuffers(numDrawBuffersInUse, IntBuffer.wrap(colorDrawBuffers));
  }
   */
 
  //TODO for  stencil enabled fbo! IMPLEMENT!
  /*
   // Allocating space for multisampled depth buffer
   gl.glRenderbufferStorageMultisampleEXT(GL.GL_RENDERBUFFER_EXT, multisampleLevel, GL_DEPTH24_STENCIL8, width, height);

   // Creating handle for multisampled FBO
   glstate.pushFramebuffer();
   glstate.setFramebuffer(multisampleFBO);

   gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_RENDERBUFFER_EXT, colorBufferMulti[0]);
   gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthStencilBuffer[0]);
   gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_STENCIL_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, depthStencilBuffer[0]);
   */

  public void checkFBOComplete(GL gl, int fboID) {
    final int framebuffer = gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT);
    switch (framebuffer) {
    case GL.GL_FRAMEBUFFER_COMPLETE_EXT:
      logger.debug("FRAMEBUFFER STATUS COMPLETE!");
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
      doError(", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception", fboID);
      break;
    case GL.GL_FRAMEBUFFER_UNSUPPORTED_EXT:
      doError(", has caused a GL_FRAMEBUFFER_UNSUPPORTED_EXT exception", fboID);
      break;
    default:
      doError(", Unexpected reply from glCheckFramebufferStatusEXT: ", fboID);
      break;
    }
  }

 
  private void doError(String msg, int fboID){
//    throw new RuntimeException("FrameBuffer: " + fboID  + msg);
    logger.error("FrameBuffer: " + fboID + msg);
  }
 


  public void setViewportX(int viewportX,int viewportY, int viewportWidth, int viewportHeight) {
    this.viewportX = viewportX;
    this.viewportY = viewportY;
    this.viewportWidth = viewportWidth;
    this.viewportHeight = viewportHeight;
  }
 
  public int[] getViewport(){
    return new int[]{
        this.viewportX,
        this.viewportY,
        this.viewportWidth,
        this.viewportHeight};
  }


 
  /**
   * Checks if the FrameBufferObject is supported on this platform.
   *
   * @param app the PApplet
   *
   * @return true, if is supported
   */
  public static boolean isSupported(PApplet app){
    return Tools3D.isGLExtensionSupported(app, "GL_EXT_framebuffer_object");
  }
 

 
 
}
TOP

Related Classes of org.mt4j.util.opengl.GLFBO

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.