PageRenderTime 54ms CodeModel.GetById 11ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

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