What is Java 3D? It is a hierarchy of Java classes.

What is it for? It serves as the interface to a sophisticated system.

A system for what? Three-dimensional graphics rendering and sound rendering system.

Sound too? Yes, but we won't worry too much about that today.

Fair enough. Using Java 3D the programmer works with high-level constructs for creating and manipulating 3D geometric objects.

Oh? And these geometric objects, where are they located? They reside in a virtual universe, which is then rendered.

So, a Java 3D program:
  • creates instances of Java 3D objects and
  • places them into a scene graph data structure.
Yes, that's how it's called, scene graph. The scene graph is an arrangement of 3D objects in a tree structure that completely specifies the content of a virtual universe, and how it is to be rendered.

Can I see some code? No, it's too early for that.

Can I see a scene graph, then? You want to cut to the chase, don't you?

Well, since you said it's such an important concept. It is. Here's a basic diagram:

Holy guacamole! What is that? An example of a simple scene graph.


Any Java 3D virtual universe is created from a scene graph. That is correct. Since you said the basic diagram looks so complicated, let's see if we can simplify it further.

OK. A common definition of a graph is a data structure composed of nodes and arcs. A node is a data element, and an arc is a relationship between data elements. That's exactly it: the nodes in the scene graph are instances of Java 3D classes. The arcs represent the two kinds of relationships between Java 3D instances: parent-child, and reference.

Ah! I found class VirtualUniverse. Each scene graph has a single VirtualUniverse.

That's why it shows as the root of the tree? Yes. The VirtualUniverse object has a list of Locale objects.

Ah, so the solid arrow symbol represents a parent-child relationship: a parent can have any number of children, but a child can have only one parent. Like amoebas? You are correct. Now, a Locale object provides a reference point in the virtual universe.

Hm... Sounds like a content pane to me. Okay... Think of a Locale object as being a landmark used to determine the location of visual objects in the virtual universe.

Most Java 3D programs have only one Locale object. Yes, and let's not get into those details now.

Next I see BranchGroup nodes. A BranchGroup object is the root of a subgraph, or branch graph.

How many different categories of scene subgraphs are there? Only two:
  • the view branch graph category, and
  • the content branch graph category.

I bet the content branch graph specifies the contents of the virtual universe. You are correct, and by that (i.e., contents) we mean: geometry, appearance, behaviour, location, sound, and lights.

The view branch graph specifies the viewing parameters, such as the viewing location and direction. You got it. Together, the two branches specify much of the work the renderer has to do.

OK, I get the idea. Can we see some code now? Still early. How about some pseudocode?

Is there a general recipe for writing a Java 3D program? That's exactly what I was going to suggest we look at.

The basic outline consists of seven steps, as follows:

  1. Create a Canvas3D object.
  2. Create a VirtualUniverse object.
  3. Create a Locale object, attaching it to the VirtualUniverse object.
  4. Construct a view branch graph:
  5. Construct content branch graph(s)
  6. Compile branch graph(s)
  7. Insert subgraphs into the Locale

Ugh... Can we simplify this even further? Fortunately, yes. Java 3D programs written using the basic recipe have view branch graphs with identical structure.

This reminds me of adapter classes. Your intuition is right: the regularity of view branch graph structure is also found in the SimpleUniverse class. Instances of this class perform steps 2, 3 and 4 from the basic recipe. Using the SimpleUniverse class in Java 3D programming significantly reduces the time and effort needed to create the view branch graph.

Consequently, the programmer has more time to concentrate on the content. This is what writing a Java 3D program is about.

Is there a drawback to using this class every time? Using a SimpleUniverse object makes the basic recipe even easier:
  1. Create a Canvas3D object.
  2. Create a SimpleUniverse object which references the earlier Canvas3D object.
    • Customize the SimpleUniverse object.
  3. Construct content branch graph
  4. Compile branch graph
  5. Insert subgraphs into the Locale of the SimpleUniverse object.

But is there a drawback to using it all the time? This class is a good starting point for Java 3D programming because it allows the programmer to ignore the view branch graph.

However, using it does not allow having multiple views of the virtual universe. That is correct. You must have also noticed that it belongs to a different package.

It is part of com.sun.j3d.utils, which contains the utility classes. The core classes are in javax.media.j3d. Other packages used are java.awt, and javax.vecmath. Let's wrap this part up now.

OK. What can you say about this picture:

A SimpleUniverse object provides a minimal virtual universe, and supplies the functionality of all of the objects inside the dashed blue line. The packages in the API are listed here. The SimpleUniverse object creates a complete view branch graph for a virtual universe. The view branch graph includes an image plate. An image plate is the conceptual rectangle where the content is projected to form the rendered image. The Canvas3D object, which provides an image in a window on your computer display, can be thought of as the image plate.


