/**
* 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.scenegraph.primitives;
import org.jagatoo.opengl.enums.TextureBoundaryMode;
import org.jagatoo.opengl.enums.TextureFormat;
import org.openmali.vecmath2.Point3f;
import org.openmali.vecmath2.TexCoord2f;
import org.xith3d.loaders.texture.TextureLoader;
import org.xith3d.loaders.texture.TextureLoader.FlipMode;
import org.xith3d.render.BackgroundRenderPass;
import org.xith3d.render.BaseRenderPassConfig;
import org.xith3d.scenegraph.Appearance;
import org.xith3d.scenegraph.BranchGroup;
import org.xith3d.scenegraph.GroupNode;
import org.xith3d.scenegraph.RenderingAttributes;
import org.xith3d.scenegraph.Shape3D;
import org.xith3d.scenegraph.Texture;
import org.xith3d.scenegraph.TriangleStripArray;
import org.xith3d.scenegraph.Geometry.Optimization;
import org.xith3d.scenegraph.View.CameraMode;
import org.xith3d.utility.texturing.CubeTextureSet;
/**
* A SkyBox is a special type of Background Node. It is a six sided cube which is
* actually very small (20 x 20 x 20). It is drawn with the depth buffer disabled,
* so all objects which are drawn after it will be drawn in front. As it is a background
* node, it will always be drawn as if the camera was in its center regardless of the
* actual position of the camera. For realism, the six textures need to be carefully constructed
* with a 90 degree field of view. Typically this can be done in most 3d terrain generation
* programs (for example Terragen).
*
* @author William Denniss
* @author Marvin Froehlich (aka Qudus)
*/
public class SkyBox extends BackgroundRenderPass
{
private static final boolean DEFAULT_TEXTURE_FLIP = false;
private static final float DEFAULT_SIZE = 20f;
public static <G extends GroupNode> G createSkyBoxGroup( float size, Texture[] textures, G group )
{
if ( textures.length != 6 )
{
throw new IllegalArgumentException( "The given array of Textures MUST be of length 6." );
}
final float halfSize = size / 2f;
// vertex-coords of the cube...
final Point3f leftTopFront = new Point3f( -halfSize, halfSize, halfSize );
final Point3f rightTopFront = new Point3f( halfSize, halfSize, halfSize );
final Point3f rightBottomFront = new Point3f( halfSize, -halfSize, halfSize );
final Point3f leftBottomFront = new Point3f( -halfSize, -halfSize, halfSize );
final Point3f leftTopBack = new Point3f( -halfSize, halfSize, -halfSize );
final Point3f rightTopBack = new Point3f( halfSize, halfSize, -halfSize );
final Point3f rightBottomBack = new Point3f( halfSize, -halfSize, -halfSize );
final Point3f leftBottomBack = new Point3f( -halfSize, -halfSize, -halfSize );
/*
// Coordinates for 6 Quads
Point3f[][] vertices = new Point3f[][]
{
{
// back
leftBottomBack, rightBottomBack, leftTopBack,
//leftTopBack, rightBottomBack,
rightTopBack
},
{
// right
rightBottomBack, rightBottomFront, rightTopBack,
//rightTopBack, rightBottomFront,
rightTopFront
},
{
// front
rightBottomFront, leftBottomFront, rightTopFront,
//rightTopFront, leftBottomFront,
leftTopFront
},
{
// left
leftBottomFront, leftBottomBack, leftTopFront,
//leftTopFront, leftBottomBack,
leftTopBack
},
{
// top
leftTopBack, rightTopBack, leftTopFront,
//leftTopFront, rightTopBack,
rightTopFront
},
{
// bottom
leftBottomFront, rightBottomFront, leftBottomBack,
//leftBottomBack, rightBottomFront,
rightBottomBack
}
};
*/
/*
// Coordinates for 6 Quads
Point3f[][] vertices = new Point3f[][]
{
{
leftTopFront, rightTopFront, rightBottomFront, leftBottomFront
},
{
rightTopFront, rightTopBack, rightBottomBack, rightBottomFront
},
{
rightTopBack, leftTopBack, leftBottomBack, rightBottomBack
},
{
leftTopBack, leftTopFront, leftBottomFront, leftBottomBack
},
{
leftTopBack, rightTopBack, rightTopFront, leftTopFront
},
{
leftBottomFront, rightBottomFront, rightBottomBack, leftBottomBack
},
};
*/
Point3f[][] vertices = new Point3f[][]
{
{
leftTopFront, rightTopFront, leftBottomFront, rightBottomFront
},
{
rightTopFront, rightTopBack, rightBottomFront, rightBottomBack
},
{
rightTopBack, leftTopBack, rightBottomBack, leftBottomBack
},
{
leftTopBack, leftTopFront, leftBottomBack, leftBottomFront
},
{
leftTopBack, rightTopBack, leftTopFront, rightTopFront
},
{
leftBottomFront, rightBottomFront, leftBottomBack, rightBottomBack
},
};
// Texture coordinates (same for all sides)
/*
TexCoord2f[] texCoords = new TexCoord2f[]
{
new TexCoord2f( 0f, 0f ),
new TexCoord2f( 1f, 0f ),
new TexCoord2f( 0f, 1f ),
new TexCoord2f( 1f, 1f ),
};
*/
/*
TexCoord2f[] texCoords = new TexCoord2f[]
{
new TexCoord2f( 0f, 1f ),
new TexCoord2f( 1f, 1f ),
new TexCoord2f( 1f, 0f ),
new TexCoord2f( 0f, 0f ),
};
*/
TexCoord2f[] texCoords = new TexCoord2f[]
{
new TexCoord2f( 0f, 1f ),
new TexCoord2f( 1f, 1f ),
new TexCoord2f( 0f, 0f ),
new TexCoord2f( 1f, 0f ),
};
Shape3D[] sh = new Shape3D[ 6 ];
RenderingAttributes ra = new RenderingAttributes();
ra.setDepthBufferWriteEnabled( false );
for ( int i = 0; i < sh.length; i++ )
{
// Skips this side if the texture is null (i.e. the side isn't needed).
if ( textures[ i ] == null )
{
continue;
}
// Creates the side
//QuadArray quads = new QuadArray( vertices[ i ].length );
//TriangleArray quads = new TriangleArray( vertices[ i ].length );
TriangleStripArray quads = new TriangleStripArray( vertices[ i ].length );
quads.setOptimization( Optimization.USE_VBOS );
quads.setCoordinates( 0, vertices[ i ] );
quads.setTextureCoordinates( 0, 0, texCoords );
//quads.calculateFaceNormals();
// Clamped texture
Texture texture = textures[ i ];
texture.setBoundaryModeS( TextureBoundaryMode.CLAMP_TO_EDGE );
texture.setBoundaryModeT( TextureBoundaryMode.CLAMP_TO_EDGE );
// Sets texture and diables depth buffer (so all nodes drawn after this one will be drawn in front)
Appearance a = new Appearance();
a.setRenderingAttributes( ra );
a.setTexture( texture );
sh[ i ] = new Shape3D( quads, a);
group.addChild( sh[ i ] );
}
group.setPickableRecursive( false );
return ( group );
}
public static <G extends GroupNode> G createSkyBoxGroup( Texture[] textures, G group )
{
return ( createSkyBoxGroup( DEFAULT_SIZE, textures, group ) );
}
public static BranchGroup createSkyBoxGroup( float size, Texture[] textures )
{
BranchGroup skybox = new BranchGroup();
createSkyBoxGroup( size, textures, skybox );
//skybox.setPickableRecursive( false );
return ( skybox );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
*/
public static final BranchGroup createSkyBoxGroup( Texture front, Texture right, Texture back, Texture left, Texture top, Texture bottom )
{
return ( createSkyBoxGroup( DEFAULT_SIZE, new Texture[] { front, right, back, left, top, bottom } ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param textures a six elemental array of Textures of the following form:
* <blockquote>
* [0] The front image (0 degrees rotation, 0 degrees pitch)
* [1] The right image (90 degrees rotation, 0 degrees pitch)
* [2] The back image (180 degrees rotation, 0 degrees pitch)
* [3] The left image (270 degrees rotation, 0 degrees pitch)
* [4] The top image (0 degrees rotation, -90 degrees pitch)
* [5] The bottom image (0 degrees rotation, 90 degrees pitch)
* </blockquote>
* @param flipTextures flip textures vertically?
*/
public static final BranchGroup createSkyBoxGroup( String[] textures, boolean flipTextures )
{
return ( createSkyBoxGroup( textures[ 0 ], textures[ 1 ], textures[ 2 ], textures[ 3 ], textures[ 4 ], textures[ 5 ], flipTextures ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param textures a six elemental array of Textures of the following form:
* <blockquote>
* [0] The front image (0 degrees rotation, 0 degrees pitch)
* [1] The right image (90 degrees rotation, 0 degrees pitch)
* [2] The back image (180 degrees rotation, 0 degrees pitch)
* [3] The left image (270 degrees rotation, 0 degrees pitch)
* [4] The top image (0 degrees rotation, -90 degrees pitch)
* [5] The bottom image (0 degrees rotation, 90 degrees pitch)
* </blockquote>
*/
public static final BranchGroup createSkyBoxGroup( String[] textures )
{
return ( createSkyBoxGroup( textures, DEFAULT_TEXTURE_FLIP ) );
}
private static Texture getTextureOrNull( String texture, boolean flipVertically )
{
if ( texture == null )
return ( null );
TextureLoader tl = TextureLoader.getInstance();
tl.getCache().pushEnabled( false );
/*
* In OpenGL a flipped texture is the "normal" case.
* Therefore we use (flipVertically == true) -> NOT_FLIPPED.
*/
FlipMode flipMode = flipVertically ? FlipMode.NOT_FLIPPED : FlipMode.FLIPPED_VERTICALLY;
Texture tex = tl.getTexture( texture, flipMode, TextureFormat.RGB, Texture.MipmapMode.BASE_LEVEL );
tl.getCache().popEnabled();
tex.enableAutoFreeLocalData();
return ( tex );
}
private static Texture[] createTexturesArray( String front, String right, String back, String left, String top, String bottom, boolean flipVertically )
{
Texture[] textures = new Texture[ 6 ];
textures[ 0 ] = getTextureOrNull( front, flipVertically );
textures[ 1 ] = getTextureOrNull( right, flipVertically );
textures[ 2 ] = getTextureOrNull( back, flipVertically );
textures[ 3 ] = getTextureOrNull( left, flipVertically );
textures[ 4 ] = getTextureOrNull( top, flipVertically );
textures[ 5 ] = getTextureOrNull( bottom, flipVertically );
for ( int i = 0; i < textures.length; i++ )
{
textures[ i ].setBoundaryModeS( TextureBoundaryMode.CLAMP_TO_EDGE );
textures[ i ].setBoundaryModeT( TextureBoundaryMode.CLAMP_TO_EDGE );
}
return ( textures );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was
* passed will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
* @param flipVertically
*/
public static final BranchGroup createSkyBoxGroup( String front, String right, String back, String left, String top, String bottom, boolean flipVertically )
{
return ( createSkyBoxGroup( DEFAULT_SIZE, createTexturesArray( front, right, back, left, top, bottom, flipVertically ) ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was
* passed will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
*/
public static final BranchGroup createSkyBoxGroup( String front, String right, String back, String left, String top, String bottom )
{
return ( createSkyBoxGroup( front, right, back, left, top, bottom, DEFAULT_TEXTURE_FLIP ) );
}
public static final BranchGroup createSkyBoxGroup( CubeTextureSet textureSet )
{
return ( createSkyBoxGroup( textureSet.getFrontTexture(), textureSet.getRightTexture(), textureSet.getBackTexture(), textureSet.getLeftTexture(), textureSet.getTopTexture(), textureSet.getBottomTexture() ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param textures a six elemental array of Textures of the following form:
* <blockquote>
* [0] The front image (0 degrees rotation, 0 degrees pitch)
* [1] The right image (90 degrees rotation, 0 degrees pitch)
* [2] The back image (180 degrees rotation, 0 degrees pitch)
* [3] The left image (270 degrees rotation, 0 degrees pitch)
* [4] The top image (0 degrees rotation, -90 degrees pitch)
* [5] The bottom image (0 degrees rotation, 90 degrees pitch)
* </blockquote>
*/
public SkyBox( Texture[] textures )
{
super( createSkyBoxGroup( DEFAULT_SIZE, textures ), new BaseRenderPassConfig( CameraMode.VIEW_FIXED_POSITION ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
*/
public SkyBox( Texture front, Texture right, Texture back, Texture left, Texture top, Texture bottom )
{
this( new Texture[] { front, right, back, left, top, bottom } );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param textures a six elemental array of Textures of the following form:
* <blockquote>
* [0] The front image (0 degrees rotation, 0 degrees pitch)
* [1] The right image (90 degrees rotation, 0 degrees pitch)
* [2] The back image (180 degrees rotation, 0 degrees pitch)
* [3] The left image (270 degrees rotation, 0 degrees pitch)
* [4] The top image (0 degrees rotation, -90 degrees pitch)
* [5] The bottom image (0 degrees rotation, 90 degrees pitch)
* </blockquote>
*/
public SkyBox( String[] textures, boolean flipVertically )
{
this( textures[ 0 ], textures[ 1 ], textures[ 2 ], textures[ 3 ], textures[ 4 ], textures[ 5 ], flipVertically );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was passed
* will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param textures a six elemental array of Textures of the following form:
* <blockquote>
* [0] The front image (0 degrees rotation, 0 degrees pitch)
* [1] The right image (90 degrees rotation, 0 degrees pitch)
* [2] The back image (180 degrees rotation, 0 degrees pitch)
* [3] The left image (270 degrees rotation, 0 degrees pitch)
* [4] The top image (0 degrees rotation, -90 degrees pitch)
* [5] The bottom image (0 degrees rotation, 90 degrees pitch)
* </blockquote>
*/
public SkyBox( String[] textures )
{
this( textures, DEFAULT_TEXTURE_FLIP );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was
* passed will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
* @param flipVertically
*/
public SkyBox( String front, String right, String back, String left, String top, String bottom, boolean flipVertically )
{
this( createTexturesArray( front, right, back, left, top, bottom, flipVertically ) );
}
/**
* Creates a new SkyBox using the six given Textures. <i>Null</i> may be
* passed instead of the Texture filename, and those sides which <i>null</i> was
* passed will not be included in the SkyBox (e.g. if you don't need a bottom or top
* to the SkyBox, simply pass <i>null</i> for those Textures).
*
* @param front The front image (0 degrees rotation, 0 degrees pitch)
* @param right The right image (90 degrees rotation, 0 degrees pitch)
* @param back The back image (180 degrees rotation, 0 degrees pitch)
* @param left The left image (270 degrees rotation, 0 degrees pitch)
* @param top The top image (0 degrees rotation, -90 degrees pitch)
* @param bottom The bottom image (0 degrees rotation, 90 degrees pitch)
*/
public SkyBox( String front, String right, String back, String left, String top, String bottom )
{
this( front, right, back, left, top, bottom, DEFAULT_TEXTURE_FLIP );
}
public SkyBox( CubeTextureSet textureSet )
{
this( textureSet.getFrontTexture(), textureSet.getRightTexture(), textureSet.getBackTexture(), textureSet.getLeftTexture(), textureSet.getTopTexture(), textureSet.getBottomTexture() );
}
}