Introducing Texture Mapping
ExTexture.java
//
// CLASS
// ExTexture - illustrate use of textures
//
// LESSON
// Use Texture2D and TextureAttributes to apply a texture image
// to a shape.
//
// AUTHOR
// Michael J. Bailey / San Diego Supercomputer Center
//
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.lang.*;
import java.net.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.image.*;
public class ExTexture
extends Example
{
// nodes that can be updated via a menu:
private Shape3D sh = null; // overall scene shape
private TransformGroup geomGroup = null; // group of geometry
private GeometryArray geom = null; // scene geometry
private Appearance app = null; // geometry appearance
private Appearance dummyApp = null; // temporary appearance
private Material mat = null; // geometry surface color
private PolygonAttributes polyatt = null; // polygon attributes
private TextureAttributes texatt = null; // texture attributes
private TextureAttributes dummyAtt = null; // temporary texture attributes
private Transform3D tt = null; // texture transform
private Texture2D tex = null; // current texture
private TransformGroup tg = null; // object transform
private Transform3D tgt = null; // object transform
// Build the scene:
public Group buildScene()
{
// Turn on the headlight
setHeadlightEnable( true );
// Build the scene root
Group scene = new Group();
// Create application bounds
BoundingSphere worldBounds = new BoundingSphere(
new Point3d( 0.0, 0.0, 0.0 ), // Center
1000.0 ); // Extent
mat = new Material();
mat.setAmbientColor( 0.6f, 0.6f, 0.6f );
mat.setDiffuseColor( 1.0f, 0.0f, 0.0f );
mat.setSpecularColor( 0.0f, 0.0f, 0.f );
tt = new Transform3D();
tt.setIdentity();
tt.setScale( 1. );
polyatt = new PolygonAttributes();
polyatt.setCullFace( PolygonAttributes.CULL_NONE );
polyatt.setPolygonMode( PolygonAttributes.POLYGON_FILL );
dummyAtt = new TextureAttributes();
texatt = new TextureAttributes();
texatt.setCapability( TextureAttributes.ALLOW_MODE_WRITE );
texatt.setCapability( TextureAttributes.ALLOW_BLEND_COLOR_WRITE );
texatt.setCapability( TextureAttributes.ALLOW_TRANSFORM_WRITE );
texatt.setTextureMode( ((Integer)modes[currentMode].value).intValue() );
texatt.setPerspectiveCorrectionMode( TextureAttributes.NICEST );
texatt.setTextureTransform( tt );
texatt.setTextureBlendColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.5f ) );
dummyApp = new Appearance();
app = new Appearance();
app.setCapability( Appearance.ALLOW_TEXTURE_WRITE );
app.setCapability( Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE );
app.setMaterial( mat );
app.setPolygonAttributes( polyatt );
app.setTextureAttributes( texatt );
app.setTexture( tex );
sh = new Shape3D();
sh.setAppearance( app );
sh.setCapability( Shape3D.ALLOW_GEOMETRY_WRITE );
sh.setCapability( Shape3D.ALLOW_APPEARANCE_WRITE );
buildGeometry();
sh.setGeometry( geom );
tgt = new Transform3D();
tgt.rotX( 0.0 * Math.PI / 6. );
tg = new TransformGroup( tgt );
tg.addChild( sh );
scene.addChild( tg );
return scene;
}
//
// Main (if invoked as an application)
//
public static void main( String[] args )
{
ExTexture ex = new ExTexture();
ex.initialize( args );
ex.buildUniverse();
ex.showFrame();
}
// Image menu choices
private int currentTexture = 0;
private int currentMode = 0;
private int currentBoundary = 0;
private int currentFilter = 0;
private CheckboxMenuItem[] textureMenu;
private CheckboxMenuItem[] modeMenu;
private CheckboxMenuItem[] boundaryMenu;
private CheckboxMenuItem[] filterMenu;
private CheckboxMenuItem[] xformMenu;
private NameValue[] images =
{
new NameValue( "Earth", "earth.jpg" ),
};
private NameValue[] modes =
{
new NameValue( "BLEND", new Integer( TextureAttributes.BLEND ) ),
new NameValue( "DECAL", new Integer( TextureAttributes.DECAL ) ),
new NameValue( "MODULATE", new Integer( TextureAttributes.MODULATE ) ),
new NameValue( "REPLACE", new Integer( TextureAttributes.REPLACE ) ),
};
private NameValue[] boundaries =
{
new NameValue( "CLAMP", new Integer( Texture.CLAMP ) ),
new NameValue( "WRAP", new Integer( Texture.WRAP ) ),
};
private NameValue[] filters =
{
new NameValue( "POINT", new Integer( Texture.BASE_LEVEL_POINT ) ),
new NameValue( "LINEAR", new Integer( Texture.BASE_LEVEL_LINEAR ) ),
};
private Texture2D[] textureComponents;
private TextureLoader[] texLoader;
//
// Initialize the GUI (application and applet)
//
public void initialize( String[] args )
{
// Initialize the window, menubar, etc.
super.initialize( args );
exampleFrame.setTitle( "Java 3D Texture Mapping Example" );
// Add a menu to select among texture options
Menu mt = new Menu( "Texture" );
textureMenu = new CheckboxMenuItem[ images.length ];
for( int i = 0; i < images.length; i++ )
{
textureMenu[i] = new CheckboxMenuItem( images[i].name );
textureMenu[i].addItemListener( this );
textureMenu[i].setState( false );
mt.add( textureMenu[i] );
}
exampleMenuBar.add( mt );
// Add a menu to select the texture mode:
Menu mm = new Menu( "Texture Mode" );
modeMenu = new CheckboxMenuItem[ modes.length ];
for( int i = 0; i < modes.length; i++ )
{
modeMenu[i] = new CheckboxMenuItem( modes[i].name );
modeMenu[i].addItemListener( this );
modeMenu[i].setState( false );
mm.add( modeMenu[i] );
}
exampleMenuBar.add( mm );
// Add a menu to select the texture boundary:
Menu mb = new Menu( "Texture Boundary" );
boundaryMenu = new CheckboxMenuItem[ boundaries.length ];
for( int i = 0; i < boundaries.length; i++ )
{
boundaryMenu[i] = new CheckboxMenuItem( boundaries[i].name );
boundaryMenu[i].addItemListener( this );
boundaryMenu[i].setState( false );
mb.add( boundaryMenu[i] );
}
exampleMenuBar.add( mb );
// Add a menu to select the filter mode:
Menu mf = new Menu( "Filter Mode" );
filterMenu = new CheckboxMenuItem[ filters.length ];
for( int i = 0; i < filters.length; i++ )
{
filterMenu[i] = new CheckboxMenuItem( filters[i].name );
filterMenu[i].addItemListener( this );
filterMenu[i].setState( false );
mf.add( filterMenu[i] );
}
exampleMenuBar.add( mf );
// Add a menu to select the texture transformation:
Menu mx = new Menu( "Transformation" );
xformMenu = new CheckboxMenuItem[ 3 ];
xformMenu[0] = new CheckboxMenuItem( "Identity" );
xformMenu[0].addItemListener( this );
xformMenu[0].setState( true );
mx.add( xformMenu[0] );
xformMenu[1] = new CheckboxMenuItem( "Rotate 45" );
xformMenu[1].addItemListener( this );
xformMenu[1].setState( false );
mx.add( xformMenu[1] );
xformMenu[2] = new CheckboxMenuItem( "Scale 2" );
xformMenu[2].addItemListener( this );
xformMenu[2].setState( false );
mx.add( xformMenu[2] );
exampleMenuBar.add( mx );
// set the current values:
currentTexture = 0;
textureMenu[currentTexture].setState( true );
currentMode = 3;
modeMenu[currentMode].setState( true );
currentBoundary = 0;
boundaryMenu[currentBoundary].setState( true );
currentFilter = 0;
filterMenu[currentFilter].setState( true );
// Preload the texture images
// Use the texture loading utility to read in the texture
// files and process them into an ImageComponent2D
// for use in the Background node.
if( debug )
System.err.println( "Loading textures..." );
textureComponents = new Texture2D[images.length];
texLoader = new TextureLoader[images.length];
String value = null;
for( int i = 0; i < images.length; i++ )
{
value = (String)images[i].value;
if( debug )
System.err.println( "Loading texture '" + value + "'" );
texLoader[i] = new TextureLoader( value, this);
ImageComponent2D ic = texLoader[i].getImage();
if( ic == null )
{
System.err.println( "Cannot load texture '" + value + "'" );
textureComponents[i] = null;
}
else
{
Texture2D t = (Texture2D) texLoader[i].getTexture();
t.setBoundaryModeS( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryModeT( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );
t.setMagFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMinFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMipMapMode( Texture.BASE_LEVEL );
t.setCapability( Texture.ALLOW_BOUNDARY_COLOR_READ );
t.setCapability( Texture.ALLOW_BOUNDARY_MODE_READ );
t.setCapability( Texture.ALLOW_ENABLE_READ );
t.setCapability( Texture.ALLOW_FILTER_READ );
t.setCapability( Texture.ALLOW_IMAGE_READ );
t.setCapability( Texture.ALLOW_MIPMAP_MODE_READ );
t.setCapability( Texture.ALLOW_ENABLE_WRITE );
t.setEnable( true );
textureComponents[i] = t;
}
}
tex = textureComponents[ currentTexture ];
}
//
// Handle checkboxes
//
public void itemStateChanged( ItemEvent event )
{
Object src = event.getSource();
// Check if it is an image choice
for( int i = 0; i < textureMenu.length; i++ )
{
if( src == textureMenu[i] )
{
// Update the checkboxes
textureMenu[currentTexture].setState( false );
currentTexture = i;
textureMenu[currentTexture].setState( true );
// Set the texture
sh.setAppearance( dummyApp );
tex = textureComponents[currentTexture];
app.setTexture( tex );
sh.setAppearance( app );
return;
}
}
// Check if it is a mode choice
for( int i = 0; i < modeMenu.length; i++ )
{
if( src == modeMenu[i] )
{
// Update the checkboxes
modeMenu[currentMode].setState( false );
currentMode = i;
modeMenu[currentMode].setState( true );
// Set the mode
app.setTextureAttributes( dummyAtt );
texatt.setTextureMode( ((Integer)modes[currentMode].value).intValue() );
app.setTextureAttributes( texatt );
return;
}
}
// Check if it is a boundary choice
for( int i = 0; i < boundaryMenu.length; i++ )
{
if( src == boundaryMenu[i] )
{
// Update the checkboxes
boundaryMenu[currentBoundary].setState( false );
currentBoundary = i;
boundaryMenu[currentBoundary].setState( true );
// Set the boundary
String value = (String)images[currentTexture].value;
if( debug )
System.err.println( "Re-loading texture '" + value + "'" );
TextureLoader tl = new TextureLoader( value, this );
Texture2D t = (Texture2D) tl.getTexture();
t.setBoundaryModeS( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryModeT( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );
t.setMagFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMinFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMipMapMode( Texture.BASE_LEVEL );
t.setCapability( Texture.ALLOW_ENABLE_WRITE );
t.setEnable( true );
app.setTexture( t );
tex = textureComponents[currentTexture] = t;
if( debug )
System.err.println( "Ready." );
return;
}
}
// Check if it is a filter choice
for( int i = 0; i < filterMenu.length; i++ )
{
if( src == filterMenu[i] )
{
// Update the checkboxes
filterMenu[currentFilter].setState( false );
currentFilter = i;
filterMenu[currentFilter].setState( true );
// Set the filter
String value = (String)images[currentTexture].value;
if( debug )
System.err.println( "Re-loading texture '" + value + "'" );
TextureLoader tl = new TextureLoader( value, this );
Texture2D t = (Texture2D) tl.getTexture();
t.setBoundaryModeS( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryModeT( ((Integer)boundaries[currentBoundary].value).intValue() );
t.setBoundaryColor( new Color4f( 0.0f, 1.0f, 0.0f, 0.0f ) );
t.setMagFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMinFilter( ((Integer)filters[currentFilter].value).intValue() );
t.setMipMapMode( Texture.BASE_LEVEL );
t.setCapability( Texture.ALLOW_ENABLE_WRITE );
t.setEnable( true );
app.setTexture( t );
tex = textureComponents[currentTexture] = t;
if( debug )
System.err.println( "Ready." );
return;
}
}
// Check if it is a texture transform choice
for( int i = 0; i < 3; i++ )
{
if( src == xformMenu[0] )
{
tt = new Transform3D( );
tt.setIdentity();
texatt.setTextureTransform( tt );
xformMenu[0].setState( true );
xformMenu[1].setState( false );
xformMenu[2].setState( false );
if( debug )
System.err.println( "Ready." );
return;
}
else if( src == xformMenu[1] )
{
tt = new Transform3D( );
tt.setIdentity();
tt.rotZ( Math.PI/4. );
texatt.setTextureTransform( tt );
xformMenu[0].setState( false );
xformMenu[1].setState( true );
xformMenu[2].setState( false );
if( debug )
System.err.println( "Ready." );
return;
}
else if( src == xformMenu[2] )
{
tt = new Transform3D( );
tt.setIdentity();
tt.setScale( 2. );
texatt.setTextureTransform( tt );
xformMenu[0].setState( false );
xformMenu[1].setState( false );
xformMenu[2].setState( true );
if( debug )
System.err.println( "Ready." );
return;
}
}
// Handle all other checkboxes
super.itemStateChanged( event );
}
public void buildGeometry()
{
QuadArray cube = new QuadArray(
24,
GeometryArray.COORDINATES |
GeometryArray.NORMALS |
GeometryArray.TEXTURE_COORDINATE_2 );
cube.setCapability( GeometryArray.ALLOW_COORDINATE_WRITE );
cube.setCapability( GeometryArray.ALLOW_TEXCOORD_WRITE );
VertexList vl = new VertexList( cube );
float MAX = 1.0f;
float MIN = -1.0f;
vl.xyzijkst( -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MIN, MIN );
vl.xyzijkst( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MAX, MIN );
vl.xyzijkst( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MAX, MAX );
vl.xyzijkst( -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MIN, MAX );
vl.xyzijkst( -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MIN, MIN );
vl.xyzijkst( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MAX, MIN );
vl.xyzijkst( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MAX, MAX );
vl.xyzijkst( -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MIN, MAX );
vl.xyzijkst( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, MIN, MIN );
vl.xyzijkst( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, MAX, MIN );
vl.xyzijkst( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, MAX, MAX );
vl.xyzijkst( 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, MIN, MAX );
vl.xyzijkst( -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, MIN, MIN );
vl.xyzijkst( -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, MAX, MIN );
vl.xyzijkst( -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, MAX, MAX );
vl.xyzijkst( -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, MIN, MAX );
vl.xyzijkst( -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, MIN, MIN );
vl.xyzijkst( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, MAX, MIN );
vl.xyzijkst( 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, MAX, MAX );
vl.xyzijkst( -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, MIN, MAX );
vl.xyzijkst( -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, MIN, MIN );
vl.xyzijkst( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, MAX, MIN );
vl.xyzijkst( 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, MAX, MAX );
vl.xyzijkst( -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, MIN, MAX );
geom = cube;
}
public class
VertexList
{
private int index = 0;
private GeometryArray ga = null;
public VertexList( GeometryArray _ga )
{
index = 0;
ga = _ga;
}
public void xyzst( float x, float y, float z, float s, float t )
{
ga.setCoordinate( index, new Point3f( x, y, z ) );
ga.setTextureCoordinate( index, new Point2f( s, t ) );
index++;
}
public void xyzijkst( float x, float y, float z,
float i, float j, float k,
float s, float t )
{
ga.setCoordinate( index, new Point3f( x, y, z ) );
ga.setNormal( index, new Vector3f( i, j, k ) );
ga.setTextureCoordinate( index, new Point2f( s, t ) );
index++;
}
}
}