CHAPTER 3

Getting started, Hello Java 3D!

3.1 Installation

3.2 Your first Java 3D application

3.3 Exercises for the reader

3.4 Summary

Now the fun begins. It’s time to begin conquering the Java 3D development environment, setting ourselves up for serious Java 3D fun in the chapters to come. I’ll introduce a realistic Java 3D application to test your configuration, and allow you to experiment with some of the features described in later chapters. You’ll look at a simple Java 3D example, SimpleTest, that illustrates building an AWT-based Java 3D application. The SimpleTest example uses the Sun utility classes MainFrame and SimpleUniverse (included with your Java 3D distribution) to hide some of the complexities that we will be delving into in the chapters to come.

3.1 Installation

Our first step, obviously, is to install everything we need for Java 3D development. Refer to appendix B and the bibliography for useful sources of information or additional help.

3.1.1 Java 2 SDK

Check the Sun web site (http://java.sun.com) and download the latest release. Java 2 SDK 1.3.1 (JDK 1.3.1) is the latest release at the time of print. You can also find it at http://www.javasoft.com/j2se/1.3/. Remove all previous versions of the SDK, JDK, or JRE prior to installing the new SDK. After installation launch the Java plug-in control applet from the Windows Control Panel and set the Java Runtime Environment (JRE) to the newly installed SDK location. This will enable running Java 3D applets using the Java 2 plug-in.

3.1.2 Java 3D 1.2 JDK

Download the latest release of the Java 3D SDK at http://www.javasoft.com/products/java-media/3D/index.html. The OpenGL version of Java 3D has historically been more stable and ahead of the DirectX release in terms of features. At the time of print the latest release is Java 3D 1.2.1. You should install Java 3D into the same directory as the Java 2 SDK, typically c:\jdk1.3. This will ensure that all your Java 2 demo applications are installed into the same place.

You can then use REGEDIT to edit the Windows registry to remove all references to the JRE installation directory (which is also installed when you install the SDK). Replace all occurrences of c:\program files\javasoft\jre\1.3\… with the SDK installation location, usually c:\jdk1.3\jre\… This will enable running the Java 3D demos from the command line, and ensure that only one Java 2 runtime environment is installed on your machine.

IMPORTANT          Do not run REGEDIT unless you are an experienced Windows user and familiar with editing the registry. It is not strictly necessary to remove all references to the JRE install location.

Test your Java 3D installation by running the HelloUniverse Java 3D demo. First run from the command line by going to the relevant installation directory and then typing:

java HelloUniverse

You can test the Java 2 plug-in installation by double-clicking the HelloUniverse_plug-in.html file. Your web browser should launch, the Java 2 plug-in window will appear, and the HelloUniverse applet should start.

Once the tests are running you can safely delete the c:\program files\javasoft directory.

3.1.3 Documentation

Java 3D programming involves general Java programming, high-performance programming, 3D graphics algorithms, 2D graphics programming, UI design, and many other issues. A good reference library or collection of electronic bookmarks will save you a lot of time and help you to avoid some of the pitfalls that have befallen your predecessors.

Though far from exhaustive, the list of references which follows should give you some indication of fruitful areas to start researching.

3.1.4 Java 2 development environment (optional)

Every developer has their favorite programmer’s editor, and an increasing number of Integrated Development Environments (IDEs) are available that support Java 2. They range from free to expensive, and have a wide variety of features. Some of the more popular IDEs for Java 2 development are:

All the examples for this book were built using Kawa. Unfortunately, after Allaire was acquired by Macromedia, development of Kawa was discontinued.

3.1.5 Performance analysis tools (optional)

As you formalize your designs and requirements it is often helpful to drop into a performance measurement tool to see where your code is spending its time. Two popular commercial tools for Java optimization are:

You can also use the free (but harder to interpret) performance measurement capabilities of the Java 2 JVM. See the documentation for the java –Xprof argument for details.

3.1.6 Java class decompiler (optional)

When things get really sticky and you can’t understand what Java 3D is doing it can be useful to decompile the Java 3D class files. You will need to decompress the Java 3D JAR files and extract the class files prior to decompling them. A popular (and free) decompiler is JAD (JAva Decompiler). Find it at http://kpdus.tripod.com/jad.html.

3.2 Your first Java 3D application

The SimpleTest example (figure 3.1) is intended to build upon the HelloUniverse example that comes with the Java 3D distribution. I’ve attempted to expand upon HelloUniverse by documenting the relationships between the various constructs used in the example and showcasing some of the features of Java 3D that enable you to build fairly complex applications with very little code. This example is 280 lines (less than 100 without comments) and illustrates some fairly complex functionality:

Figure 3.1

Figure 3.1 The SimpleTest example. One hundred lines of Java code give you an animated scene, including a graphical textured background with directional lighting

For instructions on running the examples that accompany the book please refer to appendix A.

To produce a comparable example using basic OpenGL would require many hundreds of lines of code. You can quickly see the benefits of a Java 3D’s higher-level of scene description—the scenegraph.

From SimpleTest.java

import java.applet.Applet;

import javax.media.j3d.*; import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.image.TextureLoader;
/* * This example builds a simple Java 3D application using the * Sun utility classes: MainFrame and SimpleUniverse. * The example displays a moving sphere, in front of a * background image. It uses a texture image and one light * to increase the visual impact of the scene. */ public class SimpleTest extends Applet { /* * Create a simple Java 3D environment containing: * a sphere (geometry), a light,background geometry * with an applied texture, and a behavior that will * move the sphere along the X-axis. */ public SimpleTest() { // create the SimpleUniverse class that will // encapsulate the scene that we are building. // SimpleUniverse is a helper class (utility) // from SUN that is included with the core Java 3D // distribution. SimpleUniverse u = new SimpleUniverse();
// create a BranchGroup. A BranchGroup is a node in // a Tree data structure that can have child nodes BranchGroup bgRoot = new BranchGroup();
// create the Background node and add it to the SimpleUniverse u.addBranchGraph( createBackground() );
// create the behaviors to move the geometry along the X-axis. // The behavior is added as a child of the bgRoot node. // Anything added as a child of the tg node will be effected by the // behavior (will be moved along the X-axis). TransformGroup tg = createBehaviors( bgRoot );
// add the Sphere geometry as a child of the tg // so that it will be moved along the X-axis. tg.addChild( createSceneGraph() );
// because the sphere was added at the 0,0,0 coordinate // and by default the viewer is also located at 0,0,0 // we have to move the viewer back a little so that // she can see the scene. u.getViewingPlatform().setNominalViewingTransform();
// add a light to the root BranchGroup to illuminate the scene addLights( bgRoot );
// finally wire everything together by adding the root // BranchGroup to the SimpleUniverse u.addBranchGraph( bgRoot ); }
/* * Create the geometry for the scene. In this case * we simply create a Sphere * (a built-in Java 3D primitive). */ public BranchGroup createSceneGraph() { // create a parent BranchGroup node for the Sphere BranchGroup bg = new BranchGroup();
// create an Appearance for the Sphere. // The Appearance object controls various rendering // options for the Sphere geometry. Appearance app = new Appearance();
// assign a Material to the Appearance. For the Sphere // to respond to the light in the scene it must have a Material. // Assign some colors to the Material and a shininess setting // that controls how reflective the surface is to lighting. Color3f objColor = new Color3f(0.8f, 0.2f, 1.0f); Color3f black = new Color3f(0.0f, 0.0f, 0.0f); app.setMaterial(new Material(objColor, black, objColor, black, 80.0f));
// create a Sphere with a radius of 0.1 // and associate the Appearance that we described. // the option GENERATE_NORMALS is required to ensure that the // Sphere responds correctly to lighting. Sphere sphere = new Sphere( 0.1f, Primitive.GENERATE_NORMALS, app );
// add the sphere to the BranchGroup to wire // it into the scene. bg.addChild( sphere ); return bg; }
/* * Add a directional light to the BranchGroup. */ public void addLights( BranchGroup bg ) { // create the color for the light Color3f color = new Color3f( 1.0f,1.0f,0.0f );
// create a vector that describes the direction that // the light is shining. Vector3f direction = new Vector3f( -1.0f,-1.0f,-1.0f );
// create the directional light with the color and direction DirectionalLight light = new DirectionalLight( color, direction );
// set the volume of influence of the light. // Only objects within the Influencing Bounds // will be illuminated. light.setInfluencingBounds( getBoundingSphere() );
// add the light to the BranchGroup bg.addChild( light ); }
/* * Create some Background geometry to use as * a backdrop for the application. Here we create * a Sphere that will enclose the entire scene and * apply a texture image onto the inside of the Sphere * to serve as a graphical backdrop for the scene. */ public BranchGroup createBackground() { // create a parent BranchGroup for the Background BranchGroup backgroundGroup = new BranchGroup();
// create a new Background node Background back = new Background();
// set the range of influence of the background back.setApplicationBounds( getBoundingSphere() );
// create a BranchGroup that will hold // our Sphere geometry BranchGroup bgGeometry = new BranchGroup();
// create an appearance for the Sphere Appearance app = new Appearance();
// load a texture image using the Java 3D texture loader Texture tex = new TextureLoader( "back.jpg", this).getTexture();
// apply the texture to the Appearance app.setTexture( tex );
// create the Sphere geometry with radius 1.0. // we tell the Sphere to generate texture coordinates // to enable the texture image to be rendered // and because we are *inside* the Sphere we have to generate // Normal coordinates inwards or the Sphere will not be visible. Sphere sphere = new Sphere( 1.0f, Primitive.GENERATE_TEXTURE_COORDS |
Primitive.GENERATE_NORMALS_INWARD, app );v
// start wiring everything together,
// add the Sphere to its parent BranchGroup.
bgGeometry.addChild( sphere );

// assign the BranchGroup to the Background as geometry.
back.setGeometry( bgGeometry );

// add the Background node to its parent BranchGroup.
backgroundGroup.addChild( back );

return backgroundGroup;
}

/*
* Create a behavior to move child nodes along the X-axis.
* The behavior is added to the BranchGroup bg, whereas
* any nodes added to the returned TransformGroup will be
* effected by the behavior.
*/
public TransformGroup createBehaviors( BranchGroup bg )
{
// create a TransformGroup.
//
// A TransformGroup is a Group node (can have children)
// and contains a Transform3D member.
//
// The Transform3D member contains a 4x4 transformation matrix
// that is applied during rendering to all the TransformGroup's
// child nodes. The 4x4 matrix can describe:
// scaling, translation and rotation in one neat package!

// enable the TRANSFORM_WRITE capability so that
// our behavior code can modify it at runtime.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

// create a new Transform3D that will describe
// the direction we want to move.
Transform3D xAxis = new Transform3D();

// create an Alpha object.
// The Alpha object describes a function against time.
// The Alpha will output a value that ranges between 0 and 1
// using the time parameters (in milliseconds).
Alpha xAlpha = new Alpha( -1,
Alpha.DECREASING_ENABLE |
Alpha.INCREASING_ENABLE,
1000,
1000,
5000,
1000,
1000,
10000,
2000,
4000 );

// create a PositionInterpolator.
// The PositionInterpolator will modify the translation components
// of a TransformGroup's Transform3D (objTrans) based on the output
// from the Alpha. In this case the movement will range from
// -0.8 along the X-axis with Alpha=0 to X=0.8 when Alpha=1.
PositionInterpolator posInt = new PositionInterpolator( xAlpha,
objTrans,
xAxis, -0.8f, 0.8f );

// set the range of influence of the PositionInterpolator
posInt.setSchedulingBounds( getBoundingSphere() );

// wire the PositionInterpolator into its parent
// TransformGroup. Just like rendering nodes behaviors
// must be added to the scenegraph.
objTrans.addChild( posInt );

// add the TransformGroup to its parent BranchGroup
bg.addChild( objTrans );

// we return the TransformGroup with the
// behavior attached so that we can add nodes to it
// (which will be effected by the PositionInterpolator).
return objTrans;
}

/*
* Return a BoundingSphere that describes the
* volume of the scene.
*/
BoundingSphere getBoundingSphere()
{
return new BoundingSphere( new Point3d(0.0,0.0,0.0), 200.0 );
}

/*
* main entry point for the Application.
*/
public static void main(String[] args)
{
SimpleTest simpleTest = new SimpleTest();
}
}

3.3 Exercises for the reader

When you run the example, I would encourage you to make some changes and see their effects. For example:

Colors and lighting

See how the color of the Material and the color of the directional light interact to produce the actual rendered color. Sophisticated lighting equations are at work to combine the effects of both at runtime. Try changing the shininess parameter to (80.0f) to increase or decrease the apparent shininess of the smaller Sphere.

Try removing the setMaterial call and see how rendering is affected.

Animation parameters

The Alpha class that is used to control the PositionInterpolator can be parameterized using nine variables (figure 3.2) to produce a sophisticated timing function.

Figure 3.2

Figure 3.2 The phases of the Alpha class: trigger time (1), phase delay (2), increasing alpha (3), increasing alpha ramp (4), at one (5), decreasing alpha (6), decreasing alpha ramp (7), at zero (8), loop count (9)

I’ll discuss the Alpha class in depth in chapter 12 (Interpolators), but, for now, try changing some of the Alpha parameters and noting the effects.

The axis that the PositionInterpolator is moving along can also be easily modified. For example try inserting the line:

  xAxis.rotY( 1.2 );

This will move the Sphere along a trajectory more perpendicular to the screen. You can experiment with calls to rotX and rotZ as well. Remember that rotations are described using radians, not degrees.

Background geometry

Try removing the background Sphere, or just remove the texture that was applied to the inside of the Sphere. What happens if you remove the ORed flag Primitive.GENERATE_NORMALS_INWARD when the background Sphere is created?

What happens if you remove the Primitive.GENERATE_TEXTURE_COORDS flag from the Sphere when it is created?

Load the background image into a graphics editor and experiment by modifying it. Can you see how the rectangular image was applied to the inner surface of the background Sphere?

Scheduling bounds

Experiment by changing the size of the BoundingSphere that describes the application’s volume. What effect does it have on the PositionInterpolator and the background?

Capability bits

What happens when you remove the call to:

   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

The position of the viewer of the scene

Try removing the call to:

   u.getViewingPlatform().setNominalViewingTransform();

Size of sphere primitives

Try changing the sizes of the Sphere used for background geometry as well as the smaller Sphere in the scene.

3.4 Summary

This example has rather plunged you in at the deep-end of the Java 3D pool. I hope you have enjoyed the guided tour of some of the capabilities of Java 3D. In the chapters to come we will be picking apart many of the features listed and showing you how to get the most out of them for your Java 3D application.

Do not be too concerned if the example code presented in this chapter looks very intimidating, and the descriptions were too vague. You can refer to this example as topics such as scenegraphs, geometry, and appearances are explained in detail in later chapters. This example should have given you a sense of the power of Java 3D and can serve as a good test bed for experimenting with your own applications or trying out the ideas presented in later chapters.

Before you start designing your application, it is important that you understand the data structure that underlies Java 3D rendering—the scenegraph. Once you have a firm grasp of it you will quickly be assembling complex Java 3D functionality that does not suffer from performance problems and is easily reusable.

So now, on to one of the most important concepts in Java 3D—the scenegraph.

[previous]  |  [main]  |  [next]