Java3D is part of the Java Media APIs and enables Java programmers to develop large interactive 3D universes. Java3D incorporates some of the best features of popular 3D programming toolkits, including Direct3D, OpenGL, and QuickDraw3D. The APIs also include support for 3D sound functionality.
In this article, I will describe how to build a simple Java3D program. Many of the gruesome details of Java3D programming will be deferred for a later article. For now, we just want to have fun.
The Java3D software development kit (SDK) and Java Development Kit (JDK) 1.2beta4 must be installed on your system before you can create 3D programs in Java. The most recent JDK implementation can be obtained from the Java Developer Connection (JDC) website. You will have to join the JDC before you can obtain either the JDK 1.2beta4 or the Java3D SDK. Registration is free. Check out http://www.javasoft.com/products for JDK 1.2beta4 download information. The latest Java3D implementation, currently version 1.1 Beta 1, can be downloaded from http://www.javasoft.com/products/java-media/3D.
The Java3D implementation that I use is built on top of Direct3D. Since this release is still in beta, there will undoubtedly be a few bugs. Also, each separate implementation will demonstrate quirks that cannot be duplicated in other implementations (e.g., OpenGL versus Direct3D). Bug reports are posted periodically at the Java Developer Connection Java3D page.
A Java3D program file has the same attributes as any other Java application. No new syntax or file naming scheme must be learned. The source code for the example used in this article can be found at the bottom of the page. Copy the source to a file called Simple3D.java. You will probably just copy-and-paste, but I find it easier to learn a new programming paradigm by actually rewriting program examples in my own particular style.
This is an essential step in any Java program. Besides a few standard Java classes, a Java3D program must import class files that enable the creation of 3D universes. Table 1 lists and describes the classes required for Simple3D.java.
|
Class |
Package |
Description |
|
MainFrame |
com.sun.j3d.utils.applet |
Convenience class that enables a Java3D applet to be run as an application. |
|
ColorCube |
com.sun.j3d.utils.geometry |
Convenience class used to create a multi-color cube. |
|
SimpleUniverse |
com.sun.j3d.utils.universe |
Convenience class used to create a simple Java3D universe that is ready for viewing. |
|
TextureLoader |
com.sun.j3d.utils.image |
Convenience class used to load textures. |
|
Applet |
java.applet |
Used to create Java applet programs. |
|
Frame |
java.awt |
Used to create a framed (windowed) Java application. |
|
BorderLayout |
java.awt |
Used to layout components in an AWT Container (such as a Frame or Applet). |
|
BranchGroup |
javax.media.j3d |
The root of a scene graph branch. |
|
Background |
javax.media.j3d |
The background color or image that fills the window of each new rendering frame. |
|
Canvas3D |
javax.media.j3d |
An extension of java.awt.Canvas that enables basic 3D rendering capabilities. |
|
TransformGroup |
javax.media.j3d |
A group node that contains a transform. |
|
Point3d |
javax.media.j3d |
Double precision floating point x, y, z coordinates. |
|
Transform3D |
javax.media.j3d |
An abstraction that encapsulates a row-major, 4x4 double precision floating point matrix. Used for rotations, translations, and other transformations. |
Table 1: Classes Imported into Simple3D.java
The code for importing the classes presented in Table 1 is shown in Figure 1 below.
import java.applet.Applet;
import java.awt.Frame;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Background;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Point3D;
Figure 1: Import statements for Simple3D.java
Java3D renders scenes on a special component called javax.media.j3d.Canvas3D. This component is an extension of the Abstract Window Toolkit (AWT) java.awt.Canvas class. While Canvas3D is also extensible, it is sufficient for most 3D applications.
Only a few lines of code are required to create a Canvas3D component and add it to a container. Figure 2 shows the code in Simple3D.java used to create a 3D canvas.
public void layoutComponents() {
setLayout(new BorderLayout());
canvas3D = new Canvas3D(null);
add("Center", canvas3D);
}
Figure 2: Container Layout and Canvas3D Creation Code
The first line in the method simply sets the layout so that components are added to specific regions of the container (e.g., "East", "West", or "Center"). The second line creates the Canvas3D object. There is only one constructor method in Canvas3D, and that method requires a java.awt.GraphicsConfiguration object as a parameter. GraphicsConfiguration is an abstract class that encapsulates information about a particular graphics device. On a PC, for example, a GraphicsConfiguration object might represent a particular screen mode. In our example, the GraphicsConfiguration is set to null, so the default device configuration is used. The last line simply adds the 3D canvas to the current container.
In general, Java3D uses a scene graph for rendering purposes (as you may find out in your perusal of the Java3D documentation, this is not always true, e.g., immediate mode does not require the use of a scene graph). A scene graph is a tree structure that contains Java3D nodes. Each node connection represents a parent-child relationship. A scene graph is constructed in such a way that state information cannot be shared among subgraphs. This enables Java3D to render scenes concurrently.
In our example, the creation of the scene graph requires the following substeps:
The next few sections explain the scene graph creation process in detail.
A BranchGroup object represents the root of a particular scene graph. The BranchGroup object is created in Simple3D.java in the createSceneGraph method as follows:
sceneGraph = BranchGroup();
Once the root of a scene graph has been defined, a scene graph can be constructed and readied for rendering.
By default, the background of our 3D scene is null and void (black). Just to make the scene a little more intersting, we will add an image to the background. The createBackground() method in Simple3D.java contains the code required to add a background image to the scene. This code is shown in Figure 3.
public void createBackground() {
BoundingSphere boundingSphere =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
TextureLoader backgroundTexture =
new TextureLoader(backgroundImage, this);
Background background =
new Background(backgroundTexture.getImage());
background.setApplicationBounds(boundingSphere);
sceneGraph.addChild(background);
}
Figure 3: Code to Add a Background to a Scene
The first object created is the javax.media.j3d.BoundingSphere. The BoundingSphere object specifies the spherical region in which the background will be active. There are two parameters: The sphere center location and the sphere radius. In Simple3D.java, the BoundingSphere is located at x, y, z coordinates 0.0, 0.0, 0.0, respectively, and has a radius of 100.0 meters. Java3D uses the right hand coordinate system, meaning that the x-axis points toward the right, the y axis points up, and the z-axis points out of the screen. In Java3D, the meter is the default unit of measurement.
The com.sun.j3d.utils.image.TextureLoader object is used to load the background image specified by the user at start-up. The specification for the TextureLoader constructor is as follows:
TextureLoader(java.lang.String fname, java.awt.Component
observer)
Where fname is the name of the file that contains the texture, and observer is the image observer. An image observer receives asynchronous image update notifications during image construction.
The background is constructed with a javax.media.j3d.Background object. The Background constructor expects a javax.media.j3d.ImageComponent2D object. The ImageComponent2D class represents a 2D image and can be applied to 3D objects in addition to backgrounds. The ImageComponent2D object is obtained by calling the TextureLoader.getImage() method.
Once the background image has been obtained, the application bounds of the background is set with the Background.setApplicationBounds() method. This is necessary to specify the region in which the texture is to be applied.
Finally, the background is added to the scene graph with the BranchGroup.addChild() method, which is inherited from javax.media.j3d.Group.
A 3D transformation is the movement of a 3D point from one location to another. Rotations, scales, translations, and reflections are all examples of transformations. In Java3D, a 3D transformation is represented by the javax.media.j3d.Transform3D class. The scene graph group node that contains a Transform3D object is an instance of javax.media.j3d.TransformGroup.
The transformation performed in Simple3D.java simply rotates the cube a specified number of degrees about the x axis. The code required to perform this transformation is shown in Figure 4.
public void createTransformation() {
// Create a 3D transformation that will
// rotate the cube a specified number of degrees on the x axis
Transform3D transform3D = new Transform3D();
transform3D.rotX(xRotationAngle);
// Create the transform group node and add the transformation
// to the node. Add the transformation group to the scene graph.
TransformGroup transformGroup = new TransformGroup(transform3D);
sceneGraph.addChild(transformGroup);
// Add a colored cube to the transform group node.
transformGroup.addChild(new ColorCube(cubeScale));
}
Figure 4: Code to Perform a 3D Transformation
The first two lines of code create a Java3D transformation that will be used to rotate a 3D object about the x-axis. The method used to perform the rotation, javax.media.j3d.Transform3D.rotX(), accepts a single double precision floating point number that specifies the angle in radians. The next two lines creates a transform group node with the specified 3D transformation. Once this group node has been properly initialized, it is added to the scene graph.
The last line of code simply adds the colored cube to the transform group node. The transform group node now has two children: the 3D transformation object and a colored cube.
After the scene graph has been constructed, it can be compiled. This is done by simply calling the BranchGroup.compile() method.
Now that you have a scene graph, you are ready to create a Java3D universe. In more sophisticated 3D application, this may require some complex steps. However, since our scene is very simple, we will use the convenience classes provided by Sun. In Simple3D.java, the createSimpleUniverse() method is used to construct the universe and add it to the scene graph. The code required to do this is shown in Figure 5.
public void createSimpleUniverse() {
SimpleUniverse simpleUniverse = new SimpleUniverse(canvas3D);
simpleUniverse.getViewingPlatform().setNominalViewingTransform();
simpleUniverse.addBranchGraph(sceneGraph);
}
This segment of source code readies the scene graph for rendering. First, a javax.java-media.j3d.SimpleUniverse object is created. This constructor method accepts a Canvas3D object that will be used for rendering. Next, the nominal viewing distance is set by calling the com.sun.j3d.utils.universe.ViewingPlatform.setNominalViewingTransform() method. Finally, the scene graph is added to the universe by calling the com.sun.j3d.utils.universe.SimpleUniverse.addBranchGraph() method.
That's all there is to our Simple3D.java application. To compile the program, use JDK1.2beta 4 javac, as illustrated below:
javac Simple3D.java
The main method simply retrieves any arguments passed from the command line and creates a Simple3D object. Table 2 shows the valid command line arguments and their semantics.
|
Argument |
Java Type |
Description |
|
background |
java.lang.String |
Image to be used as a background. |
|
scale |
double |
Used to set how much the object is to be scaled. |
|
degrees |
double |
The angle to rotate the cube on the xaxis. This angle is converted to radians in the Simple3D constructor. |
Table 2: Command Line Arguments Accepted by Simple3D.java
Use the standard JDK1.2beta4 java program to run the application, like so:
java Simple3D -background m18.jpg -scale 0.5 -degrees 65.5
All command line arguments except background are optional.
Explore and Enjoy!
/*
* Simple3D.java
*
* Author: George Crawford III
*
* Permission to use and modify this source code is hereby granted provided
* the author's name is included in all modified versions of this software.
*/
import java.applet.Applet;
import java.awt.Frame;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Background;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Point3d;
public class Simple3D extends Applet {
public void createSimpleUniverse() {
SimpleUniverse simpleUniverse = new SimpleUniverse(canvas3D);
simpleUniverse.getViewingPlatform().setNominalViewingTransform();
simpleUniverse.addBranchGraph(sceneGraph);
}
public void createBackground() {
BoundingSphere boundingSphere =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
TextureLoader backgroundTexture =
new TextureLoader(backgroundImage, this);
Background background =
new Background(backgroundTexture.getImage());
background.setApplicationBounds(boundingSphere);
sceneGraph.addChild(background);
}
public void createSceneGraph() {
// Create the root of the scene graph
sceneGraph = new BranchGroup();
// Create the background for the scene and add it to the scene graph
createBackground();
// Create a 3D transformation and add it to the scene graph.
createTransformation();
// Allow Java3D to optimize the scene graph.
sceneGraph.compile();
}
public void createTransformation() {
// Create a 3D transformation that will
// rotate the cube a specified number of degrees on the x axis
Transform3D transform3D = new Transform3D();
transform3D.rotX(xRotationAngle);
// Create the transform group node and add the transformation
// to the node. Add the transformation group to the scene graph.
TransformGroup transformGroup = new TransformGroup(transform3D);
sceneGraph.addChild(transformGroup);
// Add a colored cube to the transform group node.
transformGroup.addChild(new ColorCube(cubeScale));
}
public void layoutComponents() {
setLayout(new BorderLayout());
canvas3D = new Canvas3D(null);
add("Center", canvas3D);
}
public Simple3D(String background, double scale, double xAngle) {
backgroundImage = background;
cubeScale = scale;
xRotationAngle = (Math.PI/180)*xAngle; // Convert to radians
layoutComponents();
createSceneGraph();
createSimpleUniverse();
}
public static void main(String[] args) {
String backgroundImage = "m18.jpg";
double scale = 0.5;
double xAngle = 90.0;
if(args.length 0)
try {
for(int i = 0; i < args.length; i++)
if(args[i].equalsIgnoreCase("-background"))
backgroundImage = args[++i];
else if(args[i].equalsIgnoreCase("-scale"))
scale = Double.valueOf(args[++i]).doubleValue();
else if(args[i].equalsIgnoreCase("-xAngle"))
xAngle = Double.valueOf(args[++i]).doubleValue();
} catch(ArrayIndexOutOfBoundsException exception) {
handleException();
} catch(NumberFormatException exception) {
handleException();
}
Frame frame = new MainFrame(new Simple3D(backgroundImage,
scale, xAngle),
300, 300);
}
public static void handleException() {
System.err.print("Usage: java Simple3D -background [image] ");
System.err.println("<-scale [scale]> <-xAngle [degrees]>");
System.err.println("Where: ");
System.err.print("\t[image] is the image to display in the");
System.err.println("background");
System.err.println("\t[scale] is the size of the bounding sphere");
System.err.print("\t[degrees] is the angle in degrees ");
System.err.println("for axis rotation");
System.exit(0);
}
private String backgroundImage; // Background image filename
private double cubeScale; // Used to scale the 3D cube
private double xRotationAngle; // The rotation angle in degrees
private Canvas3D canvas3D; // Used to render the 3D scene
private BranchGroup sceneGraph; // Contains 3D scene information
}
Copyright © 1998 by George Crawford III
Biography: George Crawford III is a software engineer at MPI Software Technology, Inc. and completing his M.S. degree in computer science at Mississippi State University. His technical areas of interest include software design, distributed object technology, Java programming, games programming, and DirectX/Direct3D programming.
Copyright 1998 George Crawford III
Want to learn more about Java? You can go to an index or the next installment or the previous installment of this series.
Last Modified:
Location: www.acm.org/crossroads/xrds5-2/ovp52.html