OK, I get an idea: from here on we can concentrate on creating content. Yes, and I can finally show you some code:
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.ColorCube;
import javax.media.j3d.*;
import javax.vecmath.*;

//   HelloJava3Da renders a single, rotating cube.

public class HelloJava3Da extends Applet {
    public HelloJava3Da() {

        setLayout(new BorderLayout());

        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of HelloJava3Da (constructor) 

     public BranchGroup createSceneGraph() {
        // Create the root of the branch graph
        BranchGroup objRoot = new BranchGroup();

        objRoot.addChild(new ColorCube(0.4));

        return objRoot;
    } // end of CreateSceneGraph method of HelloJava3Da

    //  The following allows this to be run as an application
    //  as well as an applet

    public static void main(String[] args) {
        Frame frame = new MainFrame(new HelloJava3Da(), 256, 256);
    } // end of main (method of HelloJava3Da) 

} // end of class HelloJava3Da  
Heres a diagram to go with it:


I can see the cube, but its position does not make for a very impressive rendering. Let's change the code a little bit, then.

The content branch is now a bit more complex.

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

//   HelloJava3Db renders a single, rotated cube.

public class HelloJava3Db extends Applet {
    public BranchGroup createSceneGraph() {
        // Create the root of the branch graph
        BranchGroup objRoot = new BranchGroup();

        // rotate object has composited transformation matrix
        Transform3D rotate = new Transform3D();
        Transform3D tempRotate = new Transform3D();

        rotate.rotX(Math.PI/4.0d);
        tempRotate.rotY(Math.PI/5.0d);
        rotate.mul(tempRotate);

        TransformGroup objRotate = new TransformGroup(rotate);
        objRoot.addChild(objRotate);        
        objRotate.addChild(new ColorCube(0.4));
        // Let Java 3D perform optimizations on this scene graph.
        objRoot.compile();

        return objRoot;
    } // end of CreateSceneGraph method of HelloJava3Db 

    // Create a simple scene and attach it to the virtual universe 

    public HelloJava3Db() {
        setLayout(new BorderLayout());
        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class 
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the 
        // objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of HelloJava3Db (constructor) 
    //  The following allows this to be run as an application 
    //  as well as an applet 

    public static void main(String[] args) {
        Frame frame = new MainFrame(new HelloJava3Db(), 256, 256);
    } // end of main (method of HelloJava3Db)

} // end of class HelloJava3Db
As a matter of fact a better representation would be:


I could come up with a variation on this code:
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

//   HelloJava3Dbalt renders a single, rotated cube.

public class HelloJava3Dbalt extends Applet {
    public BranchGroup createSceneGraph() {
        // Create the root of the branch graph
        BranchGroup objRoot = new BranchGroup();

        // rotate object has composited transformation matrix
        Transform3D rotate = new Transform3D();
        Transform3D tempRotate = new Transform3D();

        rotate.rotX(Math.PI/4.0d);
        tempRotate.rotY(Math.PI/5.0d);
        tempRotate.mul(rotate);

        TransformGroup objRotate = new TransformGroup(tempRotate);

        objRoot.addChild(objRotate);
        objRotate.addChild(new ColorCube(0.4));
        // Let Java 3D perform optimizations on this scene graph.
        objRoot.compile();

        return objRoot;
    } // end of CreateSceneGraph method of HelloJava3Dbalt

    // Create a simple scene and attach it to the virtual universe

    public HelloJava3Dbalt() {
        setLayout(new BorderLayout());
        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of HelloJava3Db (constructor)
    //  The following allows this to be run as an application
    //  as well as an applet

    public static void main(String[] args) {
        Frame frame = new MainFrame(new HelloJava3Dbalt(), 256, 256);
    } // end of main (method of HelloJava3Dbalt)

} // end of class HelloJava3Dbalt
Not a huge difference.

I agree. Show me more code. Here's stage three of our example:

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

//   HelloJava3Dc renders a single, rotating cube.

public class HelloJava3Dc extends Applet {

