/**
* 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.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.openmali.FastMath;
import org.openmali.spatial.bounds.BoundingBox;
import org.openmali.spatial.bounds.BoundingSphere;
import org.openmali.spatial.bounds.Bounds;
import org.openmali.spatial.bounds.BoundsType;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.openmali.vecmath2.Colorf;
import org.openmali.vecmath2.Matrix4f;
import org.openmali.vecmath2.Tuple3f;
import org.xith3d.render.OpenGLCapabilities;
import org.xith3d.render.OpenGLStatesCache;
import org.xith3d.render.RenderAtomPeer;
import org.xith3d.render.RenderOptions;
import org.xith3d.render.RenderPeer;
import org.xith3d.render.RenderPeer.RenderMode;
import org.xith3d.render.preprocessing.RenderAtom;
import org.xith3d.scenegraph.Transform3D;
import org.xith3d.scenegraph.TransformGroup;
import org.xith3d.scenegraph.View;
/**
* Insert package comments here
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
public class BoundsAtomPeer extends RenderAtomPeer
{
private static final Matrix4f TEMP_MAT = new Matrix4f();
private static final Colorf boundsColor_Shape = new Colorf( 1.0f, 0.0f, 0.0f, 0.0f );
private static BoundingSphere boundingSphere = new BoundingSphere();
private static BoundingBox boundingBox = new BoundingBox();
private static FloatBuffer tmpPlaneBuffer = BufferUtils.createFloatBuffer( 4 );
private static final IntBuffer tmpIntBuffer = BufferUtils.createIntBuffer( 16 );
private static FloatBuffer currentColor = BufferUtils.createFloatBuffer( 16 );
private static final Colorf boundsColor_Group = new Colorf( 0.0f, 1.0f, 0.0f, 0.0f );
private static final int DRAW_STYLE_POINT = 1;
private static final int DRAW_STYLE_LINE = 2;
private static final int DRAW_STYLE_FILL = 3;
private static final int DRAW_STYLE_SILHOUETTE = 4;
/**
* draws a sphere of the given radius centered around the origin.
* The sphere is subdivided around the x/y axis into slices and along the z axis
* into stacks (similar to lines of longitude and latitude).
*
* This code is ported from LWJGL's GLU implementation to avoid the GLU requirement. (GLU is not included in LWJGL 2.0's jar!)
*/
private static void drawSphere( float radius, int slices, int stacks, boolean withNormals, boolean normalsToOutside, boolean withTextureCoords )
{
float rho, theta;
float x, y, z;
float s, t, ds, dt;
int i, j, imin, imax;
float nsign = normalsToOutside ? +1.0f : -1.0f;
float drho = FastMath.PI / stacks;
float dtheta = FastMath.TWO_PI / slices;
int drawStyle = DRAW_STYLE_LINE;
if ( drawStyle == DRAW_STYLE_FILL )
{
if ( !withTextureCoords )
{
// draw +Z end as a triangle fan
GL11.glBegin( GL11.GL_TRIANGLE_FAN );
GL11.glNormal3f( 0.0f, 0.0f, 1.0f );
GL11.glVertex3f( 0.0f, 0.0f, nsign * radius );
for ( j = 0; j <= slices; j++ )
{
theta = ( j == slices ) ? 0.0f : j * dtheta;
x = -FastMath.sin( theta ) * FastMath.sin( drho );
y = FastMath.cos( theta ) * FastMath.sin( drho );
z = nsign * FastMath.cos( drho );
if ( withNormals )
{
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
}
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
GL11.glEnd();
}
ds = 1.0f / slices;
dt = 1.0f / stacks;
t = 1.0f; // because loop now runs from 0
if ( withTextureCoords )
{
imin = 0;
imax = stacks;
}
else
{
imin = 1;
imax = stacks - 1;
}
// draw intermediate stacks as quad strips
for ( i = imin; i < imax; i++ )
{
rho = i * drho;
GL11.glBegin( GL11.GL_QUAD_STRIP );
s = 0.0f;
for ( j = 0; j <= slices; j++ )
{
theta = ( j == slices ) ? 0.0f : j * dtheta;
x = -FastMath.sin( theta ) * FastMath.sin( rho );
y = FastMath.cos( theta ) * FastMath.sin( rho );
z = nsign * FastMath.cos( rho );
if ( withNormals )
{
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
}
if ( withTextureCoords )
{
GL11.glTexCoord2f( s, t );
}
GL11.glVertex3f( x * radius, y * radius, z * radius );
x = -FastMath.sin( theta ) * FastMath.sin( rho + drho );
y = FastMath.cos( theta ) * FastMath.sin( rho + drho );
z = nsign * FastMath.cos( rho + drho );
if ( withNormals )
{
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
}
if ( withTextureCoords )
{
GL11.glTexCoord2f( s, t - dt );
}
s += ds;
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
GL11.glEnd();
t -= dt;
}
if ( !withTextureCoords )
{
// draw -Z end as a triangle fan
GL11.glBegin( GL11.GL_TRIANGLE_FAN );
GL11.glNormal3f( 0.0f, 0.0f, -1.0f );
GL11.glVertex3f( 0.0f, 0.0f, -radius * nsign );
rho = FastMath.PI - drho;
s = 1.0f;
for ( j = slices; j >= 0; j-- )
{
theta = ( j == slices ) ? 0.0f : j * dtheta;
x = -FastMath.sin( theta ) * FastMath.sin( rho );
y = FastMath.cos( theta ) * FastMath.sin( rho );
z = nsign * FastMath.cos( rho );
if ( withNormals )
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
s -= ds;
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
GL11.glEnd();
}
}
else if ( ( drawStyle == DRAW_STYLE_LINE ) || ( drawStyle == DRAW_STYLE_SILHOUETTE ) )
{
// draw stack lines
for ( i = 1; i < stacks; i++ )
{ // stack line at i==stacks-1 was missing here
rho = i * drho;
GL11.glBegin( GL11.GL_LINE_LOOP );
for ( j = 0; j < slices; j++ )
{
theta = j * dtheta;
x = FastMath.cos( theta ) * FastMath.sin( rho );
y = FastMath.sin( theta ) * FastMath.sin( rho );
z = FastMath.cos( rho );
if ( withNormals )
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
GL11.glEnd();
}
// draw slice lines
for ( j = 0; j < slices; j++ )
{
theta = j * dtheta;
GL11.glBegin( GL11.GL_LINE_STRIP );
for ( i = 0; i <= stacks; i++ )
{
rho = i * drho;
x = FastMath.cos( theta ) * FastMath.sin( rho );
y = FastMath.sin( theta ) * FastMath.sin( rho );
z = FastMath.cos( rho );
if ( withNormals )
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
GL11.glEnd();
}
}
else if ( drawStyle == DRAW_STYLE_POINT )
{
// top and bottom-most points
GL11.glBegin( GL11.GL_POINTS );
if ( withNormals )
GL11.glNormal3f( 0.0f, 0.0f, nsign );
GL11.glVertex3f( 0.0f, 0.0f, radius );
if ( withNormals )
GL11.glNormal3f( 0.0f, 0.0f, -nsign );
GL11.glVertex3f( 0.0f, 0.0f, -radius );
// loop over stacks
for ( i = 1; i < stacks - 1; i++ )
{
rho = i * drho;
for ( j = 0; j < slices; j++ )
{
theta = j * dtheta;
x = FastMath.cos( theta ) * FastMath.sin( rho );
y = FastMath.sin( theta ) * FastMath.sin( rho );
z = FastMath.cos( rho );
if ( withNormals )
GL11.glNormal3f( x * nsign, y * nsign, z * nsign );
GL11.glVertex3f( x * radius, y * radius, z * radius );
}
}
GL11.glEnd();
}
}
private static final void drawCube( float sizeX, float sizeY, float sizeZ )
{
float hsx = sizeX / 2f;
float hsy = sizeY / 2f;
float hsz = sizeZ / 2f;
GL11.glBegin( GL11.GL_QUADS ); // Draw A Quad
GL11.glVertex3f( hsx, hsy, -hsz ); // Top Right Of The Quad (Top)
GL11.glVertex3f( -hsx, hsy, -hsz ); // Top Left Of The Quad (Top)
GL11.glVertex3f( -hsx, hsy, hsz ); // Bottom Left Of The Quad (Top)
GL11.glVertex3f( hsx, hsy, hsz ); // Bottom Right Of The Quad (Top)
GL11.glVertex3f( hsx, -hsy, hsz ); // Top Right Of The Quad (Bottom)
GL11.glVertex3f( -hsx, -hsy, hsz ); // Top Left Of The Quad (Bottom)
GL11.glVertex3f( -hsx, -hsy, -hsz ); // Bottom Left Of The Quad (Bottom)
GL11.glVertex3f( hsx, -hsy, -hsz ); // Bottom Right Of The Quad (Bottom)
GL11.glVertex3f( hsx, hsy, hsz ); // Top Right Of The Quad (Front)
GL11.glVertex3f( -hsx, hsy, hsz ); // Top Left Of The Quad (Front)
GL11.glVertex3f( -hsx, -hsy, hsz ); // Bottom Left Of The Quad (Front)
GL11.glVertex3f( hsx, -hsy, hsz ); // Bottom Right Of The Quad (Front)
GL11.glVertex3f( hsx, -hsy, -hsz ); // Bottom Left Of The Quad (Back)
GL11.glVertex3f( -hsx, -hsy, -hsz ); // Bottom Right Of The Quad (Back)
GL11.glVertex3f( -hsx, hsy, -hsz ); // Top Right Of The Quad (Back)
GL11.glVertex3f( hsx, hsy, -hsz ); // Top Left Of The Quad (Back)
GL11.glVertex3f( -hsx, hsy, hsz ); // Top Right Of The Quad (Left)
GL11.glVertex3f( -hsx, hsy, -hsz ); // Top Left Of The Quad (Left)
GL11.glVertex3f( -hsx, -hsy, -hsz ); // Bottom Left Of The Quad (Left)
GL11.glVertex3f( -hsx, -hsy, hsz ); // Bottom Right Of The Quad (Left)
GL11.glVertex3f( hsx, hsy, -hsz ); // Top Right Of The Quad (Right)
GL11.glVertex3f( hsx, hsy, hsz ); // Top Left Of The Quad (Right)
GL11.glVertex3f( hsx, -hsy, hsz ); // Bottom Left Of The Quad (Right)
GL11.glVertex3f( hsx, -hsy, -hsz ); // Bottom Right Of The Quad (Right)
GL11.glEnd(); // Done Drawing The Quad
}
/**
* Draws bounds around shapes as a debugging aid
*
* @param bounds
*/
protected static final int drawBounds( Bounds bounds, Colorf color, OpenGLStatesCache statesCache )
{
tmpIntBuffer.clear();
GL11.glGetTexEnv( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, tmpIntBuffer );
final int oldTexEnvMode = tmpIntBuffer.get();
GL11.glTexEnvi( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE );
tmpPlaneBuffer.clear();
GL11.glGetTexEnv( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, tmpPlaneBuffer );
final float[] oldTexEnvColor = new float[]
{
tmpPlaneBuffer.get(), tmpPlaneBuffer.get(), tmpPlaneBuffer.get(), tmpPlaneBuffer.get()
};
tmpPlaneBuffer.clear();
tmpPlaneBuffer.put( 0, 0f );
tmpPlaneBuffer.put( 1, 0f );
tmpPlaneBuffer.put( 2, 0f );
tmpPlaneBuffer.put( 3, 1f );
GL11.glTexEnv( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, tmpPlaneBuffer );
final boolean oldTex1D = GL11.glIsEnabled( GL11.GL_TEXTURE_1D );
final boolean oldTex2D = GL11.glIsEnabled( GL11.GL_TEXTURE_2D );
final boolean oldTex3D = GL11.glIsEnabled( GL12.GL_TEXTURE_3D );
final boolean oldTexCM = GL11.glIsEnabled( GL13.GL_TEXTURE_CUBE_MAP );
GL11.glDisable( GL11.GL_TEXTURE_1D );
GL11.glDisable( GL11.GL_TEXTURE_2D );
GL11.glDisable( GL12.GL_TEXTURE_3D );
GL11.glDisable( GL13.GL_TEXTURE_CUBE_MAP );
switch ( bounds.getType() )
{
case SPHERE:
{
//GL11.glPushMatrix();
//GL11.glLoadIdentity();
boolean isBlended = statesCache.blendingEnabled;
boolean isAlphaTest = statesCache.alphaTestEnabled;
if ( isBlended )
GL11.glDisable( GL11.GL_BLEND );
if ( isAlphaTest )
GL11.glDisable( GL11.GL_ALPHA_TEST );
currentColor.clear();
GL11.glGetFloat( GL11.GL_CURRENT_COLOR, currentColor );
if ( color == null )
GL11.glColor4f( boundsColor_Group.getRed(), boundsColor_Group.getGreen(), boundsColor_Group.getBlue(), boundsColor_Group.getAlpha() );
else
GL11.glColor4f( color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() );
BoundingSphere s = (BoundingSphere)bounds;
GL11.glTranslatef( s.getCenterX(), s.getCenterY(), s.getCenterZ() );
drawSphere( s.getRadius(), 12, 12, false, true, false );
GL11.glTranslatef( -s.getCenterX(), -s.getCenterY(), -s.getCenterZ() );
GL11.glColor4f( currentColor.get(), currentColor.get(), currentColor.get(), currentColor.get() );
if ( isBlended )
GL11.glEnable( GL11.GL_BLEND );
if ( isAlphaTest )
GL11.glEnable( GL11.GL_ALPHA_TEST );
//GL11.glPopMatrix();
break;
}
case AABB:
{
final boolean isBlended = GL11.glIsEnabled( GL11.GL_BLEND );
final boolean isAlphaTest = GL11.glIsEnabled( GL11.GL_ALPHA_TEST );
if ( isAlphaTest )
GL11.glDisable( GL11.GL_ALPHA_TEST );
currentColor.clear();
GL11.glGetFloat( GL11.GL_CURRENT_COLOR, currentColor );
GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
if ( color == null )
GL11.glColor4f( boundsColor_Group.getRed(), boundsColor_Group.getGreen(), boundsColor_Group.getBlue(), 0.05f );
else
GL11.glColor4f( color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() );
BoundingBox box = (BoundingBox)bounds;
Tuple3f boxSize = box.getSize();
GL11.glTranslatef( box.getCenterX(), box.getCenterY(), box.getCenterZ() );
GL11.glDepthMask( false );
GL11.glColor4f( boundsColor_Group.getRed(), boundsColor_Group.getGreen(), boundsColor_Group.getBlue(), 0.1f );
GL11.glEnable( GL11.GL_BLEND );
drawCube( boxSize.getX(), boxSize.getY(), boxSize.getZ() );
GL11.glDepthMask( true );
tmpIntBuffer.clear();
GL11.glGetInteger( GL11.GL_LINE_WIDTH, tmpIntBuffer );
final int LINE_WIDTH = tmpIntBuffer.get();
//tmpIntBuffer.clear();
//GL11.glGetInteger( GL11.GL_POLYGON_MODE, tmpIntBuffer );
//final int POLYGON_MODE = tmpIntBuffer.get();
GL11.glColor4f( 1.0f, 0.0f, 0.0f, 0.0f );
GL11.glPolygonMode( GL11.GL_FRONT_AND_BACK, GL11.GL_LINE );
GL11.glLineWidth( 3f );
GL11.glDisable( GL11.GL_BLEND );
drawCube( boxSize.getX(), boxSize.getY(), boxSize.getZ() );
GL11.glLineWidth( LINE_WIDTH );
//GL11.glPolygonMode( POLYGON_MODE, GL11.GL_FILL );
GL11.glPolygonMode( GL11.GL_FRONT_AND_BACK, GL11.GL_FILL );
GL11.glTranslatef( -box.getCenterX(), -box.getCenterY(), -box.getCenterZ() );
GL11.glColor4f( currentColor.get(), currentColor.get(), currentColor.get(), currentColor.get() );
if ( isBlended )
GL11.glEnable( GL11.GL_BLEND );
else
GL11.glDisable( GL11.GL_BLEND );
if ( isAlphaTest )
GL11.glEnable( GL11.GL_ALPHA_TEST );
else
GL11.glDisable( GL11.GL_ALPHA_TEST );
statesCache.blendingEnabled = isBlended;
statesCache.alphaTestEnabled = isAlphaTest;
GL11.glDepthMask( statesCache.depthWriteMask );
break;
}
}
if ( oldTex1D )
GL11.glEnable( GL11.GL_TEXTURE_1D );
if ( oldTex2D )
GL11.glEnable( GL11.GL_TEXTURE_2D );
if ( oldTex3D )
GL11.glEnable( GL12.GL_TEXTURE_3D );
if ( oldTexCM )
GL11.glEnable( GL13.GL_TEXTURE_CUBE_MAP );
statesCache.texture1DEnabled[ statesCache.currentServerTextureUnit ] = oldTex1D;
statesCache.texture2DEnabled[ statesCache.currentServerTextureUnit ] = oldTex2D;
statesCache.texture3DEnabled[ statesCache.currentServerTextureUnit ] = oldTex3D;
statesCache.textureCMEnabled[ statesCache.currentServerTextureUnit ] = oldTexCM;
tmpPlaneBuffer.clear();
tmpPlaneBuffer.put( 0, oldTexEnvColor[ 0 ] );
tmpPlaneBuffer.put( 1, oldTexEnvColor[ 1 ] );
tmpPlaneBuffer.put( 2, oldTexEnvColor[ 2 ] );
tmpPlaneBuffer.put( 3, oldTexEnvColor[ 3 ] );
GL11.glTexEnv( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, tmpPlaneBuffer );
GL11.glTexEnvi( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, oldTexEnvMode );
return ( 0 ); // (actually this is not 0, but it's not important.)
}
/**
* {@inheritDoc}
*/
@Override
public int renderAtom( RenderAtom< ? > atom, Object glObj, RenderPeer renderPeer, OpenGLCapabilities glCaps, View view, RenderOptions options, long nanoTime, long nanoStep, RenderMode renderMode, long frameId )
{
if ( renderMode != RenderMode.NORMAL )
return ( 0 );
final OpenGLStatesCache statesCache = renderPeer.getStatesCache();
// disable color-material
if ( !statesCache.enabled || statesCache.colorMaterialEnabled )
{
GL11.glDisable( GL11.GL_COLOR_MATERIAL );
statesCache.colorMaterialEnabled = false;
}
// disable lighting
if ( !statesCache.enabled || statesCache.lightingEnabled )
{
GL11.glDisable( GL11.GL_LIGHTING );
statesCache.lightingEnabled = false;
}
ShapeAtomPeer.setMatrix( view, Transform3D.IDENTITY,//atom.getTransform().getMatrix4f(),
false, true );
Bounds bounds;
if ( atom.getNode().getBounds().getType() == BoundsType.AABB )
bounds = boundingBox;
else
bounds = boundingSphere;
bounds.set( atom.getNode().getBounds() );
bounds.transform( atom.getNode().getWorldTransform().getMatrix4f() );
if ( atom.getNode() instanceof TransformGroup )
{
TEMP_MAT.set( ( (TransformGroup)atom.getNode() ).getTransform().getMatrix4f() );
TEMP_MAT.invert();
bounds.transform( TEMP_MAT );
}
return ( drawBounds( bounds, boundsColor_Shape, renderPeer.getStatesCache() ) );
}
}