/**
* 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 javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLCapabilitiesChooser;
import javax.media.opengl.GLException;
import org.xith3d.utility.logging.X3DLog;
/**
* <P>
* The default implementation of the {@link GLCapabilitiesChooser} interface,
* which provides consistent visual selection behavior across platforms. The
* precise algorithm is deliberately left loosely specified. Some properties
* are:
* </P>
*
* <UL>
*
* <LI> As long as there is at least one available non-null GLCapabilities which
* matches the "stereo" option, will return a valid index.
*
* <LI> Attempts to match as closely as possible the given GLCapabilities, but
* will select one with fewer capabilities (i.e., lower color depth) if
* necessary.
*
* <LI> Prefers hardware-accelerated visuals to non-hardware-accelerated.
*
* <LI> If there is no exact match, prefers a more-capable visual to a
* less-capable one.
*
* <LI> If there is more than one exact match, chooses an arbitrary one.
*
* <LI> May select the opposite of a double- or single-buffered visual (based on
* the user's request) in dire situations.
*
* <LI> Color depth (including alpha) mismatches are weighted higher than depth
* buffer mismatches, which are in turn weighted higher than accumulation buffer
* (including alpha) and stencil buffer depth mismatches.
*
* </UL>
*
* @author Yuri Vl. Guschchin
*/
public class OldStyleGLCapabilitiesChooser implements GLCapabilitiesChooser
{
public int chooseCapabilities( GLCapabilities desired, GLCapabilities[] available, int windowSystemRecommendedChoice )
{
// Create score array
int[] scores = new int[ available.length ];
int NO_SCORE = -9999999;
int DOUBLE_BUFFER_MISMATCH_PENALTY = 1000;
int STENCIL_MISMATCH_PENALTY = 500;
int SAMPLE_BUFFERS_MISMATCH_PENALTY = 100;
int NUM_SAMPLES_MISMATCH_PENALTY = 10;
// Pseudo attempt to keep equal rank penalties scale-equivalent
// (e.g., stencil mismatch is 3 * accum because there are 3 accum
// components)
int COLOR_MISMATCH_PENALTY_SCALE = 36;
int DEPTH_MISMATCH_PENALTY_SCALE = 6;
int ACCUM_MISMATCH_PENALTY_SCALE = 1;
int STENCIL_MISMATCH_PENALTY_SCALE = 3;
for ( int i = 0; i < scores.length; i++ )
{
scores[ i ] = NO_SCORE;
}
// Compute score for each
for ( int i = 0; i < scores.length; i++ )
{
GLCapabilities cur = available[ i ];
if ( cur == null )
{
continue;
}
if ( desired.getStereo() != cur.getStereo() )
{
continue;
}
int score = 0;
// Compute difference in color depth
// (Note that this decides the direction of all other penalties)
score += ( COLOR_MISMATCH_PENALTY_SCALE * ( ( cur.getRedBits() + cur.getGreenBits() + cur.getBlueBits() + cur.getAlphaBits() ) - ( desired.getRedBits() + desired.getGreenBits() + desired.getBlueBits() + desired.getAlphaBits() ) ) );
// Compute difference in depth buffer depth
score += ( DEPTH_MISMATCH_PENALTY_SCALE * sign( score ) * Math.abs( cur.getDepthBits() - desired.getDepthBits() ) );
// Compute difference in accumulation buffer depth
score += ( ACCUM_MISMATCH_PENALTY_SCALE * sign( score ) * Math.abs( ( cur.getAccumRedBits() + cur.getAccumGreenBits() + cur.getAccumBlueBits() + cur.getAccumAlphaBits() ) - ( desired.getAccumRedBits() + desired.getAccumGreenBits() + desired.getAccumBlueBits() + desired.getAccumAlphaBits() ) ) );
// Compute difference in stencil bits
score += STENCIL_MISMATCH_PENALTY_SCALE * sign( score ) * ( cur.getStencilBits() - desired.getStencilBits() );
if ( cur.getDoubleBuffered() != desired.getDoubleBuffered() )
{
score += sign( score ) * DOUBLE_BUFFER_MISMATCH_PENALTY;
}
if ( ( desired.getStencilBits() > 0 ) && ( cur.getStencilBits() == 0 ) )
{
score += sign( score ) * STENCIL_MISMATCH_PENALTY;
}
if ( desired.getSampleBuffers() != cur.getSampleBuffers() )
score += SAMPLE_BUFFERS_MISMATCH_PENALTY;
score += NUM_SAMPLES_MISMATCH_PENALTY * sign( score ) * Math.abs( desired.getNumSamples() - cur.getNumSamples() );
scores[ i ] = score;
X3DLog.debug( "Available " + i + ": " + available[ i ] + " MS: " + available[ i ].getSampleBuffers() + " " + available[ i ].getNumSamples() + " Score: " + scores[ i ] );
}
// Now prefer hardware-accelerated visuals by pushing scores of
// non-hardware-accelerated visuals out
boolean gotHW = false;
int maxAbsoluteHWScore = 0;
for ( int i = 0; i < scores.length; i++ )
{
int score = scores[ i ];
if ( score == NO_SCORE )
{
continue;
}
GLCapabilities cur = available[ i ];
if ( cur.getHardwareAccelerated() )
{
int absScore = Math.abs( score );
if ( !gotHW || ( absScore > maxAbsoluteHWScore ) )
{
gotHW = true;
maxAbsoluteHWScore = absScore;
}
}
}
if ( gotHW )
{
for ( int i = 0; i < scores.length; i++ )
{
int score = scores[ i ];
if ( score == NO_SCORE )
{
continue;
}
GLCapabilities cur = available[ i ];
if ( !cur.getHardwareAccelerated() )
{
if ( score <= 0 )
{
score -= maxAbsoluteHWScore;
}
else if ( score > 0 )
{
score += maxAbsoluteHWScore;
}
scores[ i ] = score;
}
}
}
// Ready to select. Choose score closest to 0.
int scoreClosestToZero = NO_SCORE;
int chosenIndex = -1;
for ( int i = 0; i < scores.length; i++ )
{
final int score = scores[ i ];
if ( score == NO_SCORE )
{
continue;
}
// Don't substitute a positive score for a smaller negative score
if ( ( scoreClosestToZero == NO_SCORE ) || ( Math.abs( score ) < Math.abs( scoreClosestToZero ) && ( ( sign( scoreClosestToZero ) < 0 ) || ( sign( score ) > 0 ) ) ) )
{
scoreClosestToZero = score;
chosenIndex = i;
}
}
if ( chosenIndex < 0 )
{
throw new GLException( "Unable to select one of the provided GLCapabilities" );
}
X3DLog.debug( "Chosen index: ", chosenIndex );
X3DLog.debug( "Chosen capabilities:" );
if ( available[ chosenIndex ] != null )
X3DLog.debug( available[ chosenIndex ] + " MS: " + available[ chosenIndex ].getSampleBuffers() + " " + available[ chosenIndex ].getNumSamples() );
return ( chosenIndex );
}
private static int sign( int score )
{
if ( score < 0 )
{
return ( -1 );
}
return ( 1 );
}
}