 public BranchGroup createSceneGraph() {
         // Create the root of the branch graph 
         BranchGroup objRoot = new BranchGroup();

         // Create the transform group node and initialize it to the 
         // identity. Add it to the root of the subgraph. 
         TransformGroup objSpin = new TransformGroup();
         objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
         objRoot.addChild(objSpin);

         // Create a simple shape leaf node, add it to the scene graph. 
         // ColorCube is a Convenience Utility class 
         objSpin.addChild(new ColorCube(0.4));

         // Create a new Behavior object that will perform the desired 
         // operation on the specified transform object and add it into 
         // the scene graph. 
         Alpha rotationAlpha = new Alpha(-1, 4000);

         RotationInterpolator rotator =
                 new RotationInterpolator(rotationAlpha, objSpin);

         // a bounding sphere specifies a region a behavior is active 
         // create a sphere centered at the origin with radius of 100 
         BoundingSphere bounds = new BoundingSphere();
         rotator.setSchedulingBounds(bounds);
         objSpin.addChild(rotator);

         return objRoot;
     } // end of CreateSceneGraph method 


     public HelloJava3Dc() {
        setLayout(new BorderLayout());
        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class 
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the 
        // objects in the scene can be viewed. 
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of HelloJava3D (constructor) 

    //  The following allows this to be run as an application 
    //  as well as an applet 

    public static void main(String[] args) {
        Frame frame = new MainFrame(new HelloJava3Dc(), 256, 256);
    } // end of main (method of HelloJava3D) 

} // end of class HelloJava3Dc  

Now the cube is rotating. We can put together the examples we have seen thus far:

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

//   HelloJava3Dc renders a single, rotating cube.

public class HelloJava3Dd extends Applet {

     public BranchGroup createSceneGraph() {
        // Create the root of the branch graph
        BranchGroup objRoot = new BranchGroup();

        // rotate object has composited transformation matrix
        Transform3D rotate = new Transform3D();
        Transform3D tempRotate = new Transform3D();

        rotate.rotX(Math.PI/4.0d);
        tempRotate.rotY(Math.PI/5.0d);
        rotate.mul(tempRotate);

        TransformGroup objRotate = new TransformGroup(rotate);

        // Create the transform group node and initialize it to the
        // identity.  Enable the TRANSFORM_WRITE capability so that
        // our behavior code can modify it at runtime.  Add it to the
        // root of the subgraph.
        TransformGroup objSpin = new TransformGroup();
        objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        objRoot.addChild(objRotate);
        objRotate.addChild(objSpin);

        // Create a simple shape leaf node, add it to the scene graph.
        // ColorCube is a Convenience Utility class
        objSpin.addChild(new ColorCube(0.4));

        // Create a new Behavior object that will perform the desired
        // operation on the specified transform object and add it into
        // the scene graph.
        Transform3D yAxis = new Transform3D();
        Alpha rotationAlpha = new Alpha(-1, 4000);

        RotationInterpolator rotator =
            new RotationInterpolator(rotationAlpha, objSpin, yAxis,
                                     0.0f, (float) Math.PI*2.0f);

        // a bounding sphere specifies a region a behavior is active
        // create a sphere centered at the origin with radius of 1
        BoundingSphere bounds = new BoundingSphere();
        rotator.setSchedulingBounds(bounds);
        objSpin.addChild(rotator);

        return objRoot;
    } // end of CreateSceneGraph method of HelloJava3Dd

    public HelloJava3Dd() {
        setLayout(new BorderLayout());
        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();
        scene.compile();

        // SimpleUniverse is a Convenience Utility class
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
   } // end of HelloJava3Dd (constructor)

    //  The following allows this to be run as an application
    //  as well as an applet

    public static void main(String[] args) {
        Frame frame = new MainFrame(new HelloJava3Dd(), 256, 256);
    } // end of main (method of HelloJava3D)

} // end of class HelloJava3Dd
This concludes the first chapter. Here's some bibliography.

Here's a follow-up document. And the goal of this tutorial.

Still ahead. Sure. The stages need links, comments and explanations.

Last updated by: dgerman@indiana.edu on Fri Nov 11 12:22:44 EST 2005 for A202/I211 and JETT 2005.

Appendix.

Here's what's required in order to work with an object other than a ColorCube:

/* 
 * Getting Started with the Java 3D API
 * written in Java 3D
 *
 * This program demonstrates:
 *   1. writing a visual object class
 *      In this program, Yoyo class defines a visual object
 *      by extneding Shape3D.
 *   2. Using TriangleFanArray to create surfaces.
 *      Four TriangleFan array surfaces are contained in an instance of Yoyo.
 *      In addition, the color for the Yoyo is set in its geometry.
 */

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame; 
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;


public class ColorYoyoApp extends Applet {

    /////////////////////////////////////////////////
    //
    // create scene graph branch group
    //
    public class Yoyo extends Shape3D{

	////////////////////////////////////////////
	//
	// create Shape3D with geometry and appearance
        // the geometry is created in method yoyoGeometry
        // the appearance is created in method yoyoAppearance
	//
	public Yoyo() {

                this.setGeometry(yoyoGeometry());
	    
	} // end of Yoyo constructor

