/**
* 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;
import java.io.File;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.List;
import org.xith3d.picking.PickRequest;
import org.xith3d.picking.PickResult;
import org.xith3d.render.preprocessing.RenderAtom;
import org.xith3d.render.preprocessing.RenderBinProvider;
import org.xith3d.render.states.StateUnit;
import org.xith3d.render.states.units.StateUnitPeer;
import org.xith3d.scenegraph.Shape3D;
import org.xith3d.scenegraph.View;
import org.xith3d.utility.general.SortableList;
/**
* The RenderPeer is used to abstract the layer that does the actual drawing
* from the architecture of the renderer.
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
public abstract class RenderPeer
{
public static enum RenderMode
{
NORMAL,
PICKING,
SHADOW_MAP_GENERATION;
}
private CanvasPeer canvasPeer;
private StateUnitPeerRegistry stateUnitRegistry;
private RenderOptions renderOptions;
protected boolean forceNoSwap = false;
/** disableClearBuffer set to true if application does not want renderer to clear any buffers */
protected boolean disableClearBuffer = false;
/** fullOverpaint set to true if client guarantees that all the screen always completely painted and no reason to clear color buffer */
protected boolean fullOverpaint = false;
protected float[] clearColor = new float[]
{
0.2f, // red
0.2f, // green
0.2f, // blue
0.0f // alpha
};
protected int colorMask = 15;
protected boolean backgroundCachingEnabled = false;
private static final long[] nulledStates = new long[ StateUnit.MAX_STATE_TYPES ];
static
{
Arrays.fill( nulledStates, -1 );
}
private final long[] stateIDs = new long[ nulledStates.length ];
protected IntBuffer selectBuffer = null;
private SortableList< PickResult > pickResults = new SortableList< PickResult >();
private PickResult pickResult = null;
private final OpenGLStatesCache statesCache;
private static boolean gcRequested = false;
protected static void setGCRequested( boolean gcReq )
{
gcRequested = gcReq;
}
protected static final void checkGCRequested()
{
if ( gcRequested )
{
//System.gc(); // TODO: This is not good. But memory needs to be freed some day...
gcRequested = false;
}
}
protected void setCanvasPeer( CanvasPeer canvasPeer )
{
this.canvasPeer = canvasPeer;
}
public final CanvasPeer getCanvasPeer()
{
return ( canvasPeer );
}
/**
* @return the ShaderRegistry, which is responsible for the Shader handling.
*/
public final StateUnitPeerRegistry getShaderRegistry()
{
return ( stateUnitRegistry );
}
/**
* Sets the rendering options that this RenderPeer will abide by.
*
* @param renderOptions the rendering options, that this RenderPeer will abide by.
*/
public void setRenderOptions( RenderOptions renderOptions )
{
this.renderOptions = renderOptions;
}
/**
* @return the rendering options that this CanvasPeer abides by.
*/
public final RenderOptions getRenderOptions()
{
return ( renderOptions );
}
/**
* @return the {@link OpenGLStatesCache} of this render context.
*/
public final OpenGLStatesCache getStatesCache()
{
return ( statesCache );
}
/**
* Sets the color with which to clear the screen before each frame.
*
* @param r the red component
* @param g the green component
* @param b the blue component
* @param a the alpha component
*/
public final void setClearColor( float r, float g, float b, float a )
{
clearColor[ 0 ] = r;
clearColor[ 1 ] = g;
clearColor[ 2 ] = b;
clearColor[ 3 ] = a;
}
/**
* Disables or enables buffer clear operations.
*
* If DisableClearBuffer set to true, client guarantees that all it will take care of correct frame buffer
* clearing/filling. This may provide speedups for example in cases where multiple transparent objects
* rebdered over opaque background, and depth buffer testing/writing disabled.
*
* Default value for DisableClearBuffer flag is false.
*
* @param val New value for DisableClearBuffer flag
*/
public final void setDisableClearBuffer( boolean val )
{
disableClearBuffer = val;
}
/**
* Sets "Full Overpaint" flag.
*
* If Full Overpaint set to true, client guarantees that all the screen always completely painted
* and no reason to clear color buffer
*
* Default value for "Full Overpaint" flag is false.
*
* @param val New value for "Full Overpaint" flag
*/
public final void setFullOverpaint( boolean val )
{
fullOverpaint = val;
}
public final void setForceNoSwap( boolean forceNoSwap )
{
this.forceNoSwap = forceNoSwap;
}
/**
* Sets the colormask for the rendering
*
* @param enableRed
* @param enableGreen
* @param enableBlue
* @param enableAlpha
*/
public final void setColorMask( boolean enableRed, boolean enableGreen, boolean enableBlue, boolean enableAlpha )
{
this.colorMask = 0;
if ( enableRed )
colorMask |= 1;
if ( enableGreen )
colorMask |= 2;
if ( enableBlue )
colorMask |= 4;
if ( enableAlpha )
colorMask |= 8;
}
/**
* Sets flag that enables caching of the background in buffer region.
*
* @param enabled New value for background cache enable/disable flag
*/
public void setBackgroundCachingEnabled( boolean enabled )
{
backgroundCachingEnabled = enabled;
}
/**
* Called when begining a new frame to draw.
* It fills up the state and shader arrays.
*/
protected final void resetStateUnitStateArrays()
{
System.arraycopy( nulledStates, 0, stateIDs, 0, stateIDs.length );
}
protected void renderStart( PickRequest pickRequest )
{
resetStateUnitStateArrays();
if ( pickRequest != null )
{
if ( pickRequest.getPickAll() )
pickResults.clear();
else
pickResult = null;
}
}
public final void forceState( int stateType )
{
stateIDs[ stateType ] = -1;
}
/**
* Renders an Atom.
*
* @param options
* @param atom
* @param view
* @param frameId
*
* @return the number of rendered Triangles
*/
public final int renderAtom( RenderAtom< ? > atom, Object glObj, CanvasPeer canvasPeer, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, View view, RenderOptions options, long nanoTime, long nanoStep, RenderMode renderMode, long frameId )
{
if ( atom.getNode().isBillboard() )
{
BillboardManager.updateBillboardGeometry( atom, view, getCanvasPeer().getWidth(), getCanvasPeer().getHeight(), nanoTime, nanoStep, frameId );
}
final int atomType = atom.getStateType();
if ( ( atomType != org.xith3d.render.preprocessing.BoundsAtom.STATE_TYPE ) && ( renderMode == RenderMode.NORMAL ) )
{
final StateUnit[] stateUnits = atom.getStateUnits();
StateUnit stateUnit;
for ( int stateType = 0; stateType < StateUnit.MAX_STATE_TYPES; stateType++ )
{
stateUnit = stateUnits[ stateType ];
final long stateId = stateUnit.getCachedStateId();
if ( stateIDs[ stateType ] != stateId )
{
final StateUnitPeer stateUnitPeer = stateUnitRegistry.getStateUnitPeer( stateType );
stateUnitPeer.apply( atom, stateUnit, glObj, canvasPeer, this, glCaps, view, statesCache, options, nanoTime, nanoStep, renderMode, frameId );
stateIDs[ stateType ] = stateId;
}
}
}
return ( stateUnitRegistry.getRenderAtomPeer( atomType ).renderAtom( atom, glObj, this, glCaps, view, options, nanoTime, nanoStep, renderMode, frameId ) );
}
public final List< PickResult > getPickResults()
{
return ( pickResults );
}
public final PickResult getPickResult()
{
return ( pickResult );
}
protected final RenderAtom< ? > getAtomByGlobalIndex( int index, List< RenderPass > renderPasses )
{
int offset = 0;
for ( int i = 0; i < renderPasses.size(); i++ )
{
final RenderPass pass = renderPasses.get( i );
final RenderBinProvider binProvider = pass.getRenderBinProvider();
if ( pass.isEnabled() )
{
// check main opaque bin
if ( index < ( offset + binProvider.getOpaqueBin().size() ) )
return ( binProvider.getOpaqueBin().getAtom( index - offset ) );
offset += binProvider.getOpaqueBin().size();
// check main transparent bin
if ( index < ( offset + binProvider.getTransparentBin().size() ) )
return ( binProvider.getTransparentBin().getAtom( index - offset ) );
offset += binProvider.getTransparentBin().size();
}
else
{
offset += binProvider.getAtomsCount();
}
}
return ( null );
}
/**
* Convert select buffer to List<PickResult>.
*/
protected final Object convertSelectBuffer( int hits, List< RenderPass > renderPasses, boolean pickAll )
{
PickResult result;
int position = 0;
for ( int i = 0; i < hits; i++ )
{
int namesCount = selectBuffer.get( position++ );
final float zMin = (float)selectBuffer.get( position++ ) / 0x7fffffff;
final float zMax = (float)selectBuffer.get( position++ ) / 0x7fffffff;
final float zMed = zMin + ( ( zMax - zMin ) / 2.0f );
for ( int j = 0; j < namesCount; j++ )
{
final int selectName = selectBuffer.get( position++ );
try
{
RenderAtom< ? > atom = getAtomByGlobalIndex( selectName, renderPasses );
Shape3D shape = (Shape3D)atom.getNode();
result = new PickResult( shape, zMin, zMax, zMed );
if ( pickAll )
pickResults.add( result );
else if ( ( pickResult == null ) || ( result.getMinimumDistance() < pickResult.getMinimumDistance() ) )
pickResult = result;
}
catch ( Throwable t )
{
t.printStackTrace();
}
}
}
if ( pickAll )
{
pickResults.sort();
return ( pickResults );
}
return ( pickResult );
}
/**
* The frame is complete. The implementation should return as fast
* as possible, so possibly another thread should be used to wait for
* the drawing to be complete.
*/
//protected final void renderDone( Object glObj, OpenGLCapabilities glCaps, View view, RenderOptions options, long nanoTime, long nanoStep, RenderMode renderMode, long frameId )
protected final void renderDone( long frameId )
{
final long[] lastFrameIDs = getStatesCache().lastFrameId;
for ( int i = 0; i < lastFrameIDs.length; i++ )
lastFrameIDs[i] = frameId;
}
/**
* Renders a single frame using the View and RenderBins provided in
* the RenderPasses object.
*
* @param glObj the OpenGL handle object
* @param view the View used to render
* @param renderPasses the List of RenderPasses to iterate and render
* @param layeredMode if true, the RenderPasses are handled in layered mode
* @param frameId the current frame's id
* @param pickRequest <code>null</code> for normal rendering
*/
public abstract Object render( Object glObj, View view, List< RenderPass > renderPasses, boolean layeredMode, long frameId, long nanoTime, long nanoStep, PickRequest pickRequest );
/**
* Takes a screenshot of the current rendering
*
* @param file the file to save the screenshot to
* @param alpha with alpha channel?
*/
public abstract void takeScreenshot( File file, boolean alpha );
public RenderPeer( CanvasPeer canvasPeer, StateUnitPeerRegistry shaderRegistry, OpenGLStatesCache statesCache, RenderOptions renderOptions )
{
this.canvasPeer = canvasPeer;
this.stateUnitRegistry = shaderRegistry;
this.statesCache = statesCache;
this.renderOptions = renderOptions;
}
public RenderPeer( CanvasPeer canvasPeer, StateUnitPeerRegistry shaderRegistry, OpenGLStatesCache statesCache )
{
this( canvasPeer, shaderRegistry, statesCache, new RenderOptions() );
}
}