package org.andork.j3d;
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingBox;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GeometryStripArray;
import javax.media.j3d.Group;
import javax.media.j3d.IndexedGeometryArray;
import javax.media.j3d.IndexedGeometryStripArray;
import javax.media.j3d.IndexedLineArray;
import javax.media.j3d.IndexedPointArray;
import javax.media.j3d.IndexedQuadArray;
import javax.media.j3d.IndexedTriangleArray;
import javax.media.j3d.LineArray;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.PointArray;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.RenderingAttributes;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.TriangleArray;
import javax.swing.JOptionPane;
import javax.vecmath.Color3f;
import javax.vecmath.Color4f;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import org.andork.generic.Predicate;
import org.andork.generic.Visitor;
import org.andork.util.ArrayUtils;
public class J3DUtils
{
public static float max( float a , float b , float c )
{
return a > b ? ( a > c ? a : c ) : ( b > c ? b : c );
}
public static float min( float a , float b , float c )
{
return a < b ? ( a < c ? a : c ) : ( b < c ? b : c );
}
public static double min( double ... args )
{
double result = args[ 0 ];
for( int i = 1 ; i < args.length ; i++ )
{
result = Math.min( result , args[ i ] );
}
return result;
}
public static double max( double ... args )
{
double result = args[ 0 ];
for( int i = 1 ; i < args.length ; i++ )
{
result = Math.max( result , args[ i ] );
}
return result;
}
public static void printSceneGraph( Group g , int tablevel )
{
for( int i = 0 ; i < tablevel ; i++ )
{
System.out.print( '\t' );
}
System.out.println( g );
final Enumeration children = g.getAllChildren( );
while( children.hasMoreElements( ) )
{
final SceneGraphObject o = ( SceneGraphObject ) children.nextElement( );
if( o instanceof Group )
{
printSceneGraph( ( Group ) o , tablevel + 1 );
}
if( o instanceof Node )
{
printSceneGraph( ( Node ) o , tablevel + 1 );
}
}
}
public static void printSceneGraph( Node n , int tablevel )
{
for( int i = 0 ; i < tablevel ; i++ )
{
System.out.print( '\t' );
}
System.out.println( n );
}
public static int getFirstPtAfter( ArrayList<Point2f> data , float depth )
{
if( data.size( ) == 0 )
{
return -2;
}
if( depth > data.get( data.size( ) - 1 ).x )
{
return -1;
}
int lb = 0 , ub = data.size( );
while( ub > lb + 1 )
{
final int mid = ( lb + ub ) >> 1;
if( data.get( mid ).getX( ) < depth )
{
lb = mid;
}
else
{
ub = mid;
}
}
if( data.get( lb ).getX( ) >= depth )
{
return lb;
}
else
{
return ub;
}
}
public static int getFirstPtBefore( ArrayList<Point2f> data , float depth )
{
if( data.size( ) == 0 )
{
return -2;
}
if( depth < data.get( 0 ).x )
{
return -1;
}
int lb = 0 , ub = data.size( ) - 1;
while( ub > lb + 1 )
{
final int mid = ( lb + ub ) >> 1;
if( data.get( mid ).getX( ) < depth )
{
lb = mid;
}
else
{
ub = mid;
}
}
if( data.get( ub ).getX( ) <= depth )
{
return ub;
}
else
{
return lb;
}
}
public static class DataPointComparator implements Comparator<Point2f>
{
private static final DataPointComparator instance = new DataPointComparator( );
public int compare( Point2f o1 , Point2f o2 )
{
return ( o1.x > o2.x ? 1 : ( o1.x < o2.x ? -1 : 0 ) );
}
public static DataPointComparator getIntance( )
{
return instance;
}
}
// TODO all of these transform methods could be greatly optimized
/**
* Creates a {@link Shape3D} backed by a {@link QuadArray}.
*
* @param coords
* a <code>List</code> of 4*N entries, where N >= 1.
* @param texcoords
* the texture coordinates for each coordinate (can be <code>null</code>).
* @param colors
* the colors for each coordinate (can be <code>null</code>).
* @param normals
* the normals for each coordinate (can be <code>null</code>).
* @param appearance
* the appearance (can be <code>null</code>).
*/
public static Shape3D createQuadArray( List<Point3f> coords , List<TexCoord2f> texcoords , List<Color4f> colors , List<Vector3f> normals , Appearance appearance )
{
final boolean useTexcoords = texcoords != null && !texcoords.isEmpty( );
final boolean useColors = colors != null && !colors.isEmpty( );
final boolean useNormals = normals != null && !normals.isEmpty( );
if( coords.size( ) < 4 || ( coords.size( ) % 4 ) != 0 )
{
throw new IllegalArgumentException( "coords must have 4*N entries, where N >= 1" );
}
if( useTexcoords && texcoords.size( ) != coords.size( ) )
{
throw new IllegalArgumentException( "if texcoords is not null or empty it must be the same size as coords" + " (texcoords.size() = " + texcoords.size( ) + ", coords.size() = " + coords.size( ) );
}
if( useColors && colors.size( ) != coords.size( ) )
{
throw new IllegalArgumentException( "if colors is not null or empty it must be the same size as coords" + " (colors.size() = " + colors.size( ) + ", coords.size() = " + coords.size( ) );
}
if( useNormals && normals.size( ) != coords.size( ) )
{
throw new IllegalArgumentException( "if normals is not null or empty it must be the same size as coords" + " (normals.size() = " + normals.size( ) + ", coords.size() = " + coords.size( ) );
}
int vertexFormat = GeometryArray.COORDINATES;
if( useTexcoords )
{
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
}
if( useColors )
{
vertexFormat |= GeometryArray.COLOR_4;
}
if( useNormals )
{
vertexFormat |= GeometryArray.NORMALS;
}
final QuadArray geom = new QuadArray( coords.size( ) , vertexFormat );
int i;
for( i = 0 ; i < coords.size( ) ; i++ )
{
geom.setCoordinate( i , coords.get( i ) );
if( useTexcoords )
{
geom.setTextureCoordinate( 0 , i , texcoords.get( i ) );
}
if( useColors )
{
geom.setColor( i , colors.get( i ) );
}
if( useNormals )
{
geom.setNormal( i , normals.get( i ) );
}
}
final Shape3D shape = new Shape3D( geom );
if( appearance != null )
{
shape.setAppearance( appearance );
}
return shape;
}
/**
* Creates a {@link Shape3D} backed by an {@link IndexedQuadArray}.
*
* @param coordRef
* the reference coordinates
* @param coordIdx
* the coordinate indices (must have 4*N entries, where N >= 1)
* @param texCoordRef
* the reference texture coordinates (can be null)
* @param texCoordIdx
* the texture coordinate indices (can be null)
* @param colorRef
* the reference vertex colors (can be null)
* @param colorIdx
* the vertex color indices (can be null)
* @param normalRef
* the reference normals (can be null)
* @param normalIdx
* the normal indices (can be null)
* @param appearance
* the appearance (can be null)
*/
public static Shape3D createIndexedQuadArray( List<Point3f> coordRef , List<Integer> coordIdx , List<TexCoord2f> texCoordRef , List<Integer> texCoordIdx , List<Color4f> colorRef , List<Integer> colorIdx , List<Vector3f> normalRef , List<Integer> normalIdx , Appearance appearance )
{
final boolean useTexcoords = texCoordIdx != null && !texCoordIdx.isEmpty( );
final boolean useColors = colorIdx != null && !colorIdx.isEmpty( );
final boolean useNormals = normalIdx != null && !normalIdx.isEmpty( );
if( useTexcoords && texCoordIdx.size( ) != coordIdx.size( ) )
{
throw new IllegalArgumentException( "if texCoordIdx is not null or empty it must be the same size as coordIdx" + " (texCoordIdx.size() = " + texCoordIdx.size( ) + ", coordIdx.size() = " + coordIdx.size( ) );
}
if( useColors && colorIdx.size( ) != coordIdx.size( ) )
{
throw new IllegalArgumentException( "if colorIdx is not null or empty it must be the same size as coordIdx" + " (colorIdx.size() = " + colorIdx.size( ) + ", coordIdx.size() = " + coordIdx.size( ) );
}
if( useNormals && normalIdx.size( ) != coordIdx.size( ) )
{
throw new IllegalArgumentException( "if normalIdx is not null or empty it must be the same size as coordIdx" + " (normalIdx.size() = " + normalIdx.size( ) + ", coordIdx.size() = " + coordIdx.size( ) );
}
int vertexFormat = GeometryArray.COORDINATES | GeometryArray.BY_REFERENCE;
if( useTexcoords )
{
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
}
if( useColors )
{
vertexFormat |= GeometryArray.COLOR_4;
}
if( useNormals )
{
vertexFormat |= GeometryArray.NORMALS;
}
int refCount = coordRef.size( );
if( useTexcoords )
{
refCount = Math.max( refCount , texCoordRef.size( ) );
}
if( useColors )
{
refCount = Math.max( refCount , colorRef.size( ) );
}
if( useNormals )
{
refCount = Math.max( refCount , normalRef.size( ) );
}
final IndexedQuadArray geom = new IndexedQuadArray( refCount , vertexFormat , coordIdx.size( ) );
float[ ] coordArray = org.andork.vecmath.VecmathUtils.toFloatArray3( coordRef , null );
// if there are more texcoords, colors, or normals than coordinates we
// need to resize the coordinates
// array so that there isn't an IndexOutOfBoundsException
if( coordArray.length / 3 < refCount )
{
final float[ ] newArray = new float[ refCount * 3 ];
System.arraycopy( coordArray , 0 , newArray , 0 , coordArray.length );
// duplicate the last coordinate so that the bounding box doesn't
// get screwed up
final float x = coordArray[ coordArray.length - 3 ];
final float y = coordArray[ coordArray.length - 2 ];
final float z = coordArray[ coordArray.length - 1 ];
for( int i = coordArray.length ; i < refCount * 3 ; i += 3 )
{
newArray[ i ] = x;
newArray[ i + 1 ] = y;
newArray[ i + 2 ] = z;
}
coordArray = newArray;
}
geom.setCoordRefFloat( coordArray );
if( useTexcoords )
{
final float[ ] texCoordArray = org.andork.vecmath.VecmathUtils.toFloatArray2( texCoordRef , null );
geom.setTexCoordRefFloat( 0 , texCoordArray );
}
if( useColors )
{
final float[ ] colorArray = org.andork.vecmath.VecmathUtils.toFloatArray4( colorRef , null );
geom.setColorRefFloat( colorArray );
}
if( useNormals )
{
final float[ ] normalArray = org.andork.vecmath.VecmathUtils.toFloatArray3( normalRef , null );
geom.setNormalRefFloat( normalArray );
}
int i;
for( i = 0 ; i < coordIdx.size( ) ; i++ )
{
geom.setCoordinateIndex( i , coordIdx.get( i ) );
if( useTexcoords )
{
geom.setTextureCoordinateIndex( 0 , i , texCoordIdx.get( i ) );
}
if( useColors )
{
geom.setColorIndex( i , colorIdx.get( i ) );
}
if( useNormals )
{
geom.setNormalIndex( i , normalIdx.get( i ) );
}
}
final Shape3D shape = new Shape3D( geom );
if( appearance != null )
{
shape.setAppearance( appearance );
}
return shape;
}
/**
* Creates a {@link Shape3D} backed by an {@link IndexedQuadArray}.
*
* @param coordRef
* the reference coordinates
* @param coordIdx
* the coordinate indices (must have 4*N entries, where N >= 1)
* @param texCoordRef
* the reference 2d texture coordinates (can be null)
* @param texCoordIdx
* the texture coordinate indices (can be null)
* @param colorRef
* the reference vertex colors (can be null)
* @param colorIdx
* the vertex color 4f indices (can be null)
* @param normalRef
* the reference normals (can be null)
* @param normalIdx
* the normal indices (can be null)
* @param appearance
* the appearance (can be null)
*/
public static Shape3D createIndexedQuadArray( float[ ] coordRef , int[ ] coordIdx , float[ ] texCoordRef , int[ ] texCoordIdx , float[ ] colorRef , int[ ] colorIdx , float[ ] normalRef , int[ ] normalIdx , Appearance appearance )
{
final boolean useTexcoords = texCoordIdx != null && texCoordIdx.length != 0;
final boolean useColors = colorIdx != null && colorIdx.length != 0;
final boolean useNormals = normalIdx != null && normalIdx.length != 0;
if( useTexcoords && texCoordIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if texCoordIdx is not null or empty it must be the same length as coordIdx" + " (texCoordIdx.length = " + texCoordIdx.length + ", coordIdx.length = " + coordIdx.length );
}
if( useColors && colorIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if colorIdx is not null or empty it must be the same length as coordIdx" + " (colorIdx.length = " + colorIdx.length + ", coordIdx.length = " + coordIdx.length );
}
if( useNormals && normalIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if normalIdx is not null or empty it must be the same length as coordIdx" + " (normalIdx.length = " + normalIdx.length + ", coordIdx.length = " + coordIdx.length );
}
int vertexFormat = GeometryArray.COORDINATES | GeometryArray.BY_REFERENCE;
if( useTexcoords )
{
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
}
if( useColors )
{
vertexFormat |= GeometryArray.COLOR_4;
}
if( useNormals )
{
vertexFormat |= GeometryArray.NORMALS;
}
int refCount = coordRef.length / 3;
if( useTexcoords )
{
refCount = Math.max( refCount , texCoordRef.length / 2 );
}
if( useColors )
{
refCount = Math.max( refCount , colorRef.length / 4 );
}
if( useNormals )
{
refCount = Math.max( refCount , normalRef.length / 3 );
}
final IndexedQuadArray geom = new IndexedQuadArray( refCount , vertexFormat , coordIdx.length );
// if there are more texcoords, colors, or normals than coordinates we
// need to resize the coordinates
// array so that there isn't an IndexOutOfBoundsException
if( coordRef.length / 3 < refCount )
{
final float[ ] newArray = new float[ refCount * 3 ];
System.arraycopy( coordRef , 0 , newArray , 0 , coordRef.length );
// duplicate the last coordinate so that the bounding box doesn't
// get screwed up
final float x = coordRef[ coordRef.length - 3 ];
final float y = coordRef[ coordRef.length - 2 ];
final float z = coordRef[ coordRef.length - 1 ];
for( int i = coordRef.length ; i < refCount * 3 ; i += 3 )
{
newArray[ i ] = x;
newArray[ i + 1 ] = y;
newArray[ i + 2 ] = z;
}
coordRef = newArray;
}
geom.setCoordRefFloat( coordRef );
if( useTexcoords )
{
geom.setTexCoordRefFloat( 0 , texCoordRef );
}
if( useColors )
{
geom.setColorRefFloat( colorRef );
}
if( useNormals )
{
geom.setNormalRefFloat( normalRef );
}
geom.setCoordinateIndices( 0 , coordIdx );
if( useTexcoords )
{
geom.setTextureCoordinateIndices( 0 , 0 , texCoordIdx );
}
if( useColors )
{
geom.setColorIndices( 0 , colorIdx );
}
if( useNormals )
{
geom.setNormalIndices( 0 , normalIdx );
}
final Shape3D shape = new Shape3D( geom );
if( appearance != null )
{
shape.setAppearance( appearance );
}
return shape;
}
/**
* Creates a {@link Shape3D} backed by an {@link IndexedLineArray}.
*
* @param coordRef
* the reference coordinates
* @param coordIdx
* the coordinate indices
* @param texCoordRef
* the reference 2d texture coordinates (can be null)
* @param texCoordIdx
* the texture coordinate indices (can be null)
* @param colorRef
* the reference vertex colors (can be null)
* @param colorIdx
* the vertex color 4f indices (can be null)
* @param normalRef
* the reference normals (can be null)
* @param normalIdx
* the normal indices (can be null)
* @param appearance
* the appearance (can be null)
*/
public static Shape3D createIndexedLineArray( float[ ] coordRef , int[ ] coordIdx , float[ ] texCoordRef , int[ ] texCoordIdx , float[ ] colorRef , int[ ] colorIdx , float[ ] normalRef , int[ ] normalIdx , Appearance appearance )
{
final boolean useTexcoords = texCoordIdx != null && texCoordIdx.length != 0;
final boolean useColors = colorIdx != null && colorIdx.length != 0;
final boolean useNormals = normalIdx != null && normalIdx.length != 0;
if( useTexcoords && texCoordIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if texCoordIdx is not null or empty it must be the same length as coordIdx" + " (texCoordIdx.length = " + texCoordIdx.length + ", coordIdx.length = " + coordIdx.length );
}
if( useColors && colorIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if colorIdx is not null or empty it must be the same length as coordIdx" + " (colorIdx.length = " + colorIdx.length + ", coordIdx.length = " + coordIdx.length );
}
if( useNormals && normalIdx.length != coordIdx.length )
{
throw new IllegalArgumentException( "if normalIdx is not null or empty it must be the same length as coordIdx" + " (normalIdx.length = " + normalIdx.length + ", coordIdx.length = " + coordIdx.length );
}
int vertexFormat = GeometryArray.COORDINATES | GeometryArray.BY_REFERENCE;
if( useTexcoords )
{
vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
}
if( useColors )
{
vertexFormat |= GeometryArray.COLOR_4;
}
if( useNormals )
{
vertexFormat |= GeometryArray.NORMALS;
}
int refCount = coordRef.length / 3;
if( useTexcoords )
{
refCount = Math.max( refCount , texCoordRef.length / 2 );
}
if( useColors )
{
refCount = Math.max( refCount , colorRef.length / 4 );
}
if( useNormals )
{
refCount = Math.max( refCount , normalRef.length / 3 );
}
final IndexedLineArray geom = new IndexedLineArray( refCount , vertexFormat , coordIdx.length );
// if there are more texcoords, colors, or normals than coordinates we
// need to resize the coordinates
// array so that there isn't an IndexOutOfBoundsException
if( coordRef.length / 3 < refCount )
{
final float[ ] newArray = new float[ refCount * 3 ];
System.arraycopy( coordRef , 0 , newArray , 0 , coordRef.length );
// duplicate the last coordinate so that the bounding box doesn't
// get screwed up
final float x = coordRef[ coordRef.length - 3 ];
final float y = coordRef[ coordRef.length - 2 ];
final float z = coordRef[ coordRef.length - 1 ];
for( int i = coordRef.length ; i < refCount * 3 ; i += 3 )
{
newArray[ i ] = x;
newArray[ i + 1 ] = y;
newArray[ i + 2 ] = z;
}
coordRef = newArray;
}
geom.setCoordRefFloat( coordRef );
if( useTexcoords )
{
geom.setTexCoordRefFloat( 0 , texCoordRef );
}
if( useColors )
{
geom.setColorRefFloat( colorRef );
}
if( useNormals )
{
geom.setNormalRefFloat( normalRef );
}
geom.setCoordinateIndices( 0 , coordIdx );
if( useTexcoords )
{
geom.setTextureCoordinateIndices( 0 , 0 , texCoordIdx );
}
if( useColors )
{
geom.setColorIndices( 0 , colorIdx );
}
if( useNormals )
{
geom.setNormalIndices( 0 , normalIdx );
}
final Shape3D shape = new Shape3D( geom );
if( appearance != null )
{
shape.setAppearance( appearance );
}
return shape;
}
public static GeometryArray changeVertexFormat( GeometryArray array , int newVertexFormat )
{
int unionFormatMask = newVertexFormat;
int diffFormatMask = array.getVertexFormat( ) & ~newVertexFormat;
return changeVertexFormat( array , unionFormatMask , diffFormatMask );
}
/**
* Creates a copy of a GeometryArray with a different vertex format, and copies all the data it can into the new array. GeometryStripArrays,
* IndexedGeometryStripArrays, and INTERLEAVED and USE_NIO_BUFFER GeometryArrays are not supported, nor are GeometryArrays using referenced TupleXX data.
*
* @param array
* the array to copy from.
* @param unionFormatMask
* bits that will be added to the original vertex format
* @param diffFormatMask
* bits that will be removed from the original vertex format (trumps unionFormatMask)
* @return a <code>GeometryArray</code> of the same type as <code>array</code>. If the vertex format is not changed, the original array is returned.
*/
public static GeometryArray changeVertexFormat( GeometryArray array , int unionFormatMask , int diffFormatMask )
{
if( array instanceof GeometryStripArray || array instanceof IndexedGeometryStripArray )
{
throw new IllegalArgumentException( "GeometryStripArrays are not currently supported" );
}
IndexedGeometryArray indexedArray = null;
final boolean indexed = array instanceof IndexedGeometryArray;
if( indexed )
{
indexedArray = ( IndexedGeometryArray ) array;
}
final int vertexCount = array.getVertexCount( );
final int vertexFormat = array.getVertexFormat( );
final int texCoordSetCount = array.getTexCoordSetCount( );
final int newVertexFormat = ( vertexFormat | unionFormatMask ) & ~diffFormatMask;
if( ( vertexFormat & GeometryArray.INTERLEAVED ) != 0 || ( newVertexFormat & GeometryArray.INTERLEAVED ) != 0 )
{
throw new IllegalArgumentException( "Interleaved GeometryArrays are not currently supported" );
}
if( ( vertexFormat & GeometryArray.USE_NIO_BUFFER ) != 0 || ( newVertexFormat & GeometryArray.USE_NIO_BUFFER ) != 0 )
{
throw new IllegalArgumentException( "NIO Buffer GeometryArrays are not currently supported" );
}
if( vertexFormat == newVertexFormat )
{
return array;
}
int indexCount = -1;
if( indexed )
{
indexCount = indexedArray.getIndexCount( );
}
GeometryArray newArray = null;
IndexedGeometryArray newIdxArray = null;
// Allocate the new array
if( array instanceof LineArray )
{
newArray = new LineArray( vertexCount , newVertexFormat );
}
else if( array instanceof PointArray )
{
newArray = new PointArray( vertexCount , newVertexFormat );
}
else if( array instanceof QuadArray )
{
newArray = new QuadArray( vertexCount , newVertexFormat );
}
else if( array instanceof TriangleArray )
{
newArray = new TriangleArray( vertexCount , newVertexFormat );
}
else if( array instanceof IndexedLineArray )
{
newArray = newIdxArray = new IndexedLineArray( vertexCount , newVertexFormat , indexCount );
}
else if( array instanceof IndexedPointArray )
{
newArray = newIdxArray = new IndexedPointArray( vertexCount , newVertexFormat , indexCount );
}
else if( array instanceof IndexedQuadArray )
{
newArray = newIdxArray = new IndexedQuadArray( vertexCount , newVertexFormat , indexCount );
}
else if( array instanceof IndexedTriangleArray )
{
newArray = newIdxArray = new IndexedTriangleArray( vertexCount , newVertexFormat , indexCount );
}
// handy variables
final boolean from_by_reference = ( vertexFormat & GeometryArray.BY_REFERENCE ) != 0;
final boolean from_by_reference_indices = ( vertexFormat & GeometryArray.BY_REFERENCE_INDICES ) != 0;
final boolean from_use_coord_index_only = ( vertexFormat & GeometryArray.USE_COORD_INDEX_ONLY ) != 0;
final boolean from_coordinates = ( vertexFormat & GeometryArray.COORDINATES ) != 0;
final boolean from_color_3 = ( vertexFormat & GeometryArray.COLOR_3 ) != 0;
final boolean from_color_4 = ( vertexFormat & GeometryArray.COLOR_4 ) != 0;
final boolean from_texture_coordinate_2 = ( vertexFormat & GeometryArray.TEXTURE_COORDINATE_2 ) != 0;
final boolean from_texture_coordinate_3 = ( vertexFormat & GeometryArray.TEXTURE_COORDINATE_3 ) != 0;
final boolean from_texture_coordinate_4 = ( vertexFormat & GeometryArray.TEXTURE_COORDINATE_4 ) != 0;
final boolean from_normals = ( vertexFormat & GeometryArray.NORMALS ) != 0;
final boolean to_by_reference = ( newVertexFormat & GeometryArray.BY_REFERENCE ) != 0;
final boolean to_by_reference_indices = ( newVertexFormat & GeometryArray.BY_REFERENCE_INDICES ) != 0;
final boolean to_use_coord_index_only = ( newVertexFormat & GeometryArray.USE_COORD_INDEX_ONLY ) != 0;
final boolean to_coordinates = ( newVertexFormat & GeometryArray.COORDINATES ) != 0;
final boolean to_color_3 = ( newVertexFormat & GeometryArray.COLOR_3 ) != 0;
final boolean to_color_4 = ( newVertexFormat & GeometryArray.COLOR_4 ) != 0;
final boolean to_texture_coordinate_2 = ( newVertexFormat & GeometryArray.TEXTURE_COORDINATE_2 ) != 0;
final boolean to_texture_coordinate_3 = ( newVertexFormat & GeometryArray.TEXTURE_COORDINATE_3 ) != 0;
final boolean to_texture_coordinate_4 = ( newVertexFormat & GeometryArray.TEXTURE_COORDINATE_4 ) != 0;
final boolean to_normals = ( newVertexFormat & GeometryArray.NORMALS ) != 0;
// copy coordinates
if( from_coordinates && to_coordinates )
{
float[ ] coordsFloat = null;
double[ ] coordsDouble = null;
if( from_by_reference )
{
coordsFloat = array.getCoordRefFloat( );
coordsDouble = array.getCoordRefDouble( );
}
else
{
coordsDouble = new double[ vertexCount * 3 ];
array.getCoordinates( 0 , coordsDouble );
}
if( to_by_reference )
{
if( coordsFloat != null )
{
newArray.setCoordRefFloat( coordsFloat );
}
else if( coordsDouble != null )
{
newArray.setCoordRefDouble( coordsDouble );
}
}
else
{
if( coordsFloat != null )
{
newArray.setCoordinates( 0 , coordsFloat );
}
else if( coordsDouble != null )
{
newArray.setCoordinates( 0 , coordsDouble );
}
}
if( indexed )
{
int[ ] coordIdx = null;
if( from_by_reference_indices )
{
coordIdx = indexedArray.getCoordIndicesRef( );
}
else
{
coordIdx = new int[ indexCount ];
indexedArray.getCoordinateIndices( 0 , coordIdx );
}
if( to_by_reference_indices )
{
newIdxArray.setCoordIndicesRef( coordIdx );
}
else
{
newIdxArray.setCoordinateIndices( 0 , coordIdx );
}
}
}
// copy colors
if( ( from_color_3 || from_color_4 ) && ( to_color_3 || to_color_4 ) )
{
final int fromSize = from_color_3 ? 3 : 4;
final int toSize = to_color_3 ? 3 : 4;
float[ ] colorsFloat = null;
byte[ ] colorsByte = null;
if( from_by_reference )
{
colorsFloat = array.getColorRefFloat( );
colorsByte = array.getColorRefByte( );
}
else
{
colorsFloat = new float[ vertexCount * fromSize ];
array.getColors( 0 , colorsFloat );
}
if( fromSize != toSize )
{
if( colorsFloat != null )
{
colorsFloat = ArrayUtils.changeBlockSize( colorsFloat , fromSize , toSize );
}
else if( colorsByte != null )
{
colorsByte = ArrayUtils.changeBlockSize( colorsByte , fromSize , toSize );
}
}
if( to_by_reference )
{
if( colorsFloat != null )
{
newArray.setColorRefFloat( colorsFloat );
}
else if( colorsByte != null )
{
newArray.setColorRefByte( colorsByte );
}
}
else
{
if( colorsFloat != null )
{
newArray.setColors( 0 , colorsFloat );
}
else if( colorsByte != null )
{
newArray.setColors( 0 , colorsByte );
}
}
if( indexed && !to_use_coord_index_only )
{
int[ ] colorIdx = null;
if( from_by_reference_indices && from_use_coord_index_only )
{
colorIdx = indexedArray.getCoordIndicesRef( );
}
else
{
colorIdx = new int[ indexCount ];
indexedArray.getColorIndices( 0 , colorIdx );
}
newIdxArray.setColorIndices( 0 , colorIdx );
}
}
// copy texture coordinates
if( ( from_texture_coordinate_2 || from_texture_coordinate_3 || from_texture_coordinate_4 ) && ( to_texture_coordinate_2 || to_texture_coordinate_3 || to_texture_coordinate_4 ) )
{
final int fromSize = from_texture_coordinate_2 ? 2 : from_texture_coordinate_3 ? 3 : 4;
final int toSize = to_texture_coordinate_2 ? 2 : to_texture_coordinate_3 ? 3 : 4;
for( int i = 0 ; i < texCoordSetCount ; i++ )
{
float[ ] texturesFloat = null;
if( from_by_reference )
{
texturesFloat = array.getTexCoordRefFloat( i );
}
else
{
texturesFloat = new float[ vertexCount * fromSize ];
array.getTextureCoordinates( i , 0 , texturesFloat );
}
if( fromSize != toSize )
{
texturesFloat = ArrayUtils.changeBlockSize( texturesFloat , fromSize , toSize );
}
if( to_by_reference )
{
newArray.setTexCoordRefFloat( i , texturesFloat );
}
else
{
newArray.setTextureCoordinates( i , 0 , texturesFloat );
}
if( indexed && !to_use_coord_index_only )
{
int[ ] textureIdx = null;
if( from_by_reference_indices && from_use_coord_index_only )
{
textureIdx = indexedArray.getCoordIndicesRef( );
}
else
{
textureIdx = new int[ indexCount ];
indexedArray.getTextureCoordinateIndices( i , 0 , textureIdx );
}
newIdxArray.setTextureCoordinateIndices( i , 0 , textureIdx );
}
}
}
// copy normals
if( from_normals && to_normals )
{
float[ ] normalsFloat = null;
if( from_by_reference )
{
normalsFloat = array.getNormalRefFloat( );
}
else
{
normalsFloat = new float[ vertexCount * 3 ];
array.getNormals( 0 , normalsFloat );
}
if( to_by_reference )
{
newArray.setNormalRefFloat( normalsFloat );
}
else
{
newArray.setNormals( 0 , normalsFloat );
}
if( indexed && !to_use_coord_index_only )
{
int[ ] normalIdx = null;
if( from_by_reference_indices && from_use_coord_index_only )
{
normalIdx = indexedArray.getCoordIndicesRef( );
}
else
{
normalIdx = new int[ indexCount ];
indexedArray.getNormalIndices( 0 , normalIdx );
}
newIdxArray.setNormalIndices( 0 , normalIdx );
}
}
// finally, we're done!
return newArray;
}
public static Color4f toColor4f( Color color , Color4f result )
{
result.x = Math.max( Math.min( color.getRed( ) / 255f , 1f ) , 0f );
result.y = Math.max( Math.min( color.getGreen( ) / 255f , 1f ) , 0f );
result.z = Math.max( Math.min( color.getBlue( ) / 255f , 1f ) , 0f );
result.w = Math.max( Math.min( color.getAlpha( ) / 255f , 1f ) , 0f );
return result;
}
/**
* Finds the first node in a depth-first traversal of the scene graph whose name (all or part) matches a regular expression.
*
* @param start
* the starting node for the scene graph traversal.
* @param nameRegexp
* a regular expression
* @return a matching {@link Node}, or <code>null</code> if none is found.
*/
public static Node findNodeByName( Node start , String nameRegexp )
{
final Pattern p = Pattern.compile( nameRegexp );
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
if( node.getName( ) == null )
{
continue;
}
final Matcher m = p.matcher( node.getName( ) );
if( m.find( ) )
{
return node;
}
}
return null;
}
/**
* Finds the first node in a depth-first traversal of the scene graph whose {@link Node#toString()} representation (all or part) matches a regular
* expression.
*
* @param start
* the starting node for the scene graph traversal.
* @param regexp
* a regular expression
* @return a matching {@link Node}, or <code>null</code> if none is found.
*/
public static Node findNodeByString( Node start , String regexp )
{
final Pattern p = Pattern.compile( regexp );
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
final Matcher m = p.matcher( node.toString( ) );
if( m.find( ) )
{
return node;
}
}
return null;
}
/**
* Finds the first node in a depth-first traversal of the scene graph of a given type.
*
* @param start
* the starting node for the scene graph traversal.
* @param clazz
* the type to find
* @return a matching {@link Node}, or <code>null</code> if none is found.
*/
public static Node findNodeByClass( Node start , Class<?> clazz )
{
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
if( node.getClass( ).equals( clazz ) )
{
return node;
}
}
return null;
}
/**
* Finds the first node in a depth-first traversal of the scene graph with the given user data.
*
* @param start
* the starting node for the scene graph traversal.
* @param userData
* the userData to find
* @return a {@link Node} whose user data <code>== userData</code>, or <code>null</code> if none is found.
*/
public static Node findNodeByUserData( Node start , Object userData )
{
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
if( node.getUserData( ) == userData )
{
return node;
}
}
return null;
}
/**
* Finds the first node in a depth-first traversal of the scene graph with the given user data.
*
* @param start
* the starting node for the scene graph traversal.
* @param userData
* the userData to find
* @return a {@link Node} whose user data <code>.equals(userData)</code>, or <code>null</code> if none is found.
*/
public static Node findNodeByEqualUserData( Node start , Object userData )
{
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
final Object nodeUserData = node.getUserData( );
if( nodeUserData == userData || ( nodeUserData != null && userData != null && nodeUserData.equals( userData ) ) )
{
return node;
}
}
return null;
}
/**
* Finds the first node in a depth-first traversal of the scene graph matching an arbitrary predicate.
*
* @param start
* the starting node for the scene graph traversal.
* @param p
* the predicate
* @return a {@link Node} for which the predicate is <code>true</code>, or <code>null</code> if none is found.
*/
public static Node findNodeByPredicate( Node start , Predicate<Node, Boolean> p )
{
for( final Node node : SceneGraphIterator.unboundedIterable( start ) )
{
if( p.eval( node ) )
{
return node;
}
}
return null;
}
public static BranchGroup createDefaultBranchGroup( )
{
final BranchGroup result = new BranchGroup( );
result.setCapability( BranchGroup.ALLOW_DETACH );
result.setCapability( Group.ALLOW_CHILDREN_READ );
result.setCapability( Group.ALLOW_CHILDREN_WRITE );
result.setCapability( Group.ALLOW_CHILDREN_EXTEND );
return result;
}
/**
* Tries to remove a {@link Node} from the scene graph. If the necessary capabilities are not set, the node's top-level parent will be removed from its
* {@link Locale}, allowing the node to be detached, and then the top-level parent will be added back to the locale.
*
* @param n
* the node to detach
* @return <code>true</code> if the node was successfully detached.
*/
public static boolean forceDetach( Node n )
{
if( !n.isLive( ) )
{
return true;
}
final Node parent = n.getParent( );
try
{
if( parent != null && parent instanceof Group )
{
( ( Group ) parent ).removeChild( n );
return true;
}
}
catch( final Throwable t )
{
}
final Locale l = n.getLocale( );
if( l == null )
{
return false;
}
Node topParent = n;
while( topParent.getParent( ) != null )
{
topParent = topParent.getParent( );
}
if( topParent instanceof BranchGroup )
{
try
{
l.removeBranchGraph( ( BranchGroup ) topParent );
if( topParent != n )
{
( ( Group ) parent ).removeChild( n );
}
return true;
}
finally
{
if( topParent != n )
{
l.addBranchGraph( ( BranchGroup ) topParent );
}
}
}
return false;
}
/**
* Tries to add a {@link Node} to the scene graph. If the necessary capabilities are not set, the node's top-level parent will be removed from its
* {@link Locale}, allowing the node to be attached, and then the top-level parent will be added back to the locale.
*
* @param child
* the node to attach
* @param newParent
* the parent to attach <code>child</code> to
* @return <code>true</code> if the node was successfully attached.
*/
public static boolean forceAttach( Node child , Group newParent )
{
try
{
newParent.addChild( child );
return true;
}
catch( final Throwable t )
{
}
final Locale l = newParent.getLocale( );
if( l == null )
{
return false;
}
Node topParent = newParent;
while( topParent.getParent( ) != null )
{
topParent = topParent.getParent( );
}
if( topParent instanceof BranchGroup )
{
try
{
l.removeBranchGraph( ( BranchGroup ) topParent );
newParent.addChild( child );
return true;
}
finally
{
l.addBranchGraph( ( BranchGroup ) topParent );
}
}
return false;
}
/**
* Forcefully detaches a node from the scene graph, modifies it, and then forcefully reattaches the node.
*
* @param node
* the node to modify
* @param visitor
* the modifying procedure
* @see #forceDetach(Node)
* @see #forceAttach(Node, Group)
*/
public static void forceModify( Node node , Visitor<Node, Boolean> visitor )
{
Node parent = node.getParent( );
if( parent != null && !( parent instanceof Group ) )
{
parent = null;
}
try
{
if( parent != null && !J3DUtils.forceDetach( node ) )
{
return;
}
visitor.visit( node );
}
catch( final Throwable t )
{
t.printStackTrace( );
}
finally
{
if( parent != null )
{
try
{
J3DUtils.forceAttach( node , ( Group ) parent );
}
catch( final Throwable t )
{
t.printStackTrace( );
}
}
}
}
/**
* Forcefully modifies a list of nodes.
*
* @param node
* the node to modify
* @param visitor
* the modifying procedure. If <code>visitor.visit()</code> returns <code>false</code> for a node, the rest of the list is skipped.
* @see #forceModify(Node, Visitor)
*/
public static void forceModify( List<Node> nodes , Visitor<Node, Boolean> visitor )
{
for( final Node node : nodes )
{
Node parent = node.getParent( );
Locale locale = node.getLocale( );
boolean wasLive = node.isLive( );
if( parent != null && !( parent instanceof Group ) )
{
parent = null;
}
try
{
if( !J3DUtils.forceDetach( node ) )
{
continue;
}
if( !visitor.visit( node ) )
{
break;
}
}
catch( final Throwable t )
{
t.printStackTrace( );
}
finally
{
if( parent != null )
{
try
{
J3DUtils.forceAttach( node , ( Group ) parent );
}
catch( final Throwable t )
{
t.printStackTrace( );
}
}
else if( node instanceof BranchGroup && wasLive )
{
try
{
locale.addBranchGraph( ( BranchGroup ) node );
}
catch( final Throwable t )
{
t.printStackTrace( );
}
}
}
}
}
static Appearance getAppearance( Shape3D s3D )
{
Appearance app = s3D.getAppearance( );
if( app == null )
{
s3D.setAppearance( app = new Appearance( ) );
}
return app;
}
public static Appearance maskAppearance( )
{
final Appearance app = new Appearance( );
final TransparencyAttributes ta = new TransparencyAttributes( );
ta.setTransparencyMode( TransparencyAttributes.BLENDED );
ta.setSrcBlendFunction( TransparencyAttributes.BLEND_ZERO );
ta.setDstBlendFunction( TransparencyAttributes.BLEND_ONE );
app.setTransparencyAttributes( ta );
final ColoringAttributes ca = new ColoringAttributes( );
ca.setColor( new Color3f( 0 , 0 , 0 ) );
app.setColoringAttributes( ca );
final RenderingAttributes ra = new RenderingAttributes( );
ra.setDepthBufferWriteEnable( true );
app.setRenderingAttributes( ra );
return app;
}
public static Appearance flatAppearance( Color4f color )
{
final Appearance result = new Appearance( );
setColor( result , color , false );
return result;
}
public static Appearance shinyAppearance( Color4f color , float shininess )
{
final Appearance result = new Appearance( );
final Material mat = new Material( );
mat.setLightingEnable( true );
mat.setShininess( shininess );
result.setMaterial( mat );
setColor( result , color , true );
return result;
}
public static void makeDoubleSided( Appearance app )
{
PolygonAttributes pa = app.getPolygonAttributes( );
if( pa == null )
{
pa = new PolygonAttributes( );
app.setPolygonAttributes( pa );
}
pa.setCullFace( PolygonAttributes.CULL_NONE );
pa.setBackFaceNormalFlip( true );
}
public static void setDepthTestFunction( Appearance app , int function )
{
RenderingAttributes ra = app.getRenderingAttributes( );
if( ra == null )
{
ra = new RenderingAttributes( );
app.setRenderingAttributes( ra );
}
ra.setDepthTestFunction( function );
}
public static Appearance debugAppearance( Color4f color )
{
final Appearance result = new Appearance( );
setColor( result , color , false );
setDepthTestFunction( result , RenderingAttributes.ALWAYS );
return result;
}
public static BranchGroup visualizeBounds( Bounds b , Transform3D xform , Appearance app )
{
final BranchGroup bg = new BranchGroup( );
bg.setCapability( BranchGroup.ALLOW_DETACH );
final TransformGroup tg = new TransformGroup( );
final Transform3D xlate = new Transform3D( );
if( b instanceof BoundingBox )
{
final BoundingBox bbox = ( BoundingBox ) b;
final Point3d lower = new Point3d( ) , upper = new Point3d( );
bbox.getLower( lower );
bbox.getUpper( upper );
xlate.set( new double[ ] { ( upper.x - lower.x ) * 0.5 , 0 , 0 , ( upper.x + lower.x ) * 0.5 , 0 , ( upper.y - lower.y ) * 0.5 , 0 , ( upper.y + lower.y ) * 0.5 , 0 , 0 , ( upper.z - lower.z ) * 0.5 , ( upper.z + lower.z ) * 0.5 , 0 , 0 , 0 , 1 } );
if( xform != null )
{
xlate.mul( xform , xlate );
}
tg.setTransform( xlate );
bg.addChild( tg );
final com.sun.j3d.utils.geometry.Box vbox = new com.sun.j3d.utils.geometry.Box( );
vbox.setAppearance( app );
tg.addChild( vbox );
}
else if( b instanceof BoundingSphere )
{
final BoundingSphere bsphere = ( BoundingSphere ) b;
final Point3d center = new Point3d( );
bsphere.getCenter( center );
final double radius = bsphere.getRadius( );
xlate.setTranslation( new Vector3d( center ) );
if( xform != null )
{
xlate.mul( xform , xlate );
}
tg.setTransform( xlate );
bg.addChild( tg );
final com.sun.j3d.utils.geometry.Sphere vsphere = new com.sun.j3d.utils.geometry.Sphere( ( float ) radius );
vsphere.setAppearance( app );
tg.addChild( vsphere );
}
return bg;
}
/**
* Copies the coordinates from a GeometryArray into a Point3f array, even if they are stored in a different form in the GeometryArray.
*
* @param array
* @return
*/
public static Point3f[ ] copyCoordinates3f( GeometryArray array )
{
Point3f[ ] coordinates = null;
final int format = array.getVertexFormat( );
if( ( format & GeometryArray.BY_REFERENCE ) > 0 )
{
final Point3f[ ] ref3f = array.getCoordRef3f( );
final Point3d[ ] ref3d = array.getCoordRef3d( );
final double[ ] refDouble = array.getCoordRefDouble( );
final float[ ] refFloat = array.getCoordRefFloat( );
if( ref3f != null || ref3d != null || refDouble != null || refFloat != null )
{
coordinates = new Point3f[ array.getVertexCount( ) ];
if( ref3f != null )
{
for( int i = 0 ; i < ref3d.length ; i++ )
{
coordinates[ i ] = new Point3f( ref3f[ i ] );
}
}
if( ref3d != null )
{
for( int i = 0 ; i < ref3d.length ; i++ )
{
coordinates[ i ] = new Point3f( ref3d[ i ] );
}
}
else if( refDouble != null )
{
int k = 0;
for( int i = 0 ; i < refDouble.length ; i += 3 )
{
coordinates[ k++ ] = new Point3f( ( float ) refDouble[ i ] , ( float ) refDouble[ i + 1 ] , ( float ) refDouble[ i + 2 ] );
}
}
else if( refFloat != null )
{
int k = 0;
for( int i = 0 ; i < refFloat.length ; i += 3 )
{
coordinates[ k++ ] = new Point3f( refFloat[ i ] , refFloat[ i + 1 ] , refFloat[ i + 2 ] );
}
}
}
}
else
{
coordinates = org.andork.vecmath.VecmathUtils.allocPoint3fArray( array.getVertexCount( ) );
array.getCoordinates( 0 , coordinates );
}
return coordinates;
}
/**
* Copies the normals from a GeometryArray into a Vector3f array, even if they're stored in a different form in the GeometryArray.
*
* @param array
* @return
*/
public static Vector3f[ ] copyNormals3f( GeometryArray array )
{
Vector3f[ ] normals = null;
final int format = array.getVertexFormat( );
if( ( format & GeometryArray.BY_REFERENCE ) > 0 )
{
final Vector3f[ ] ref3f = array.getNormalRef3f( );
final float[ ] refFloat = array.getNormalRefFloat( );
if( ref3f != null || refFloat != null )
{
normals = new Vector3f[ array.getVertexCount( ) ];
if( ref3f != null )
{
for( int i = 0 ; i < refFloat.length ; i += 3 )
{
normals[ i ] = new Vector3f( ref3f[ i ] );
}
}
else if( refFloat != null )
{
int k = 0;
for( int i = 0 ; i < refFloat.length ; i += 3 )
{
normals[ k++ ] = new Vector3f( refFloat[ i ] , refFloat[ i + 1 ] , refFloat[ i + 2 ] );
}
}
}
}
else
{
normals = org.andork.vecmath.VecmathUtils.allocVector3fArray( array.getVertexCount( ) );
array.getNormals( 0 , normals );
}
return normals;
}
/**
* Copies the coordinate indices out of an IndexedGeometryArray, whether they're stored by reference or not.
*
* @param array
* @return
*/
public static int[ ] copyCoordinateIndices( IndexedGeometryArray array )
{
int[ ] coordinateIndices = null;
final int format = array.getVertexFormat( );
if( ( format & GeometryArray.BY_REFERENCE_INDICES ) > 0 )
{
coordinateIndices = array.getCoordIndicesRef( ).clone( );
}
else
{
coordinateIndices = new int[ array.getIndexCount( ) ];
array.getCoordinateIndices( 0 , coordinateIndices );
}
return coordinateIndices;
}
/**
* Copies the normal indices out of an IndexedGeometryArray, whether they're stored by reference or not.
*
* @param array
* @return
*/
public static int[ ] copyNormalIndices( IndexedGeometryArray array )
{
int[ ] normalIndices = null;
final int format = array.getVertexFormat( );
if( ( format & GeometryArray.BY_REFERENCE_INDICES ) > 0 && ( format & GeometryArray.USE_COORD_INDEX_ONLY ) > 0 )
{
normalIndices = array.getCoordIndicesRef( ).clone( );
}
else
{
normalIndices = new int[ array.getIndexCount( ) ];
array.getNormalIndices( 0 , normalIndices );
}
return normalIndices;
}
public static LineArray renderNormals( IndexedGeometryArray array , float scale )
{
final Point3f[ ] coordinates = copyCoordinates3f( array );
final Vector3f[ ] normals = copyNormals3f( array );
final int[ ] coordinateIndices = copyCoordinateIndices( array );
final int[ ] normalIndices = copyNormalIndices( array );
if( coordinates == null || normals == null || coordinateIndices == null || normalIndices == null )
{
throw new IllegalArgumentException( "Unable to get necessary information from the geometry" );
}
final int vertexCount = coordinateIndices.length * 2;
final Point3f temp = new Point3f( );
final LineArray result = new LineArray( vertexCount , GeometryArray.COORDINATES );
int k = 0;
for( int i = 0 ; i < coordinateIndices.length ; i++ )
{
final Point3f coordinate = coordinates[ coordinateIndices[ i ] ];
final Vector3f normal = normals[ normalIndices[ i ] ];
result.setCoordinate( k++ , coordinate );
temp.scaleAdd( scale , normal , coordinate );
result.setCoordinate( k++ , temp );
}
return result;
}
public static LineArray renderNormals( GeometryArray array , float scale )
{
final Point3f[ ] coordinates = copyCoordinates3f( array );
final Vector3f[ ] normals = copyNormals3f( array );
if( coordinates == null || normals == null )
{
throw new IllegalArgumentException( "Unable to get necessary information from the geometry" );
}
final int vertexCount = coordinates.length * 2;
final Point3f temp = new Point3f( );
final LineArray result = new LineArray( vertexCount , GeometryArray.COORDINATES );
int k = 0;
for( int i = 0 ; i < coordinates.length ; i++ )
{
final Point3f coordinate = coordinates[ i ];
final Vector3f normal = normals[ i ];
result.setCoordinate( k++ , coordinate );
temp.scaleAdd( scale , normal , coordinate );
result.setCoordinate( k++ , temp );
}
return result;
}
/**
* Transforms the coordinates in a geometry array. DOES NOT TRANSFORM THE NORMALS
*
* @param geomArray
* @param xform
*/
public static void transformCoordinates( GeometryArray geomArray , Transform3D xform )
{
final Point3f temp = new Point3f( );
for( int i = 0 ; i < geomArray.getVertexCount( ) ; i++ )
{
geomArray.getCoordinate( i , temp );
xform.transform( temp );
geomArray.setCoordinate( i , temp );
}
}
public static Shape3D visualizeNormals( Shape3D shape , float scale , boolean local , Appearance app )
{
final GeometryArray geomArray = ( GeometryArray ) shape.getGeometry( );
LineArray rendered;
if( geomArray instanceof IndexedGeometryArray )
{
final IndexedGeometryArray indexedGeomArray = ( IndexedGeometryArray ) geomArray;
rendered = renderNormals( indexedGeomArray , scale );
}
else
{
rendered = renderNormals( geomArray , scale );
}
if( !local )
{
final Transform3D localToVworld = new Transform3D( );
shape.getLocalToVworld( localToVworld );
transformCoordinates( rendered , localToVworld );
}
return new Shape3D( rendered , app );
}
public static Shape3D visualizeNormals( Shape3D shape , float scale , Appearance app )
{
return visualizeNormals( shape , scale , true , app );
}
public static Shape3D visualizeFronts( Shape3D shape , float scale , boolean local , Appearance app )
{
LineArray rendered = renderFronts( shape , scale );
if( rendered == null )
{
return null;
}
if( local )
{
final Transform3D localToVworld = new Transform3D( );
shape.getLocalToVworld( localToVworld );
transformCoordinates( rendered , localToVworld );
}
return new Shape3D( rendered , app );
}
public static LineArray renderFronts( Shape3D shape , float scale )
{
Geometry geom = shape.getGeometry( );
if( geom instanceof IndexedGeometryArray )
{
return renderFronts( ( IndexedGeometryArray ) geom , scale );
}
else if( geom instanceof GeometryArray )
{
return renderFronts( ( GeometryArray ) geom , scale );
}
return null;
}
public static LineArray renderFronts( IndexedGeometryArray geom , float scale )
{
List<Point3f> points = new ArrayList<Point3f>( );
Point3f[ ] coordinates = copyCoordinates3f( geom );
int[ ] indices = copyCoordinateIndices( geom );
Point3f t = new Point3f( );
Vector3f v1 = new Vector3f( );
Vector3f v2 = new Vector3f( );
Vector3f n = new Vector3f( );
int k;
for( k = 0 ; k < indices.length ; k += 3 )
{
Point3f a = coordinates[ indices[ k ] ];
Point3f b = coordinates[ indices[ k + 1 ] ];
Point3f c = coordinates[ indices[ k + 2 ] ];
v1.sub( b , a );
v2.sub( c , b );
n.cross( v2 , v1 );
n.normalize( );
t.add( a , b );
t.add( c );
t.scale( 1 / 3f );
points.add( new Point3f( t ) );
t.add( n );
points.add( new Point3f( t ) );
}
LineArray result = new LineArray( points.size( ) , GeometryArray.COORDINATES );
k = 0;
for( Point3f p : points )
{
result.setCoordinate( k++ , p );
}
return result;
}
public static LineArray renderFronts( GeometryArray geom , float scale )
{
if( geom instanceof IndexedGeometryArray )
{
return renderFronts( ( IndexedGeometryArray ) geom , scale );
}
List<Point3f> points = new ArrayList<Point3f>( );
Point3f[ ] coordinates = copyCoordinates3f( geom );
Point3f t = new Point3f( );
Vector3f v1 = new Vector3f( );
Vector3f v2 = new Vector3f( );
Vector3f n = new Vector3f( );
int k;
for( k = 0 ; k < coordinates.length ; k += 3 )
{
Point3f a = coordinates[ k ];
Point3f b = coordinates[ k + 1 ];
Point3f c = coordinates[ k + 2 ];
v1.sub( b , a );
v2.sub( c , b );
n.cross( v2 , v1 );
n.normalize( );
t.add( a , b );
t.add( c );
t.scale( 1 / 3f );
points.add( new Point3f( t ) );
t.add( n );
points.add( new Point3f( t ) );
}
LineArray result = new LineArray( points.size( ) , GeometryArray.COORDINATES );
k = 0;
for( Point3f p : points )
{
result.setCoordinate( k++ , p );
}
return result;
}
public static void hardTransform( GeometryArray geom , Transform3D xform )
{
Point3f coord = new Point3f( );
for( int i = 0 ; i < geom.getVertexCount( ) ; i++ )
{
geom.getCoordinate( i , coord );
xform.transform( coord );
geom.setCoordinate( i , coord );
}
}
public static void showErrorDialog( Component parent , Throwable t )
{
final StringBuffer message = new StringBuffer( );
message.append( t );
for( final StackTraceElement elem : t.getStackTrace( ) )
{
message.append( "\n" );
message.append( elem );
}
JOptionPane.showMessageDialog( parent , message , "Exception" , JOptionPane.ERROR_MESSAGE );
}
public static void setVisible( Appearance app , boolean visible )
{
RenderingAttributes ra = app.getRenderingAttributes( );
if( ra == null )
{
ra = new RenderingAttributes( );
app.setRenderingAttributes( ra );
}
ra.setVisible( visible );
}
public static void setVisible( Shape3D s3D , boolean visible )
{
setVisible( getAppearance( s3D ) , visible );
}
public static void setVisibleRecursive( Node n , boolean visible )
{
for( final Node node : SceneGraphIterator.boundedIterable( n ) )
{
if( node instanceof Shape3D )
{
setVisible( getAppearance( ( Shape3D ) node ) , visible );
}
}
}
public static boolean isVisible( Appearance app )
{
final RenderingAttributes ra = app.getRenderingAttributes( );
if( ra == null )
{
return true;
}
return ra.getVisible( );
}
public static boolean isVisible( Shape3D s3D )
{
final Appearance app = s3D.getAppearance( );
if( app == null )
{
return true;
}
return isVisible( app );
}
public static void toggleVisibility( Shape3D s3D )
{
setVisible( s3D , !isVisible( s3D ) );
}
public static void toggleVisibilityRecursive( Node n )
{
for( final Node node : SceneGraphIterator.boundedIterable( n ) )
{
if( node instanceof Shape3D )
{
toggleVisibility( ( Shape3D ) node );
}
}
}
public static void setPolygonMode( Appearance app , int mode )
{
PolygonAttributes pa = app.getPolygonAttributes( );
if( pa == null )
{
pa = new PolygonAttributes( );
app.setPolygonAttributes( pa );
}
pa.setPolygonMode( mode );
}
public static void setPolygonMode( Shape3D s3D , int mode )
{
setPolygonMode( getAppearance( s3D ) , mode );
}
public static void setPolygonModeRecursive( Node n , int mode )
{
for( final Node node : SceneGraphIterator.boundedIterable( n ) )
{
if( node instanceof Shape3D )
{
setPolygonMode( ( Shape3D ) node , mode );
}
}
}
public static void setColor( Appearance app , Color3f color , boolean checkMaterial )
{
ColoringAttributes ca = app.getColoringAttributes( );
if( ca == null )
{
ca = new ColoringAttributes( );
ca.setShadeModel( ColoringAttributes.SHADE_FLAT );
app.setColoringAttributes( ca );
}
ca.setColor( color.x , color.y , color.z );
app.setTransparencyAttributes( null );
if( checkMaterial )
{
final Material mat = app.getMaterial( );
if( mat != null )
{
mat.setAmbientColor( color.x , color.y , color.z );
mat.setDiffuseColor( color.x , color.y , color.z );
mat.setSpecularColor( color.x , color.y , color.z );
}
}
}
public static void setColor( Appearance app , Color4f color , boolean checkMaterial )
{
ColoringAttributes ca = app.getColoringAttributes( );
if( ca == null )
{
ca = new ColoringAttributes( );
ca.setShadeModel( ColoringAttributes.SHADE_FLAT );
app.setColoringAttributes( ca );
}
ca.setColor( color.x , color.y , color.z );
if( color.w != 1f )
{
TransparencyAttributes ta = app.getTransparencyAttributes( );
if( ta == null )
{
ta = new TransparencyAttributes( );
app.setTransparencyAttributes( ta );
}
ta.setTransparencyMode( TransparencyAttributes.BLENDED );
ta.setTransparency( 1f - color.w );
}
else
{
app.setTransparencyAttributes( null );
}
if( checkMaterial )
{
final Material mat = app.getMaterial( );
if( mat != null )
{
mat.setAmbientColor( color.x , color.y , color.z );
mat.setDiffuseColor( color.x , color.y , color.z );
mat.setSpecularColor( color.x , color.y , color.z );
}
}
}
public static void setColor( Shape3D s3D , Color4f color , boolean checkMaterial )
{
setColor( getAppearance( s3D ) , color , checkMaterial );
}
public static void setColorRecursive( Node n , Color4f color , boolean checkMaterial )
{
for( final Node node : SceneGraphIterator.boundedIterable( n ) )
{
if( node instanceof Shape3D )
{
setColor( getAppearance( ( Shape3D ) node ) , color , checkMaterial );
}
}
}
public static void modifyColoringAttributesRecursive( Node n , Color3f color , int shadeModel )
{
for( final Node node : SceneGraphIterator.boundedIterable( n ) )
{
if( node instanceof Shape3D )
{
modifyColoringAttributes( ( Shape3D ) node , color , shadeModel );
}
}
}
public static void modifyColoringAttributes( Shape3D n , Color3f color , int shadeModel )
{
Appearance app = getAppearance( n );
ColoringAttributes ca = app.getColoringAttributes( );
if( ca == null )
{
ca = new ColoringAttributes( );
app.setColoringAttributes( ca );
}
ca.setColor( color );
ca.setShadeModel( shadeModel );
}
}