/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.render.jsr231; import java.awt.image.BufferedImage; import java.nio.IntBuffer; import javax.media.opengl.GL; import org.jagatoo.opengl.enums.TextureFormat; import org.openmali.types.twodee.Rect2i; import org.openmali.vecmath2.Colorf; import org.xith3d.render.CanvasPeer; import org.xith3d.render.ImageRenderTarget; import org.xith3d.render.OpenGLCapabilities; import org.xith3d.render.OpenGLStatesCache; import org.xith3d.render.RenderTarget; import org.xith3d.render.SceneGraphOpenGLReference; import org.xith3d.render.SceneGraphOpenGLReferences; import org.xith3d.render.TextureRenderTarget; import org.xith3d.scenegraph.Texture; import com.sun.opengl.util.BufferUtil; /** * This class handles the rendering of {@link RenderTarget}s. * * @author Marvin Froehlich (aka Qudus) * @author Mathias Henze (aka cylab) */ public class RenderTargetPeer { private final IntBuffer intBuffer = BufferUtil.newIntBuffer( 1 ); private final RenderPeerImpl renderPeer; private final SceneGraphOpenGLReferences.Provider frameBufferNameProvider = new SceneGraphOpenGLReferences.Provider() { public SceneGraphOpenGLReference newReference( CanvasPeer canvasPeer, SceneGraphOpenGLReferences references, int numNamesPerContext ) { return ( new SceneGraphOpenGLReference( canvasPeer, references, numNamesPerContext ) { @Override public void prepareObjectForDestroy() { SceneGraphOpenGLReference ref = getReferences().removeReference( getContext().getCanvasID() ); ( (CanvasPeerImplBase)getContext() ).addDestroyableObject( ref ); } @Override public void destroyObject( int index, int name ) { GL gl = ( (CanvasPeerImplBase)getContext() ).getGL(); intBuffer.clear(); intBuffer.put( name ).rewind(); if ( index == 0 ) { gl.glDeleteFramebuffersEXT( 1, intBuffer ); } else if ( index == 1 ) { gl.glDeleteRenderbuffersEXT( 1, intBuffer ); } } } ); } }; private final void setupRenderTarget( GL gl, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, CanvasPeer canvasPeer, TextureRenderTarget renderTarget ) { final SceneGraphOpenGLReference openGLRef = renderTarget.getOpenGLReferences().getReference( canvasPeer, frameBufferNameProvider ); final Texture texture = renderTarget.getTexture(); final int texWidth = texture.getWidth(); final int texHeight = texture.getHeight(); if ( ( texWidth <= 0 ) || ( texHeight <= 0 ) ) { throw new Error( "The Texture \"" + texture.getName() + "\" of TextureRenderTarget \"" + renderTarget + "\" doesn't seem to have an InputComponent." ); } if ( !openGLRef.nameExists( 0 ) ) { intBuffer.clear(); gl.glGenFramebuffersEXT( 1, intBuffer ); openGLRef.setName( 0, intBuffer.get( 0 ) ); } final int fb = openGLRef.getName( 0 ); gl.glBindFramebufferEXT( GL.GL_FRAMEBUFFER_EXT, fb ); final int tex = TextureUnitStateUnitPeer.setTextureState2( gl, glCaps, statesCache, texture, statesCache.currentServerTextureUnit, texture.isChanged2(), canvasPeer, renderPeer.getCanvasPeer().getDepthBufferSize() ); if ( texture.getFormat() == TextureFormat.DEPTH ) { /* gl.glRenderbufferStorageEXT( GL.GL_RENDERBUFFER_EXT, TextureUnitStateUnitPeer.translateInternalFormat( texture.getFormat(), texture.getImage( 0 ).getInternalFormatHint(), renderPeer.getPeer().getDepthBufferSize() ), texWidth, texHeight ); */ gl.glFramebufferTexture2DEXT( GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_TEXTURE_2D, tex, 0 ); // When no color attachment is used, the color buffer has to be disabled, to still render the depth texture! gl.glDrawBuffer( GL.GL_NONE ); // cylab: dunno why this is used in some online resources, seems to have no effect... gl.glReadBuffer( GL.GL_NONE ); } else { if ( !openGLRef.nameExists( 1 ) ) { intBuffer.clear(); gl.glGenRenderbuffersEXT( 1, intBuffer ); openGLRef.setName( 1, intBuffer.get( 0 ) ); } final int rb = openGLRef.getName( 1 ); gl.glBindRenderbufferEXT( GL.GL_RENDERBUFFER_EXT, rb ); gl.glRenderbufferStorageEXT( GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT24, texWidth, texHeight ); gl.glFramebufferRenderbufferEXT( GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, rb ); gl.glFramebufferTexture2DEXT( GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_TEXTURE_2D, tex, 0 ); } gl.glPushAttrib( GL.GL_VIEWPORT_BIT ); gl.glViewport( 0, 0, texWidth, texHeight ); final Colorf bgCol = renderTarget.getBackgroundColor(); if ( bgCol != null ) { gl.glClearColor( bgCol.getRed(), bgCol.getGreen(), bgCol.getBlue(), 1f - bgCol.getAlpha() ); gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT ); final float[] clearColor = renderPeer.getClearColor(); gl.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], clearColor[ 3 ] ); } } /** * * @param gl * @param renderTarget */ private final void finishRenderTarget( GL gl, TextureRenderTarget renderTarget ) { gl.glPopAttrib(); // TODO: (cylab 07-11-18) should only be called to return to the normal rendering, not between framebuffers gl.glBindFramebufferEXT( GL.GL_FRAMEBUFFER_EXT, 0 ); } private final void setupRenderTarget( ImageRenderTarget renderTarget ) { final Rect2i viewport = ( (CanvasPeerImplBase)renderPeer.getCanvasPeer() ).getCurrentViewport(); BufferedImage image = renderTarget.getImage(); if ( ( image == null ) || ( image.getWidth() != viewport.getWidth() ) || ( image.getHeight() != viewport.getHeight() ) ) { renderTarget.setImage( new BufferedImage( viewport.getWidth(), viewport.getHeight(), BufferedImage.TYPE_INT_ARGB ) ); } } private final void finishRenderTarget( GL gl, ImageRenderTarget renderTarget ) { final BufferedImage image = renderTarget.getImage(); gl.glReadPixels( 0, 0, image.getWidth(), image.getHeight(), GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, renderTarget.getByteBuffer() ); renderTarget.copyBufferToImage(); } public final void setupRenderTarget( GL gl, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, CanvasPeer canvasPeer, RenderTarget renderTarget ) { if ( renderTarget instanceof TextureRenderTarget ) { setupRenderTarget( gl, glCaps, statesCache, canvasPeer, (TextureRenderTarget)renderTarget ); } else if ( renderTarget instanceof ImageRenderTarget ) { setupRenderTarget( (ImageRenderTarget)renderTarget ); } } public final void finishRenderTarget( GL gl, RenderTarget renderTarget ) { if ( renderTarget instanceof TextureRenderTarget ) { finishRenderTarget( gl, (TextureRenderTarget)renderTarget ); } else if ( renderTarget instanceof ImageRenderTarget ) { finishRenderTarget( gl, (ImageRenderTarget)renderTarget ); } } public RenderTargetPeer( RenderPeerImpl renderPeer ) { this.renderPeer = renderPeer; } }