	////////////////////////////////////////////
	//
	// create yoyo geometry
        // four triangle fans represent the yoyo
        // strip   indicies_______________
        //   0     0N+0 to 1N+0 ( 0 to N )
        //   1     1N+1 to 2N+1
        //   2     2N+2 to 3N+2
        //   3     3N+4 to 4N+3
	//
	private Geometry yoyoGeometry() {

                TriangleFanArray tfa;
                int     N = 17;
                int     totalN = 4*(N+1);
                Point3f coords[] = new Point3f[totalN];
		Color3f colors[] = new Color3f[totalN];
		Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
		Color3f yellow = new Color3f(0.7f, 0.5f, 0.0f);
                int     stripCounts[] = {N+1, N+1, N+1, N+1};
                float   r = 0.6f;
                float   w = 0.4f;
                int     n;
                double  a;
                float   x, y;

                // set the central points for four triangle fan strips
                coords[0*(N+1)] = new Point3f(0.0f, 0.0f, w);
                coords[1*(N+1)] = new Point3f(0.0f, 0.0f, 0.0f);
                coords[2*(N+1)] = new Point3f(0.0f, 0.0f, 0.0f);
                coords[3*(N+1)] = new Point3f(0.0f, 0.0f, -w);

		colors[0*(N+1)] = red;
		colors[1*(N+1)] = yellow;
		colors[2*(N+1)] = yellow;
		colors[3*(N+1)] = red;

                for(a = 0,n = 0; n < N; a = 2.0*Math.PI/(N-1) * ++n){
                        x = (float) (r * Math.cos(a));
                        y = (float) (r * Math.sin(a));
                        coords[0*(N+1)+n+1] = new Point3f(x, y, w);
                        coords[1*(N+1)+N-n] = new Point3f(x, y, w);
                        coords[2*(N+1)+n+1] = new Point3f(x, y, -w);
                        coords[3*(N+1)+N-n] = new Point3f(x, y, -w);

			colors[0*(N+1)+N-n] = red;
			colors[1*(N+1)+n+1] = yellow;
			colors[2*(N+1)+N-n] = yellow;
			colors[3*(N+1)+n+1] = red;
                }

		tfa = new TriangleFanArray (totalN, 
					TriangleFanArray.COORDINATES |
					TriangleFanArray.COLOR_3,
					stripCounts);
		
                tfa.setCoordinates(0, coords);
		tfa.setColors(0,colors);

                return tfa;

	} // end of method yoyoGeometry in class Yoyo

    } // end of class Yoyo

    /////////////////////////////////////////////////
    //
    // create scene graph branch group
    //
    public BranchGroup createSceneGraph() {

	BranchGroup objRoot = new BranchGroup();

      // Create the transform group node and initialize it to the
      // identity.  Enable the TRANSFORM_WRITE capability so that
      // our behavior code can modify it at runtime.  Add it to the
      // root of the subgraph.
      TransformGroup objSpin = new TransformGroup();
      objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

      objRoot.addChild(objSpin);
 
        objSpin.addChild(new Yoyo());

      // Create a new Behavior object that will perform the desired
      // operation on the specified transform object and add it into
      // the scene graph.
      Transform3D yAxis = new Transform3D();
      Alpha rotationAlpha = new Alpha(-1, 4000);

      RotationInterpolator rotator =
          new RotationInterpolator(rotationAlpha, objSpin);
      BoundingSphere bounds =
          new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
      rotator.setSchedulingBounds(bounds);
      objSpin.addChild(rotator);

	// Let Java 3D perform optimizations on this scene graph.
        objRoot.compile();

	return objRoot;
    } // end of CreateSceneGraph method of ColorYoyoApp

    // Create a simple scene and attach it to the virtual universe

    public ColorYoyoApp() {
        setLayout(new BorderLayout());
        GraphicsConfiguration config =
           SimpleUniverse.getPreferredConfiguration();

        Canvas3D canvas3D = new Canvas3D(config);
        add("Center", canvas3D);

        BranchGroup scene = createSceneGraph();

        // SimpleUniverse is a Convenience Utility class
        SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

	// This will move the ViewPlatform back a bit so the
	// objects in the scene can be viewed.
        simpleU.getViewingPlatform().setNominalViewingTransform();

        simpleU.addBranchGraph(scene);
    } // end of ColorYoyoApp constructor

    //  The following allows this to be run as an application
    //  as well as an applet

    public static void main(String[] args) {
        Frame frame = new MainFrame(new ColorYoyoApp(), 256, 256);
    } // end of main method of ColorYoyoApp

} // end of class ColorYoyoApp