/**
* 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.lwjgl;
import java.io.File;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.List;
import org.jagatoo.logging.ProfileTimer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
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;
/**
* RenderPeer implementation base for LWJGL.
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
class RenderPeerImpl extends RenderPeer
{
private final FloatBuffer gluPickMatrixBuffer = BufferUtils.createFloatBuffer( 16 );
private final DoubleBuffer planeBuffer = BufferUtils.createDoubleBuffer( 4 );
private ScissorRect lastScissorBox = null;
private int lastClipperId = -1;
private int activeClipperId = -1;
private RenderOptions effectiveRenderOptions = new RenderOptions();
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() );
}
private final FloatBuffer defaultLightColorArray = BufferUtils.createFloatBuffer( 4 );
{
defaultLightColorArray.put( 0.2f ).put( 0.2f ).put( 0.2f ).put( 1.0f ).flip();
}
public final void setDefaultLightColor( float r, float g, float b, float a )
{
defaultLightColorArray.put( r ).put( g ).put( b ).put( a ).flip();
}
public final float[] getClearColor()
{
return ( clearColor );
}
@SuppressWarnings( "unused" )
private final void drawFloor( OpenGLStatesCache statesCache )
{
GL11.glMatrixMode( GL11.GL_MODELVIEW );
GL11.glPushMatrix();
GL11.glLoadIdentity();
GL11.glDisable( GL11.GL_CULL_FACE );
statesCache.cullFaceEnabled = false;
GL11.glDisable( GL11.GL_LIGHTING ); // turn off Lighting
statesCache.lightingEnabled = false;
GL11.glDisable( GL11.GL_TEXTURE_2D );
statesCache.texture2DEnabled[ statesCache.currentServerTextureUnit ] = false;
GL11.glDisable( GL12.GL_TEXTURE_3D );
statesCache.texture3DEnabled[ statesCache.currentServerTextureUnit ] = false;
GL11.glDisable( GL11.GL_BLEND );
statesCache.blendingEnabled = false;
GL11.glDepthMask( false );
// (test)draw a floor
GL11.glColor3f( 0.2f, 0.2f, 0.7f );
GL11.glBegin( GL11.GL_QUADS ); // Begin Drawing Quads
// Floor
GL11.glNormal3f( 0.0f, 1.0f, 0.0f ); // Normal Pointing Up
GL11.glVertex3f( -200.0f, 0f, -200.0f ); // Back Left
GL11.glVertex3f( -200.0f, 0f, +200.0f ); // Front Left
GL11.glVertex3f( +200.0f, 0f, +200.0f ); // Front Right
GL11.glVertex3f( +200.0f, 0f, -200.0f );
GL11.glEnd();
GL11.glPopMatrix();
GL11.glDepthMask( statesCache.depthWriteMask );
}
private final void finishScissors( OpenGLStatesCache statesCache )
{
//if (lastScissorBox != null)
{
if ( !statesCache.enabled || statesCache.scissorTestEnabled )
{
GL11.glDisable( GL11.GL_SCISSOR_TEST );
statesCache.scissorTestEnabled = false;
}
}
lastScissorBox = null;
}
private final void startScissors( OpenGLStatesCache statesCache, ScissorRect scissorBox )
{
if ( ( scissorBox == null ) && ( ( lastScissorBox != null ) || ( GL11.glIsEnabled( GL11.GL_SCISSOR_TEST ) ) ) )
{
finishScissors( statesCache );
return;
}
if ( scissorBox != null )
{
final boolean ok;
if ( !scissorBox.equals( lastScissorBox ) )
{
scissorBox.clamp( getCanvasPeerBase().getCurrentViewport() );
ok = scissorBox.check( getCanvasPeerBase().getCurrentViewport() );
if ( ok )
GL11.glScissor( scissorBox.getX(), scissorBox.getY(), scissorBox.getWidth(), scissorBox.getHeight() );
}
else
ok = true;
if ( ok && ( ( lastScissorBox == null ) || ( !statesCache.enabled || !statesCache.scissorTestEnabled ) ) )
{
GL11.glEnable( GL11.GL_SCISSOR_TEST );
statesCache.scissorTestEnabled = true;
}
lastScissorBox = scissorBox;
}
}
public static final int translateClipPlaneIndex( int index )
{
switch ( index )
{
case 0:
return ( GL11.GL_CLIP_PLANE0 );
case 1:
return ( GL11.GL_CLIP_PLANE1 );
case 2:
return ( GL11.GL_CLIP_PLANE2 );
case 3:
return ( GL11.GL_CLIP_PLANE3 );
case 4:
return ( GL11.GL_CLIP_PLANE4 );
case 5:
return ( GL11.GL_CLIP_PLANE5 );
default:
throw new Error( "unknown clip plane " + index );
}
}
private final void finishClipper( OpenGLStatesCache statesCache )
{
for ( int i = 0; i < 6; i++ )
{
if ( !statesCache.enabled || statesCache.clipPlaneEnabled[ i ] )
{
GL11.glDisable( translateClipPlaneIndex( i ) );
statesCache.clipPlaneEnabled[ i ] = false;
}
}
activeClipperId = -1;
}
private final void startClipper( 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() )
{
GL11.glPushMatrix();
GL11.glLoadMatrix( _SG_PrivilegedAccess.getFloatBuffer( view.getModelViewTransform( false ), false ) );
if ( !clipper.isWorldCoordinateSystemUsed() )
{
final Transform3D modelView = info.getModelView();
if ( modelView != null )
{
GL11.glMultMatrix( _SG_PrivilegedAccess.getFloatBuffer( modelView, true ) );
}
}
}
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();
GL11.glClipPlane( glI, planeBuffer );
}
if ( !statesCache.enabled || !statesCache.clipPlaneEnabled[ i ] )
{
GL11.glEnable( glI );
statesCache.clipPlaneEnabled[ i ] = true;
}
}
else
{
if ( !statesCache.enabled || statesCache.clipPlaneEnabled[ i ] )
{
GL11.glDisable( glI );
statesCache.clipPlaneEnabled[ i ] = false;
}
}
}
// restore the current matrix and store the clipper id for optimizing the next call
if ( lastClipperId != clipper.getId() )
{
GL11.glPopMatrix();
lastClipperId = clipper.getId();
}
}
else
{
finishClipper( statesCache );
}
}
private final int drawBin( 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 );
try
{
if ( renderMode == RenderMode.PICKING )
//GL11.glPushName( nameOffset + atom.getLocalIndex() );
GL11.glPushName( nameOffset + i );
if ( isScissorEnabled )
startScissors( statesCache, atom.getScissorRect() );
if ( isClipperEnabled )
startClipper( statesCache, atom.getClipper(), view );
/*
if (atom.getClassification() == Classifier.INSIDE)
GL11.glHint( GL11.GL_CLIP_, GL11.GL_NICEST );
else
GL11.glHint( GL11.GL_CLIP_, GL11.GL_NICEST );
*/
triangles += this.renderAtom( atom, null, canvasPeer, glCaps, statesCache, view, options, nanoTime, nanoStep, renderMode, frameId );
}
catch ( Throwable e )
{
e.printStackTrace();
System.exit( 0 );
}
finally
{
if ( renderMode == RenderMode.PICKING )
{
GL11.glPopName();
}
}
}
//if ( isScissorEnabled )
//finishScissors( statesCache );
if ( isClipperEnabled )
finishClipper( statesCache );
return ( triangles );
}
private final void setColorMask( int colorMask, OpenGLStatesCache statesCache )
{
if ( statesCache.enabled && colorMask == statesCache.colorWriteMask )
return;
GL11.glColorMask( ( colorMask & 1 ) != 0,
( colorMask & 2 ) != 0,
( colorMask & 4 ) != 0,
( colorMask & 8 ) != 0
);
statesCache.colorWriteMask = colorMask;
}
/**
* sets up the projection and model matrices and initializes the screen
*
* @param view
*/
private final void renderStart( OpenGLStatesCache statesCache, int atomsCount, PickRequest pickRequest )
{
super.renderStart( pickRequest );
getCanvasPeerBase().beforeRenderStart();
if ( pickRequest == null )
{
selectBuffer = null;
GL11.glRenderMode( GL11.GL_RENDER );
GL11.glClearDepth( 1.0f );
if ( ( shotCreator != null ) && ( shotCreator.getFormat() == ScreenshotCreator.Format.RGBA ) )
GL11.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], 1f );
else
GL11.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], clearColor[ 3 ] );
lastScissorBox = null;
if ( !statesCache.enabled || statesCache.scissorTestEnabled )
{
GL11.glDisable( GL11.GL_SCISSOR_TEST );
statesCache.scissorTestEnabled = false;
}
if ( !disableClearBuffer )
{
if ( fullOverpaint )
GL11.glClear( GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT );
else
GL11.glClear( GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT );
}
}
else
{
GL11.glRenderMode( GL11.GL_RENDER );
GL11.glClearDepth( 1.0f );
GL11.glClear( GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT );
/*
// Start of Linux workarounds section
// Enable CULL_FACE because of the same reason - Linux compatibility...
GL11.glEnable( GL11.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
GL11.glShadeModel( GL11.GL_SMOOTH );
// Workaround for Linux picking problems
// Draw at least one triangle in RENDER mode before switching to SELECT mode
GL11.glMatrixMode( GL11.GL_MODELVIEW );
GL11.glLoadIdentity();
GL11.glBegin( GL11.GL_TRIANGLES );
GL11.glVertex3f( 0.0f, 0.0f, 0.0f);
GL11.glVertex3f( 0.0f, 0.0f, 0.0f);
GL11.glVertex3f( 0.0f, 0.0f, 0.0f);
GL11.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
selectBuffer = BufferUtils.createIntBuffer( atomsCount * 4 );
GL11.glSelectBuffer( selectBuffer );
GL11.glRenderMode( GL11.GL_SELECT );
GL11.glInitNames();
}
//setColorMask( colorMask, statesCache );
// depth buffer setup
/*
GL11.glEnable( GL11.GL_DEPTH_TEST );
statesCache.depthTestEnabled = true;
*/
// the type of depth test to do
GL11.glDepthFunc( GL11.GL_LESS );
// really nice perspective calculations
GL11.glHint( GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST );
GL11.glHint( GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST );
GL11.glHint( GL11.GL_POLYGON_SMOOTH_HINT, GL11.GL_NICEST );
GL11.glShadeModel( GL11.GL_SMOOTH );
GL11.glLightModel( GL11.GL_LIGHT_MODEL_AMBIENT, defaultLightColorArray );
if ( OpenGlExtensions.GL_EXT_separate_specular_color )
GL11.glLightModeli( GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR );
GL11.glPixelStorei( GL11.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 JMV exits.
*/
private final void gluPickMatrix( float x, float y, float width, float height, int[] viewport )
{
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;
gluPickMatrixBuffer.clear();
gluPickMatrixBuffer.put( sx ).put( 0.0f ).put( 0.0f ).put( 0.0f );
gluPickMatrixBuffer.put( 0.0f ).put( sy ).put( 0.0f ).put( 0.0f );
gluPickMatrixBuffer.put( 0.0f ).put( 0.0f ).put( 1.0f ).put( 0.0f );
gluPickMatrixBuffer.put( tx ).put( ty ).put( 0.0f ).put( 1.0f );
gluPickMatrixBuffer.flip();
GL11.glMultMatrix( gluPickMatrixBuffer );
}
/**
* Initializes the OpenGL view
*/
private final void renderStartView( View view, PickRequest pickRequest )
{
GL11.glMatrixMode( GL11.GL_PROJECTION );
GL11.glLoadIdentity();
if ( pickRequest != null )
{
final Rect2i currentViewport = getCanvasPeerBase().getCurrentViewport();
// Assume that viewport occupies the rect(0, 0, width, height)
// We could read the viewport using GL11.glGetIntegerv(GL11.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( pickRequest.getMouseX(), viewport[ 3 ] - pickRequest.getMouseY(), 1, 1, viewport );
//GLU.gluPickMatrix( pickX, viewport[3] - pickY, pickWidth, pickHeight, viewport, 0 );
//GLU.gluPerspective( view.getFieldOfView(), getWidth() / getHeight(), view.getFrontClipDistance(), view.getBackClipDistance() );
}
// Set up Projection and ModelView transforms
GL11.glMultMatrix( _SG_PrivilegedAccess.getFloatBuffer( view.getProjection(), true ) );
// YVG: In previos 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 );
//floatBuffer4x4.put( viewTrans ).flip();
//GL11.glMultMatrixf( floatBuffer4x4 );
/* Select The Modelview Matrix */
GL11.glMatrixMode( GL11.GL_MODELVIEW );
//GL11.glScalef( 1f, 1f, 1f );
//GL11.glLoadIdentity();
//GL11.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( View view, View.CameraMode mode )
{
GL11.glLoadMatrix( _SG_PrivilegedAccess.getFloatBuffer( view.getModelViewTransform( mode, true ), true ) );
}
/**
* Render the frame using the definition provided in the atomsCollector object.
*
* @throws Throwable
*/
private final int renderMain( 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 ", 2, " bins" );
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 )
{
GL11.glEnable( GL11.GL_DEPTH_TEST );
statesCache.depthTestEnabled = true;
}
triangles += drawBin( 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 )
{
GL11.glDisable( GL11.GL_DEPTH_TEST );
statesCache.depthTestEnabled = false;
}
*/
triangles += drawBin( statesCache, glCaps, options, isScissorEnabled, isClipperEnabled, binProvider.getTransparentBin(), view, frameId, nameOffset, nanoTime, nanoStep, renderMode );
nameOffset += binProvider.getTransparentBin().size();
}
ProfileTimer.endProfile();
if ( !statesCache.enabled || !statesCache.depthTestEnabled )
{
GL11.glEnable( GL11.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( 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 Object renderDone( List< RenderPass > renderPasses, PickRequest pickRequest, long frameId )
{
super.renderDone( frameId );
if ( pickRequest != null )
{
GL11.glFlush();
return ( convertSelectBuffer( GL11.glRenderMode( GL11.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( colorMask, statesCache );
else
setColorMask( passConfig.getColorMask(), statesCache );
_SG_PrivilegedAccess.set( view, true, passConfig );
setGLModelViewMatrix( 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( passConfig.getViewport() );
}
else
{
effectiveRenderOptions.loadOptions( this.getRenderOptions() );
if ( isRenderTargetMode && ( renderMode != RenderMode.SHADOW_MAP_GENERATION ) )
getCanvasPeerBase().updateViewport( 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( glCaps, statesCache, getCanvasPeer(), renderPass.getRenderTarget() );
renderPass.getRenderCallbackNotifier().notifyAfterRenderTargetIsActivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj );
}
renderStartView( 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 );
}
}
GL11.glClear( GL11.GL_DEPTH_BUFFER_BIT );
setGLModelViewMatrix( view, passConfig.getCameraMode() );
view.getFrustum( getCanvasPeerBase().getCurrentViewport() );
renderStartView( 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 )
GL11.glClear( GL11.GL_DEPTH_BUFFER_BIT );
}
// notify the RenderCallbacks, if any
if ( pickRequest == null )
{
renderPass.getRenderCallbackNotifier().notifyAfterRenderPassIsSetUp( renderPass, getCanvasPeer().getType(), glObj );
}
nameOffset += renderMain( glCaps, statesCache, effectiveRenderOptions, renderPass.isScissorEnabled(), renderPass.isClipperEnabled(),
view,
renderPass,
frameId, nameOffset, nanoTime, nanoStep,
renderMode
);
if ( isRenderTargetMode && ( renderPass.getRenderTarget() != null ) )
{
renderPass.getRenderCallbackNotifier().notifyBeforeRenderTargetIsDeactivated( renderPass, renderPass.getRenderTarget(), getCanvasPeer().getType(), glObj );
renderTargetPeer.finishRenderTarget( 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 );
}
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();
X3DLog.debug( "Starting to render the frame" );
try
{
if ( ( renderPasses != null ) && ( renderPasses.size() > 0 ) )
{
RenderBinProvider firstBinProvider = renderPasses.get( 0 ).getRenderBinProvider();
renderStart( statesCache, firstBinProvider.getAtomsCount(), pickRequest );
}
else
{
renderStart( statesCache, 0, 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( 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 );
}
}
result = renderDone( renderPasses, pickRequest, frameId );
// take a snapshot if it was requested
if ( shotCreator != null )
{
GL11.glReadPixels( 0, 0, getCanvasPeer().getWidth(), getCanvasPeer().getHeight(), shotCreator.getFormat().getIntGL(), GL11.GL_UNSIGNED_BYTE, shotCreator.getBuffer() );
shotCreator.createScreenshot();
shotCreator = null;
}
}
catch ( Throwable terr )
{
X3DLog.print( terr );
terr.printStackTrace();
if ( terr instanceof RuntimeException )
{
throw (RuntimeException)terr;
}
if ( terr instanceof Error )
{
throw (Error)terr;
}
throw new Error( terr );
}
checkGCRequested();
X3DLog.debug( "Done rendering the frame" );
return ( result );
}
public void clearViewport( float r, float g, float b, float a )
{
GL11.glClearColor( r, g, b, a );
GL11.glClear( GL11.GL_COLOR_BUFFER_BIT );
GL11.glClearColor( clearColor[ 0 ], clearColor[ 1 ], clearColor[ 2 ], clearColor[ 3 ] );
}
public void clearViewport()
{
clearViewport( 0.0f, 0.0f, 0.0f, 0.0f );
}
/**
* 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 );
}
}