// // CLASS // ExTexture - illustrate use of textures // // LESSON // Use Texture2D and TextureAttributes to apply a texture image // to a shape. // // AUTHOR // David R. Nadeau / 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 { //-------------------------------------------------------------- // SCENE CONTENT //-------------------------------------------------------------- // // Nodes (updated via menu) // private Shape3D shape = null; // overall scene shape private Appearance app = null; // geometry appearance private Appearance dummyApp = null; // temporary appearance private TextureAttributes texatt = null;// texture attributes private TextureAttributes dummyAtt = null;// temporary texture attributes private Texture2D tex = null; // current texture // // Build scene // public Group buildScene( ) { // Get the current menu choices for appearance attributes int textureMode = ((Integer)modes[currentMode].value).intValue( ); Color3f color = (Color3f)colors[currentColor].value; Color3f blendColor = (Color3f)colors[currentBlendColor].value; // Turn on the example headlight setHeadlightEnable( true ); // Default to examine navigation setNavigationType( Examine ); // Disable scene graph compilation for this example setCompilable( false ); // Create the scene group Group scene = new Group( ); // BEGIN EXAMPLE TOPIC // Set up a basic material Material mat = new Material( ); mat.setAmbientColor( 0.2f, 0.2f, 0.2f ); mat.setDiffuseColor( 1.0f, 1.0f, 1.0f ); mat.setSpecularColor( 0.0f, 0.0f, 0.0f ); mat.setLightingEnable( true ); // Set up the texturing attributes with an initial // texture mode, texture transform, and blend color texatt = new TextureAttributes( ); texatt.setPerspectiveCorrectionMode( TextureAttributes.NICEST ); texatt.setTextureMode( textureMode ); texatt.setTextureTransform( new Transform3D( ) ); // Identity texatt.setTextureBlendColor( blendColor.x, blendColor.y, blendColor.z, 0.5f ); // Enable changing these while the node component is live texatt.setCapability( TextureAttributes.ALLOW_MODE_WRITE ); texatt.setCapability( TextureAttributes.ALLOW_BLEND_COLOR_WRITE ); texatt.setCapability( TextureAttributes.ALLOW_TRANSFORM_WRITE ); // Create an appearance using these attributes app = new Appearance( ); app.setMaterial( mat ); app.setTextureAttributes( texatt ); app.setTexture( tex ); // And enable changing these while the node component is live app.setCapability( Appearance.ALLOW_TEXTURE_WRITE ); app.setCapability( Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE ); // Build a shape and enable changing its appearance shape = new Shape3D( buildGeometry( ), app ); shape.setCapability( Shape3D.ALLOW_APPEARANCE_WRITE ); // END EXAMPLE TOPIC // Create some dummy appearance and tex attribute node components // In response to menu choices, we quickly switch the shape to // use one of these, then diddle with the main appearance or // tex attribute, then switch the shape back. This effectively // makes the appearance or tex attributes we want to change // become un-live during a change. We have to do this approach // because some texture features have no capability bits to set // to allow changes while live. dummyApp = new Appearance(); dummyAtt = new TextureAttributes( ); scene.addChild( shape ); return scene; } //-------------------------------------------------------------- // USER INTERFACE //-------------------------------------------------------------- // // Main // public static void main( String[] args ) { ExTexture ex = new ExTexture( ); ex.initialize( args ); ex.buildUniverse( ); ex.showFrame( ); } // Texture enable/disable private boolean textureOnOff = true; private CheckboxMenuItem textureOnOffMenu = null; // Texture image choices private NameValue[] images = { new NameValue( "Red bricks", "brick.jpg" ), new NameValue( "Stone bricks", "stonebrk2.jpg" ), new NameValue( "Marble", "granite07rev.jpg" ), new NameValue( "Mud", "mud01.jpg" ), new NameValue( "Wood", "flooring.jpg" ), new NameValue( "Earth", "earthmap.jpg" ), }; private CheckboxMenu imageMenu = null; private int currentImage = 0; // Texture boundary mode choices private NameValue[] boundaries = { new NameValue( "CLAMP", new Integer( Texture.CLAMP ) ), new NameValue( "WRAP", new Integer( Texture.WRAP ) ), }; private CheckboxMenu boundaryMenu = null; private int currentBoundary = 0; // Texture boundary color choices private NameValue[] colors = { new NameValue( "White", White ), new NameValue( "Gray", Gray ), new NameValue( "Dark Gray", DarkGray ), new NameValue( "Black", Black ), new NameValue( "Red", Red ), new NameValue( "Dark Red", DarkRed ), new NameValue( "Green", Green ), new NameValue( "Dark Green", DarkGreen ), new NameValue( "Blue", Blue ), new NameValue( "Dark Blue", DarkBlue ), }; private CheckboxMenu colorMenu = null; private int currentColor = 6; // Texture filter choices private NameValue[] filters = { new NameValue( "POINT", new Integer( Texture.BASE_LEVEL_POINT ) ), new NameValue( "LINEAR", new Integer( Texture.BASE_LEVEL_LINEAR ) ), }; private CheckboxMenu filterMenu = null; private int currentFilter = 0; // Texture attributes mode choices 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 CheckboxMenu modeMenu = null; private int currentMode = 2; // Texture attributes blend color choices private CheckboxMenu blendColorMenu = null; private int currentBlendColor = 6; private NameValue[] xforms = { new NameValue( "Identity", new Integer( 0 ) ), new NameValue( "Scale by 2", new Integer( 1 ) ), new NameValue( "Scale by 4", new Integer( 2 ) ), new NameValue( "Rotate by 45 degrees", new Integer( 3 ) ), new NameValue( "Translate by 0.25", new Integer( 4 ) ), }; private CheckboxMenu xformMenu = null; private int currentXform = 0; private Texture2D[] textureComponents; // // 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 menubar menu to change texture parameters // Image --> // Boundary mode --> // Boundary color --> // Filter mode --> // Menu m = new Menu( "Texture" ); textureOnOffMenu = new CheckboxMenuItem( "Texturing enabled", textureOnOff ); textureOnOffMenu.addItemListener( this ); m.add( textureOnOffMenu ); imageMenu = new CheckboxMenu( "Image", images, currentImage, this ); m.add( imageMenu ); boundaryMenu = new CheckboxMenu( "Boundary mode", boundaries, currentBoundary, this ); m.add( boundaryMenu ); colorMenu = new CheckboxMenu( "Boundary color", colors, currentColor, this ); m.add( colorMenu ); filterMenu = new CheckboxMenu( "Filter mode", filters, currentFilter, this ); m.add( filterMenu ); exampleMenuBar.add( m ); // // Add a menubar menu to change texture attributes parameters // Mode --> // Blend color --> // m = new Menu( "TextureAttributes" ); modeMenu = new CheckboxMenu( "Mode", modes, currentMode, this ); m.add( modeMenu ); blendColorMenu = new CheckboxMenu( "Blend color", colors, currentBlendColor, this ); m.add( blendColorMenu ); xformMenu = new CheckboxMenu( "Transform", xforms, currentXform, this ); m.add( xformMenu ); exampleMenuBar.add( m ); // 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]; String value = null; for ( int i = 0; i < images.length; i++ ) { value = (String)images[i].value; textureComponents[i] = loadTexture( value ); } tex = textureComponents[ currentImage ]; } // // Handle checkboxes and menu choices // public void checkboxChanged( CheckboxMenu menu, int check ) { if ( menu == imageMenu ) { // Change the texture image currentImage = check; Texture tex = textureComponents[currentImage]; int mode = ((Integer)boundaries[currentBoundary].value).intValue(); Color3f color = (Color3f)colors[currentColor].value; int filter = ((Integer)filters[currentFilter].value).intValue( ); shape.setAppearance( dummyApp ); tex.setEnable( textureOnOff ); tex.setBoundaryModeS( mode ); tex.setBoundaryModeT( mode ); tex.setBoundaryColor( color.x, color.y, color.z, 0.0f ); tex.setMagFilter( filter ); tex.setMinFilter( filter ); app.setTexture( tex ); shape.setAppearance( app ); return; } if ( menu == boundaryMenu ) { // Change the texture boundary mode currentBoundary = check; Texture tex = textureComponents[currentImage]; int mode = ((Integer)boundaries[currentBoundary].value).intValue(); shape.setAppearance( dummyApp ); tex.setBoundaryModeS( mode ); tex.setBoundaryModeT( mode ); app.setTexture( tex ); shape.setAppearance( app ); return; } if ( menu == colorMenu ) { // Change the boundary color currentColor = check; Color3f color = (Color3f)colors[currentColor].value; Texture tex = textureComponents[currentImage]; shape.setAppearance( dummyApp ); tex.setBoundaryColor( color.x, color.y, color.z, 0.0f ); app.setTexture( tex ); shape.setAppearance( app ); return; } if ( menu == filterMenu ) { // Change the filter mode currentFilter = check; int filter = ((Integer)filters[currentFilter].value).intValue( ); Texture tex = textureComponents[currentImage]; shape.setAppearance( dummyApp ); tex.setMagFilter( filter ); tex.setMinFilter( filter ); app.setTexture( tex ); shape.setAppearance( app ); return; } if ( menu == modeMenu ) { // Change the texture mode currentMode = check; int mode = ((Integer)modes[currentMode].value).intValue( ); app.setTextureAttributes( dummyAtt ); texatt.setTextureMode( mode ); app.setTextureAttributes( texatt ); return; } if ( menu == blendColorMenu ) { // Change the boundary color currentBlendColor = check; Color3f color = (Color3f)colors[currentBlendColor].value; app.setTextureAttributes( dummyAtt ); texatt.setTextureBlendColor( color.x, color.y, color.z, 0.5f ); app.setTextureAttributes( texatt ); return; } if ( menu == xformMenu ) { // Change the texture transform currentXform = check; Transform3D tt = new Transform3D( ); switch ( currentXform ) { default: case 0: // Identity texatt.setTextureTransform( tt ); return; case 1: // Scale by 2 tt.setScale( 2.0 ); texatt.setTextureTransform( tt ); return; case 2: // Scale by 4 tt.setScale( 4.0 ); texatt.setTextureTransform( tt ); return; case 3: // Z rotate by 45 degrees tt.rotZ( Math.PI/4.0 ); texatt.setTextureTransform( tt ); return; case 4: // Translate by 0.25 tt.set( new Vector3f( 0.25f, 0.0f, 0.0f ) ); texatt.setTextureTransform( tt ); return; } } // Handle all other checkboxes super.checkboxChanged( menu, check ); } public void itemStateChanged( ItemEvent event ) { Object src = event.getSource( ); // Check if it is the texture on/off choice if ( src == textureOnOffMenu ) { textureOnOff = textureOnOffMenu.getState( ); Texture tex = textureComponents[currentImage]; tex.setEnable( textureOnOff ); } // Handle all other checkboxes super.itemStateChanged( event ); } //-------------------------------------------------------------- // UTILITY METHODS //-------------------------------------------------------------- // // Handle loading a texture and setting up its attributes // private Texture2D loadTexture( String filename ) { // Load the texture image file if ( debug ) System.err.println( "Loading texture '" + filename + "'" ); TextureLoader texLoader = new TextureLoader( filename, this); // If the image is NULL, something went wrong ImageComponent2D ic = texLoader.getImage( ); if ( ic == null ) { System.err.println( "Cannot load texture '" + filename + "'" ); return null; } // Configure a Texture2D with the image Texture2D t = (Texture2D)texLoader.getTexture( ); int mode = ((Integer)boundaries[currentBoundary].value).intValue(); t.setBoundaryModeS( mode ); t.setBoundaryModeT( mode ); Color3f color = (Color3f)colors[currentColor].value; t.setBoundaryColor( color.x, color.y, color.z, 0.0f ); int filter = ((Integer)filters[currentFilter].value).intValue( ); t.setMagFilter( filter ); t.setMinFilter( filter ); t.setMipMapMode( Texture.BASE_LEVEL ); // Turn it on and allow future changes t.setEnable( true ); t.setCapability( Texture.ALLOW_ENABLE_WRITE ); return t; } // // Build a cube using a QuadArray // public QuadArray 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 = 0.0f; // Coordinate Normal Texture // X Y Z I J K S T // Front 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 ); // Back 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, MIN, MIN ); 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, MAX, MAX ); // Right 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 ); // Left 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, MIN, MAX ); 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, MAX, MIN ); // Top 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, MAX, MAX ); 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, MIN, MIN ); // Bottom 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 ); return cube; } // // Use a helper class to manage the coordinate lists // private class VertexList { private int index = 0; private GeometryArray ga = null; public VertexList( GeometryArray newga ) { index = 0; ga = newga; } 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++; } } }