/** * 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.io.File; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.util.List; import javax.media.opengl.GL; import org.jagatoo.logging.ProfileTimer; import org.openmali.types.twodee.Rect2i; import org.xith3d.picking.PickRequest; import org.xith3d.render.BackgroundRenderPass; import org.xith3d.render.CanvasPeer; import org.xith3d.render.Clipper; import org.xith3d.render.ClipperInfo; import org.xith3d.render.OpenGLCapabilities; import org.xith3d.render.OpenGLStatesCache; import org.xith3d.render.OpenGlExtensions; import org.xith3d.render.RenderOptions; import org.xith3d.render.RenderPass; import org.xith3d.render.RenderPassConfig; import org.xith3d.render.RenderPeer; import org.xith3d.render.ScissorRect; import org.xith3d.render.StateUnitPeerRegistry; import org.xith3d.render.preprocessing.RenderAtom; import org.xith3d.render.preprocessing.RenderBin; import org.xith3d.render.preprocessing.RenderBinProvider; import org.xith3d.scenegraph.Transform3D; import org.xith3d.scenegraph.View; import org.xith3d.scenegraph._SG_PrivilegedAccess; import org.xith3d.utility.logging.X3DLog; import org.xith3d.utility.screenshots.ScreenshotCreator; import com.sun.opengl.util.BufferUtil; /** * RenderPeer implementation base for JOGL (JSR-231). * * @author David Yazel * @author Lilian Chamontin [jsr231 port] * @author Marvin Froehlich (aka Qudus) */ class RenderPeerImpl extends RenderPeer { private GL gl; private final FloatBuffer openGLProjMatBuffer = BufferUtil.newFloatBuffer( 16 ); private final DoubleBuffer planeBuffer = BufferUtil.newDoubleBuffer( 4 ); private final FloatBuffer defaultLightColorBuffer = BufferUtil.newFloatBuffer( 4 ); private final FloatBuffer pickMBuffer = BufferUtil.newFloatBuffer( 16 ); private float[] openGLProjMatrix = new float[ 16 ]; private ScissorRect lastScissorBox = null; private int lastClipperId = -1; private int activeClipperId = -1; private RenderOptions effectiveRenderOptions = new RenderOptions(); private float[] defaultLightColorArray = { 0.2f, 0.2f, 0.2f, 1.0f }; private final RenderTargetPeer renderTargetPeer = new RenderTargetPeer( this ); private final ShadowRenderPeer shadowPeer = new ShadowRenderPeer( this ); private ScreenshotCreator scheduledShotCreator = null; private ScreenshotCreator shotCreator = null; public RenderPeerImpl( CanvasPeerImplBase canvasPeer, StateUnitPeerRegistry shaderRegistry, OpenGLStatesCache statesCache, RenderOptions renderOptions ) { super( canvasPeer, shaderRegistry, statesCache, renderOptions ); } public RenderPeerImpl( CanvasPeerImplBase canvasPeer, StateUnitPeerRegistry shaderRegistry, OpenGLStatesCache statesCache ) { this( canvasPeer, shaderRegistry, statesCache, new RenderOptions() ); } protected static void setGCRequested() { setGCRequested( true ); } /** * {@inheritDoc} */ @Override protected void setCanvasPeer( CanvasPeer canvasPeer ) { super.setCanvasPeer( canvasPeer ); } public CanvasPeerImplBase getCanvasPeerBase() { return ( (CanvasPeerImplBase)getCanvasPeer() ); } public GL getGL() { return ( gl ); } public final void setDefaultLightColor( float r, float g, float b, float a ) { defaultLightColorArray[ 0 ] = r; defaultLightColorArray[ 1 ] = g; defaultLightColorArray[ 2 ] = b; defaultLightColorArray[ 3 ] = a; } public final float[] getClearColor() { return ( clearColor ); } @SuppressWarnings( "unused" ) private final void drawFloor( GL gl, OpenGLStatesCache statesCache ) { gl.glMatrixMode( GL.GL_MODELVIEW ); gl.glPushMatrix(); gl.glLoadIdentity(); gl.glDisable( GL.GL_CULL_FACE ); statesCache.cullFaceEnabled = false; gl.glDisable( GL.GL_LIGHTING ); // Turn Off Lighting statesCache.lightingEnabled = false; gl.glDisable( GL.GL_TEXTURE_2D ); statesCache.texture2DEnabled[ statesCache.currentServerTextureUnit ] = false; gl.glDisable( GL.GL_TEXTURE_3D ); statesCache.texture3DEnabled[ statesCache.currentServerTextureUnit ] = false; gl.glDisable( GL.GL_BLEND ); statesCache.blendingEnabled = false; gl.glDepthMask( false ); //gl.glDepthFunc( GL.GL_LEQUAL ); //gl.glDisable( GL.GL_DEPTH ); // (test)draw a floor gl.glColor3f( 0.2f, 0.2f, 0.7f ); gl.glBegin( GL.GL_QUADS ); // Begin Drawing Quads // Floor gl.glNormal3f( 0.0f, 1.0f, 0.0f ); // Normal Pointing Up gl.glVertex3f( -200.0f, 0f, -200.0f ); // Back Left gl.glVertex3f( -200.0f, 0f, +200.0f ); // Front Left gl.glVertex3f( +200.0f, 0f, +200.0f ); // Front Right gl.glVertex3f( +200.0f, 0f, -200.0f ); gl.glEnd(); gl.glPopMatrix(); gl.glDepthMask( statesCache.depthWriteMask ); //gl.glEnable( GL.GL_DEPTH ); } private final void finishScissors( GL gl, OpenGLStatesCache statesCache ) { //if (lastScissorBox != null) { if ( !statesCache.enabled || statesCache.scissorTestEnabled ) { gl.glDisable( GL.GL_SCISSOR_TEST ); statesCache.scissorTestEnabled = false; } } lastScissorBox = null; } private final void startScissors( GL gl, OpenGLStatesCache statesCache, ScissorRect scissorBox ) { if ( ( scissorBox == null ) && ( ( lastScissorBox != null ) || ( gl.glIsEnabled( GL.GL_SCISSOR_TEST ) ) ) ) { finishScissors( gl, statesCache ); return; } if ( scissorBox != null ) { final boolean ok; if ( !scissorBox.equals( lastScissorBox ) ) { scissorBox.clamp( getCanvasPeerBase().getCurrentViewport() ); ok = scissorBox.check( getCanvasPeerBase().getCurrentViewport() ); if ( ok ) gl.glScissor( scissorBox.getX(), scissorBox.getY(), scissorBox.getWidth(), scissorBox.getHeight() ); } else ok = true; if ( ok && ( ( lastScissorBox == null ) || ( !statesCache.enabled || !statesCache.scissorTestEnabled ) ) ) { gl.glEnable( GL.GL_SCISSOR_TEST ); statesCache.scissorTestEnabled = true; } lastScissorBox = scissorBox; } } public static final int translateClipPlaneIndex( int index ) { switch ( index ) { case 0: return ( GL.GL_CLIP_PLANE0 ); case 1: return ( GL.GL_CLIP_PLANE1 ); case 2: return ( GL.GL_CLIP_PLANE2 ); case 3: return ( GL.GL_CLIP_PLANE3 ); case 4: return ( GL.GL_CLIP_PLANE4 ); case 5: return ( GL.GL_CLIP_PLANE5 ); default: throw new Error( "unknown clip plane " + index ); } } private final void finishClipper( GL gl, OpenGLStatesCache statesCache ) { for ( int i = 0; i < 6; i++ ) { if ( statesCache.clipPlaneEnabled[ i ] ) { gl.glDisable( translateClipPlaneIndex( i ) ); statesCache.clipPlaneEnabled[ i ] = false; } } activeClipperId = -1; } private final void startClipper( GL gl, OpenGLStatesCache statesCache, ClipperInfo info, View view ) { final Clipper clipper = ( info == null ) ? null : info.getClipper(); if ( ( clipper != null ) && ( clipper.isEnabled() ) ) { // skip an already activated clipper if (clipper.getId() == activeClipperId ) return; activeClipperId = clipper.getId(); // only load the clipper transform, if the clipper will be newly set up if ( lastClipperId != clipper.getId() ) { gl.glPushMatrix(); gl.glLoadMatrixf( _SG_PrivilegedAccess.getFloatBuffer( view.getModelViewTransform( false ), true ) ); if ( !clipper.isWorldCoordinateSystemUsed() ) { final Transform3D modelView = info.getModelView(); if ( modelView != null ) { float[] matrix = new float[ 16 ]; modelView.getColumnMajor( matrix ); gl.glMultMatrixf( matrix, 0 ); } } } for ( int i = 0; i < 6; i++ ) { final int glI = translateClipPlaneIndex( i ); if ( clipper.isPlaneEnabled( i ) ) { // only setup the clip planes, if they were not already set up in a previous startClipper()-call if ( lastClipperId != clipper.getId() ) { planeBuffer.clear(); planeBuffer.put( clipper.getPlane( i ).getA() ); planeBuffer.put( clipper.getPlane( i ).getB() ); planeBuffer.put( clipper.getPlane( i ).getC() ); planeBuffer.put( clipper.getPlane( i ).getD() ); planeBuffer.rewind(); gl.glClipPlane( glI, planeBuffer ); } if ( !statesCache.enabled || !statesCache.clipPlaneEnabled[ i ] ) { gl.glEnable( glI ); statesCache.clipPlaneEnabled[ i ] = true; } } else { if ( !statesCache.enabled || statesCache.clipPlaneEnabled[ i ] ) { gl.glDisable( glI ); statesCache.clipPlaneEnabled[ i ] = false; } } } // restore the current matrix and store the clipper id for optimizing the next call if ( lastClipperId != clipper.getId() ) { gl.glPopMatrix(); lastClipperId = clipper.getId(); } } else { finishClipper( gl, statesCache ); } } private final int drawBin( GL gl, OpenGLStatesCache statesCache, OpenGLCapabilities glCaps, RenderOptions options, boolean isScissorEnabled, boolean isClipperEnabled, RenderBin bin, View view, long frameId, int nameOffset, long nanoTime, long nanoStep, RenderMode renderMode ) { final CanvasPeer canvasPeer = getCanvasPeer(); //bin.resetIterationPointer(); int triangles = 0; final int n = bin.size(); for ( int i = 0; i < n; i++ ) { RenderAtom< ? > atom = bin.getAtom( i ); if (atom == null) { // THIS IS AN ERROR - fix it continue; } try { if ( renderMode == RenderMode.PICKING ) //gl.glPushName( nameOffset + atom.getLocalIndex() ); gl.glPushName( nameOffset + i ); if ( isScissorEnabled ) startScissors( gl, statesCache, atom.getScissorRect() ); if ( isClipperEnabled ) startClipper( gl, statesCache, atom.getClipper(), view ); /* if ( atom.getClassification() == Classifier.INSIDE ) { if ( !b ) gl.glHint( GL.GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL.GL_FASTEST ); b = true; } else { if ( b ) gl.glHint( GL.GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL.GL_DONT_CARE ); b = false; } */ triangles += this.renderAtom( atom, gl, canvasPeer, glCaps, statesCache, view, options, nanoTime, nanoStep, renderMode, frameId ); } catch ( Throwable e ) { e.printStackTrace(); System.exit( 0 ); } finally { if ( renderMode == RenderMode.PICKING ) { gl.glPopName(); } } } //if ( isScissorEnabled ) //finishScissors( gl ); if ( isClipperEnabled ) finishClipper( gl, statesCache ); return ( triangles ); } private final void setColorMask( GL gl, int colorMask, OpenGLStatesCache statesCache ) { if ( statesCache.enabled && colorMask == statesCache.colorWriteMask ) return; gl.glColorMask( ( colorMask & 1 ) != 0, ( colorMask & 2 ) != 0, ( colorMask & 4 ) != 0, ( colorMask & 8 ) != 0 ); statesCache.colorWriteMask = colorMask; } /** * setup the frame */ private final void renderStart( GL gl, OpenGLStatesCache statesCache, int atomsCount, PickRequest pickRequest ) { super.renderStart( pickRequest ); getCanvasPeerBase().beforeRenderStart( pickRequest, forceNoSwap ); if ( pickRequest == null ) { selectBuffer = null; gl.glRenderMode( GL.GL_RENDER ); gl.glClearDepth( 1.0f ); if ( ( shotCreator != null ) && ( shotCreator.getFormat() == ScreenshotCreator.Format.RGBA ) ) gl.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], 1f ); else gl.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], clearColor[ 3 ] ); lastScissorBox = null; if ( !statesCache.enabled || statesCache.scissorTestEnabled ) { gl.glDisable( GL.GL_SCISSOR_TEST ); statesCache.scissorTestEnabled = false; } if ( !disableClearBuffer ) { if ( fullOverpaint ) gl.glClear( GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT ); else gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT ); } } else { gl.glRenderMode( GL.GL_RENDER ); gl.glClearColor( 0.2f, 0.2f, 0.2f, 0.0f ); gl.glClearDepth( 1.0f ); gl.glClear( GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT ); /* // Start of Linux workarounds section // Enable CULL_FACE because of the same reason - Linux compatibility... gl.glEnable( GL.GL_CULL_FACE ); // Switch to GL_SMOOTH shade model because of on some cards (VIA CLE266) // SELECT mode fails if shade model set to flat gl.glShadeModel( GL.GL_SMOOTH ); // Workaround for Linux picking problems // Draw at least one triangle in RENDER mode before switching to SELECT mode gl.glMatrixMode( GL.GL_MODELVIEW ); gl.glLoadIdentity(); gl.glBegin( GL.GL_TRIANGLES ); gl.glVertex3f( 0.0f, 0.0f, 0.0f ); gl.glVertex3f( 0.0f, 0.0f, 0.0f ); gl.glVertex3f( 0.0f, 0.0f, 0.0f ); gl.glEnd(); // End of Linux workarounds section */ // Create select buffer that can hold all atoms (if all will hit) // Here we assume that the used name stack depth should be maximum of 1 final int selectBufferCapacity = atomsCount * 4; selectBuffer = BufferUtil.newIntBuffer( selectBufferCapacity ); gl.glSelectBuffer( selectBufferCapacity, selectBuffer ); gl.glRenderMode( GL.GL_SELECT ); gl.glInitNames(); } //setColorMask( gl, colorMask, statesCache ); // Depth Buffer Setup /* gl.glEnable( GL.GL_DEPTH_TEST ); statesCache.depthTestEnabled = true; */ // The type of depth test to do gl.glDepthFunc( GL.GL_LESS ); /* Really Nice Perspective Calculations */ gl.glHint( GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST ); gl.glHint( GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST ); gl.glHint( GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST ); gl.glShadeModel( GL.GL_SMOOTH ); defaultLightColorBuffer.put( defaultLightColorArray ); defaultLightColorBuffer.rewind(); gl.glLightModelfv( GL.GL_LIGHT_MODEL_AMBIENT, defaultLightColorBuffer ); if ( OpenGlExtensions.GL_EXT_separate_specular_color ) gl.glLightModeli( GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL.GL_SEPARATE_SPECULAR_COLOR ); gl.glPixelStorei( GL.GL_UNPACK_ALIGNMENT, 1 ); } /** * Our own functionally equivalent implementation of Mesa-style gluPickMatrix. * We need this because of at least on some installations of Linux glu.gluPickMatrix(...) fails * with unknown error (Unexpected signal 11) and JVM exits. */ private final void gluPickMatrix( GL gl, float x, float y, float width, float height, int[] viewport ) { final float[] m = new float[ 16 ]; final float sx, sy; final float tx, ty; sx = viewport[ 2 ] / width; sy = viewport[ 3 ] / height; tx = ( viewport[ 2 ] + 2.0f * ( viewport[ 0 ] - x ) ) / width; ty = ( viewport[ 3 ] + 2.0f * ( viewport[ 1 ] - y ) ) / height; m[ 0 + 0 * 4 ] = sx; m[ 0 + 1 * 4 ] = 0.0f; m[ 0 + 2 * 4 ] = 0.0f; m[ 0 + 3 * 4 ] = tx; m[ 1 + 0 * 4 ] = 0.0f; m[ 1 + 1 * 4 ] = sy; m[ 1 + 2 * 4 ] = 0.0f; m[ 1 + 3 * 4 ] = ty; m[ 2 + 0 * 4 ] = 0.0f; m[ 2 + 1 * 4 ] = 0.0f; m[ 2 + 2 * 4 ] = 1.0f; m[ 2 + 3 * 4 ] = 0.0f; m[ 3 + 0 * 4 ] = 0.0f; m[ 3 + 1 * 4 ] = 0.0f; m[ 3 + 2 * 4 ] = 0.0f; m[ 3 + 3 * 4 ] = 1.0f; pickMBuffer.put( m ); pickMBuffer.rewind(); gl.glMultMatrixf( pickMBuffer ); } /** * Initializes the OpenGL view */ private final void renderStartView( GL gl, View view, PickRequest pickRequest ) { gl.glMatrixMode( GL.GL_PROJECTION ); gl.glLoadIdentity(); if ( pickRequest != null ) { final Rect2i currentViewport = getCanvasPeerBase().getCurrentViewport(); // YVG: Set pick matrix. In glSelect pick render mode, projectsion matrix is a combination (multiply) // of a pick matrix defining picking Region Of Interest, and regular projection matrix, whatever // projection you are using. Pick matrix is de-facto screen-coordinate zoom-in to the pick rectangle, // so if you try to render scene normal way with pick matrix set you will see your pick region filling // whole screen. // Assume that viewport occupies the rect(0, 0, width, height) // We could read the viewport using gl.glGetIntegerv(GL.GL_VIEWPORT, viewport), but under Linux in some cases it is [0, 0, 0, 0] final int y = getCanvasPeer().getHeight() - currentViewport.getTop() - currentViewport.getHeight(); final int[] viewport = new int[] { currentViewport.getLeft(), y, currentViewport.getWidth(), currentViewport.getHeight() }; // Flip vertically the viewport to reflect the Y axis direction // Call our own version of gluPickMatrix because of at least on some versions of Linux it fails gluPickMatrix( gl, pickRequest.getMouseX(), viewport[ 3 ] - pickRequest.getMouseY(), 1, 1, viewport ); //getGLU().gluPerspective( view.getFieldOfView(), getWidth() / getHeight(), view.getFrontClipDistance(), view.getBackClipDistance() ); } // Set up Projection and ModelView transforms view.getProjection().getColumnMajor( openGLProjMatrix ); openGLProjMatBuffer.put( openGLProjMatrix ); openGLProjMatBuffer.rewind(); gl.glMultMatrixf( openGLProjMatBuffer ); /* * YVG: In previous versions of Xith3D View transform (defined by view.getTransform()) was * combining with Projection transform. This caused some texture coordinate generation * modes to work wrong way. Now it has been modified to fit common concept of making it * a part of MODELVIEW transform. */ //view.getTransposedTransform().get( viewTrans ); //view.getTransposedTransform().get( trans ); //gl.glMultMatrixf( trans ); /* Select The Modelview Matrix */ gl.glMatrixMode( GL.GL_MODELVIEW ); //gl.glScalef( 1f, 1f, 1f ); //gl.glLoadIdentity(); //gl.glPushMatrix(); } /** * Sets the current GL matrix to the view adjusting it depending * on the mode. * @param view * @param mode VIEW_NORMAL will use the standard view, VIEW_FIXED_POSITION * will use only the rotational component of the standard view * (the position is left as the identity), VIEW_FIXED sets the view * to the identity matrix */ private final void setGLModelViewMatrix( GL gl, View view, View.CameraMode mode ) { gl.glLoadMatrixf( _SG_PrivilegedAccess.getFloatBuffer( view.getModelViewTransform( mode, true ), true ) ); } /** * Render the frame using the definition provided in the atomsCollector object. * * @param gl * @throws Throwable */ private final int renderMain( GL gl, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, RenderOptions options, boolean isScissorEnabled, boolean isClipperEnabled, View view, RenderPass renderPass, long frameId, int nameOffset, long nanoTime, long nanoStep, RenderMode renderMode ) throws Throwable { // render the main scene X3DLog.debug( "Rendering opaque and transparent bin" ); int triangles = 0; ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "CanvasPeerImpl::Drawing Main Scene" ); final RenderBinProvider binProvider = renderPass.getRenderBinProvider(); if ( binProvider.getOpaqueBin().size() > 0 ) { if ( !statesCache.enabled || !statesCache.depthTestEnabled ) { gl.glEnable( GL.GL_DEPTH_TEST ); statesCache.depthTestEnabled = true; } triangles += drawBin( gl, statesCache, glCaps, options, isScissorEnabled, isClipperEnabled, binProvider.getOpaqueBin(), view, frameId, nameOffset, nanoTime, nanoStep, renderMode ); nameOffset += binProvider.getOpaqueBin().size(); } if ( binProvider.getTransparentBin().size() > 0 ) { /* if ( statesCache.depthTestEnabled ) { gl.glDisable( GL.GL_DEPTH_TEST ); statesCache.depthTestEnabled = false; } */ triangles += drawBin( gl, statesCache, glCaps, options, isScissorEnabled, isClipperEnabled, binProvider.getTransparentBin(), view, frameId, nameOffset, nanoTime, nanoStep, renderMode ); nameOffset += binProvider.getTransparentBin().size(); } ProfileTimer.endProfile(); if ( ( renderMode == RenderMode.PICKING ) ) { if ( !statesCache.enabled || !statesCache.depthTestEnabled ) { gl.glEnable( GL.GL_DEPTH_TEST ); statesCache.depthTestEnabled = true; } } // render shadows if there are any occluders if ( ( renderMode != RenderMode.PICKING ) && ( ( shotCreator == null ) || ( shotCreator.getFormat() == ScreenshotCreator.Format.RGB ) ) ) { triangles += shadowPeer.drawShadows( gl, view, renderPass.getShadowCasterLight(), binProvider.getShadowsBin(), frameId ); } getCanvasPeer().addTriangles( triangles ); return ( nameOffset ); } /** * Stop picking mode and convert select buffer to PickRenderResult[] in addition to the default renderDone() action */ private final Object renderDone( GL gl, List< RenderPass > renderPasses, PickRequest pickRequest, long frameId ) { super.renderDone( frameId ); if ( pickRequest != null ) { gl.glFlush(); gl.glFinish(); return ( convertSelectBuffer( gl.glRenderMode( GL.GL_RENDER ), renderPasses, pickRequest.getPickAll() ) ); } return ( null ); } public final int renderRenderPass( Object glObj, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, List< RenderPass > renderPasses, RenderPass renderPass, final int rpIndex, boolean isRenderTargetMode, RenderMode renderMode, View view, boolean layeredMode, long frameId, long nanoTime, long nanoStep, PickRequest pickRequest, int nameOffset ) throws Throwable { final RenderPassConfig passConfig = renderPass.getConfig(); // notify the RenderCallbacks, if any if ( pickRequest == null ) { renderPass.getRenderCallbackNotifier().notifyBeforeRenderPassIsRendered( renderPass, getCanvasPeer().getType(), glObj ); } if ( passConfig.getColorMask() == -1 ) setColorMask( gl, colorMask, statesCache ); else setColorMask( gl, passConfig.getColorMask(), statesCache ); _SG_PrivilegedAccess.set( view, true, passConfig ); setGLModelViewMatrix( gl, view, passConfig.getCameraMode() ); if ( renderPass.isEnabled() ) { if ( passConfig != null ) { if ( passConfig.getRenderOptions() != null ) effectiveRenderOptions.loadOptions( passConfig.getRenderOptions() ); else effectiveRenderOptions.loadOptions( this.getRenderOptions() ); if ( isRenderTargetMode && ( renderMode != RenderMode.SHADOW_MAP_GENERATION ) ) getCanvasPeerBase().updateViewport( gl, passConfig.getViewport() ); } else { effectiveRenderOptions.loadOptions( this.getRenderOptions() ); if ( isRenderTargetMode && ( renderMode != RenderMode.SHADOW_MAP_GENERATION ) ) getCanvasPeerBase().updateViewport( gl, null ); } statesCache.enabled = effectiveRenderOptions.isGLStatesCacheEnabled(); // recalculate projection matrices view.getFrustum( ( getCanvasPeerBase().getCurrentViewport() == null ) ? getCanvasPeer().getCanvas3D() : getCanvasPeerBase().getCurrentViewport() ); if ( isRenderTargetMode && ( renderPass.getRenderTarget() != null ) ) { renderPass.getRenderCallbackNotifier().notifyBeforeRenderTargetIsActivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj ); renderTargetPeer.setupRenderTarget( gl, glCaps, statesCache, getCanvasPeer(), renderPass.getRenderTarget() ); renderPass.getRenderCallbackNotifier().notifyAfterRenderTargetIsActivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj ); } renderStartView( gl, view, pickRequest ); if ( isRenderTargetMode && ( renderPass.getRenderTarget() != null ) && renderPass.getRenderTarget().isBackgroundRenderingEnabled() ) { for ( int i = 0; i < renderPasses.size(); i++ ) { final RenderPass bgPass = renderPasses.get( i ); if ( bgPass instanceof BackgroundRenderPass ) { nameOffset += renderRenderPass( glObj, glCaps, statesCache, renderPasses, bgPass, i, false, renderMode, view, layeredMode, frameId, nanoTime, nanoStep, pickRequest, nameOffset ); } } gl.glClear( GL.GL_DEPTH_BUFFER_BIT ); setGLModelViewMatrix( gl, view, passConfig.getCameraMode() ); view.getFrustum( getCanvasPeerBase().getCurrentViewport() ); renderStartView( gl, view, pickRequest ); } // if we're in layered mode, we need to clear the depth buffer. if ( ( layeredMode && !renderPass.isUnlayeredModeForced() ) || ( !layeredMode && renderPass.isLayeredModeForced() ) ) { if ( rpIndex > 0 ) gl.glClear( GL.GL_DEPTH_BUFFER_BIT ); } // notify the RenderCallbacks, if any if ( pickRequest == null ) { renderPass.getRenderCallbackNotifier().notifyAfterRenderPassIsSetUp( renderPass, getCanvasPeer().getType(), glObj ); } nameOffset += renderMain( gl, glCaps, statesCache, effectiveRenderOptions, renderPass.isScissorEnabled(), renderPass.isClipperEnabled(), view, renderPass, frameId, nameOffset, nanoTime, nanoStep, ( pickRequest != null ) ? RenderMode.PICKING : RenderMode.NORMAL ); if ( isRenderTargetMode && ( renderPass.getRenderTarget() != null ) ) { renderPass.getRenderCallbackNotifier().notifyBeforeRenderTargetIsDeactivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj ); renderTargetPeer.finishRenderTarget( gl, renderPass.getRenderTarget() ); renderPass.getRenderCallbackNotifier().notifyAfterRenderTargetIsDeactivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj ); } } else if ( pickRequest != null ) { nameOffset += renderPass.getRenderBinProvider().getAtomsCount(); } if ( ( renderMode == RenderMode.SHADOW_MAP_GENERATION ) || ( ( passConfig != null ) && ( passConfig.getRenderOptions() != null ) ) || ( ( rpIndex + 1 < renderPasses.size() ) && ( renderPasses.get( rpIndex + 1 ).getConfig() != null ) && ( renderPasses.get( rpIndex + 1 ).getConfig().getRenderOptions() != null ) ) ) { super.renderDone( frameId ); super.resetStateUnitStateArrays(); } _SG_PrivilegedAccess.set( view, false, (RenderPassConfig)null ); // notify the RenderCallbacks, if any if ( pickRequest == null ) { renderPass.getRenderCallbackNotifier().notifyAfterRenderPassCompleted( renderPass, getCanvasPeer().getType(), glObj ); } return ( nameOffset ); } /** * Does the actual rendering. OpenGL context threading issues must be handled/solved earlier. */ @Override public final Object render( Object glObj, View view, List< RenderPass > renderPasses, boolean layeredMode, long frameId, long nanoTime, long nanoStep, PickRequest pickRequest ) { if ( view == null ) { return ( null ); } this.gl = (GL)glObj; lastClipperId = -1; // reset the clipper for this frame shotCreator = scheduledShotCreator; scheduledShotCreator = null; final OpenGLCapabilities glCaps = getCanvasPeer().getOpenGLCapabilities(); final OpenGLStatesCache statesCache = getStatesCache(); statesCache.enabled = getRenderOptions().isGLStatesCacheEnabled(); Object result = null; /* * Reset the ShapeAtomPeer's TransformGroup-id indicator to setup the * modelview matrix at least once per frame. */ ShapeAtomPeer.reset(); //clearCachedBackground( gl, ( pickRequest != null ) ); X3DLog.debug( "Starting to render the frame" ); try { if ( ( renderPasses != null ) && ( renderPasses.size() > 0 ) ) { final RenderBinProvider firstBinProvider = renderPasses.get( 0 ).getRenderBinProvider(); renderStart( gl, statesCache, firstBinProvider.getAtomsCount(), pickRequest ); int nameOffset = 0; for ( int i = 0; i < renderPasses.size(); i++ ) { final RenderPass renderPass = renderPasses.get( i ); if ( renderPass.isEnabled() ) { // initialize shadows if necessary if ( ( shotCreator == null ) || ( shotCreator.getFormat() == ScreenshotCreator.Format.RGB ) ) { nameOffset += shadowPeer.initShadows( gl, view, renderPass.getShadowCasterLight(), renderPass.getRenderBinProvider().getShadowsBin(), frameId ); } nameOffset += renderRenderPass( glObj, glCaps, statesCache, renderPasses, renderPass, i, true, ( pickRequest == null ) ? RenderMode.NORMAL : RenderMode.PICKING, view, layeredMode, frameId, nanoTime, nanoStep, pickRequest, nameOffset ); } } renderDone( gl, renderPasses, pickRequest, frameId ); // take a snapshot if it was requested if ( shotCreator != null ) { // Set up the OpenGL state. // Qudus: Don't know what these two calls do, but they were necessary. gl.glReadBuffer( GL.GL_FRONT ); gl.glPixelStorei( GL.GL_PACK_ALIGNMENT, 1 ); final int offset; if ( shotCreator.getFormat() == ScreenshotCreator.Format.RGBA ) //offset = glCanvas.getHeight() - glCanvas.getParent().getHeight(); offset = 0; // TODO else offset = 0; gl.glReadPixels( 0, offset, getCanvasPeer().getWidth(), getCanvasPeer().getHeight(), shotCreator.getFormat().getIntGL(), GL.GL_UNSIGNED_BYTE, shotCreator.getBuffer() ); shotCreator.createScreenshot(); shotCreator = null; } } } catch ( Throwable terr ) { X3DLog.print( terr ); terr.printStackTrace(); throw new Error( terr ); } checkGCRequested(); X3DLog.debug( "Done rendering the frame" ); return ( result ); } public void clearViewport( GL gl, float r, float g, float b, float a ) { gl.glClearColor( r, g, b, a ); gl.glClear( GL.GL_COLOR_BUFFER_BIT ); gl.glClearColor( clearColor[0], clearColor[1], clearColor[2], clearColor[3] ); } public void clearViewport( GL gl ) { clearViewport( gl, clearColor[0], clearColor[1], clearColor[2], clearColor[3] ); } /** * Takes a screenshot of the current rendering * * @param file the file to save the screenshot to * @param alpha with alpha channel? */ @Override public final void takeScreenshot( File file, boolean alpha ) { ScreenshotCreator.Format format; if ( alpha ) format = ScreenshotCreator.Format.RGBA; else format = ScreenshotCreator.Format.RGB; this.scheduledShotCreator = new ScreenshotCreator( getCanvasPeer().getWidth(), getCanvasPeer().getHeight(), format, file ); } }