JavaCombining Rotation and Translation in Java 3d

Combining Rotation and Translation in Java 3d

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes # 1548


Preface

General

Fifth in a series of lessons

This is the fifth lesson in a series of lessons designed to start with Java 3D basics and work up to some very complicated programs, such as the program that I explained in the earlier lesson titled “Understanding Lighting in the Java 3D API” (see Resources).

The first lesson in this series was titled “Back to Basics in the Java 3D API” (see Resources). The previous lesson was titled “Understanding the Alpha Time-Base Class in Java 3D.” This lesson is titled “Combining Rotation and Translation in Java 3d.” My current plan is for future lessons to deal with user and object interaction as well as advanced animation and textures.

What you will learn

Understanding rotation in Java 3D is not too difficult. Similarly, it is not too difficult to understand translation in Java 3D. However, when you combine rotation with translation, things become very complicated very quickly. If you don’t really understand what you are doing when you combine the two, the chance that you will get it right the first time is probably less than fifty percent. In this lesson, I will teach you how to get it right the first time by helping you to understand the impact of the order of rotation and translation on the behavior of the program.

Compiling and running Java 3D programs

In order to compile and run programs using the Java 3D API, you will need to download and install the Java 3D API software. As of the date of this writing, version 1.5.0 is available for download.

In addition, you will need to download and install either Microsoft DirectX or OpenGL. All of the sample programs in this series of tutorials were developed and tested using Microsoft DirectX. They were not tested using OpenGL.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. A view of the universe along with the user input GUI.
  • Figure 2. Another screen shot of the program named Java3D008.
  • Figure 3. White sphere rotating about its own tilted vertical axis.
  • Figure 4. White sphere orbiting yellow sphere in a tilted plane.

Listings

  • Listing 1. Constructor for the top-level class.
  • Listing 2. Beginning of the class named TheScene.
  • Listing 3. Beginning of the constructor for the class named TheScene.
  • Listing 4. Beginning of code used to animate the universe.
  • Listing 5. Beginning of code to animate the white sphere.
  • Listing 6. The method named rotate.
  • Listing 7. The method named translate.
  • Listing 8. Code that is executed when the user clicks the “b” button.
  • Listing 9. Code that is executed when the user clicks the “c” button.
  • Listing 10. Complete the constructor for the class named TheScene.
  • Listing 11. Code that is executed when the user clicks the “a” button.
  • Listing 12. The method named tiltTheAxes.
  • Listing 13. Code that is executed when the user clicks the “b” button.
  • Listing 14. Code that is executed when the user clicks the “c” button.
  • Listing 15. Program listing for the program named Java3D008.
  • Listing 16. Program listing for the program named Java3D009.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

General background information

Rotation and translation are two of several different types of transforms that you can apply to objects in Java 3D, including:

  • scaling
  • translation
  • shear
  • rotation

I explained some of these transforms as they apply in the 2D world in the earlier lesson titled “Java 2D Graphics, Simple Affine Transforms” (see Resources). I will deal with translation and rotation in Java 3D in this lesson, and will deal with scaling and shear in future lessons.

Preview

The program named Java3D008

In this lesson, I will present and explain two programs. The first program is named Java3D008. The purpose of this program is to illustrate the behavior imparted by the order of rotation and translation when used together.

The universe

The universe contains a yellow sphere, a green sphere, and a white sphere as shown by the left image in Figure 1.

Figure 1. A view of the universe along with the user input GUI.

Figure 1 also shows the user input GUI on the right.

Behavior of the yellow sphere

The yellow sphere slowly rotates around the vertical axis in 3D space for a specified period of time. The center of the yellow sphere is at the origin in 3D space. Therefore, the yellow sphere appears to rotate around its own vertical axis.

Behavior of the green sphere

The green sphere is translated to a location slightly above, to the right of, and behind the yellow sphere. The green sphere maintains its position throughout the time that the program is running in order to provide a fixed object in 3D space to help you stay properly oriented.

Behavior of the white sphere

The behavior of the white sphere depends on user input via the input GUI shown in Figure 1. As you can see, the input GUI contains three buttons labeled “a”, “b”, and “c”. Depending on which button the user clicks, the white sphere is animated in one of three ways:

  1. If the user clicks the “a” button, the white sphere is translated to a location just outside the yellow sphere on the positive z-axis (0.0f,0.0f,0.7f) where it maintains its location in space and rotates about its own vertical axis. (This is the behavior depicted in Figure 1.) This behavior is the result of rotation followed by translation.
  2. If the user clicks the “b” button, the white sphere is translated to the same location in space as for the “a” button but then the white sphere orbits the yellow sphere with the same face of the white sphere always being oriented toward the yellow sphere. (See Figure 2.) The white sphere orbits in a horizontal plane that cuts through the equator of the yellow sphere. This is the result of translation followed by rotation.
  3. If the user clicks the “c” button, the white sphere orbits the yellow sphere as in Figure 2. However, for this case, the white sphere also rotates on its own vertical axis while orbiting the yellow sphere. This is the result of rotation followed by translation followed by another rotation.

Figure 2. Another screen shot of the program named Java3D008.

A slow computer
On my relatively slow laptop, the first animation cycle is perhaps 25-percent complete before the first image appears on the screen. I was unable to discover any way to prevent this from happening. In addition, there are sporadic undesirable pauses in the animation.

If you perform the rotations and translations in the wrong order, the results are not likely to be what you expected or wanted.

Program testing

Both of the programs that I will present and explain in this lesson were tested using Java SE 6, and Java 3D 1.5.0 running under Windows XP.

The program named Java3D009

The second program that I will present and explain is named Java3D009. This program is an update to the program named Java3D008. The purpose of this update is to illustrate one approach to using translation and rotation in combination to modify the rotational axis of objects in 3D space.

Three cases are presented

In one case, a white sphere spins around its own vertical axis that is tilted counter-clockwise as shown in Figure 3.

Figure 3. White sphere rotating about its own tilted vertical axis.

If you compare the detailed facet structure on the surface of the white sphere in Figure 3 with the sphere in Figure 1, you can see that the white sphere’s vertical axis is tilted counter-clockwise.

The two other cases

In the other two cases, the white sphere orbits around the yellow sphere on a plane that is tilted relative to the X-Y, Y-Z, and Z-X planes. This is shown in Figure 4.

Figure 4. White sphere orbiting yellow sphere in a tilted plane.

The planes on which the white spheres orbit are different in the two cases. For the case shown in Figure 4, the plane is tilted such that from its current position, the white sphere will move diagonally up to the right across the yellow sphere and will then block the view of the green sphere momentarily. Then it will disappear by dropping down behind the yellow sphere.

The universe

As shown in Figures 3 and 4, the universe once again contains a yellow sphere, a green sphere, and a white sphere. As before, the yellow sphere slowly rotates around the vertical axis in 3D space for a specified period of time.

Also as before, the green sphere is translated to the location shown in Figures 3 and 4 where it remains stationary throughout the time that the program is running.

The behavior of the white sphere

The behavior of the white sphere depends on user input via the input GUI that provides three buttons as shown in Figure 1. Depending on which button is clicked by the user, the white sphere is animated in one of three ways:

  1. If the user clicks the “a” button, the white sphere is translated to a location just outside the yellow sphere on the positive z-axis (0.0f,0.0f,0.7f) where it maintains its location in space and rotates about its own vertical axis. However, its vertical axis is tilted relative to the direction of the vertical axis of 3D space. This is the result of rotation followed by axis transformation followed by translation. This behavior is shown in Figure 3.
  2. If the user clicks the “b” button, the white sphere is translated to the same location in space as for the “a” button, but then it orbits the yellow sphere with the same face of the white sphere always oriented toward the yellow sphere. However, unlike in the previous program where the sphere orbits in a horizontal plane at the equator of the yellow sphere, the orbit in this program is on a plane that is tilted relative to the X-Y, Y-Z, and Z-X planes. This is the result of translation followed by rotation followed by axis transformation. This behavior is shown in Figure 4.
  3. If the user clicks the “c” button, the white sphere orbits the yellow sphere as in 2 above. However, for this case, the white sphere orbits on a different tilted plane and also rotates on its own vertical axis while orbiting the yellow sphere. As before, the orbit is on a plane that is tilted relative to the X-Y, Y-Z, and Z-X planes. This is the result of rotation followed by translation followed by another rotation followed by axis transformation.

Discussion and sample code

The program named Java3D008

A complete listing of this program is presented in Listing 15. As is my custom, I will present and explain this program in fragments.

Much of the code in this lesson is very similar or identical to code that I explained in the earlier lessons on Java 3D (see Resources). I won’t repeat those explanations here, but rather will assume that you have already studied the earlier lessons and that you understand that code. Once again, you can view that code in Listing 15.

Constructor for the top-level class

The program begins in Listing 1, which includes the main method and the constructor for the user input GUI shown in Figure 1.

Listing 1. Constructor for the top-level class.

public class Java3D008 extends Frame{
  TheScene theScene;

  public static void main(String[] args){
    Java3D008 thisObj = new Java3D008();
  }//end main
  //----------------------------------------------------//

  public Java3D008(){//top-level constructor
    setLayout(new GridLayout(1,3));
    Button aButton = new Button("a");
    Button bButton = new Button("b");
    Button cButton = new Button("c");

    add(aButton);
    add(bButton);
    add(cButton);

    aButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("a");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    bButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("b");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    cButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("c");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    setTitle("Copyright 2007, R.G.Baldwin");
    setBounds(236,0,235,75);
    setVisible(true);

    //This window listener is used to terminate the
    // program when the user clicks the X button.
    addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end new WindowAdapter
    );//end addWindowListener

  }//end constructor
  //----------------------------------------------------//

Depending on which button is clicked by the user, the action listeners registered on the buttons instantiate a new object of the class named TheScene, passing a string to the constructor for that class. As you will see later, the value of the string that is passed to the constructor determines the animation behavior of the objects in the universe. Otherwise, the code in Listing 1 is straightforward and shouldn’t require further explanation.

Beginning of the class named TheScene

Listing 2 shows the beginning of the class named TheScene. This is an inner class from which the universe is instantiated and animated.

Listing 2. Beginning of the class named TheScene.

  class TheScene extends Frame{

    //Declare instance variables that are used later by
    // the program.
    Canvas3D canvas3D;
    Sphere yellowSph;
    Sphere whiteSph;
    Sphere greenSph;
    PointLight pointLight;
    SimpleUniverse simpleUniverse;

    BranchGroup mainBranchGroup = new BranchGroup();

    TransformGroup greenTransformGroup = 
                                     new TransformGroup();
    TransformGroup yellowRotXformGroup = 
                                     new TransformGroup();

    String buttonLabel;

The code in Listing 2 simply declares instance variables that will be used later in the program, initializing some of them in the process.

Beginning of the constructor for the class named TheScene

The beginning of the constructor is shown in Listing 3.

Listing 3. Beginning of the constructor for the class named TheScene.

    TheScene(String input){//constructor
      //Save the incoming parameter to be used later to
      // decide which behavior to execute.
      buttonLabel = input;

      //Construct the universe.
      createACanvas();
      createTheUniverse();

      //Construct the objects that will occupy the
      // universe.
      createYellowSphere();
      createWhiteSphere();
      createGreenSphere();
      createPointLight();

In addition to saving the incoming parameter to use later in deciding which of three animation behaviors to execute, the code in Listing 3 calls a series of methods to construct the universe and to construct the objects that occupy the universe. The code in each of those methods is similar or identical to code that I have explained in earlier lessons on Java 3D (see Resources), so I won’t repeat those explanations here. You can view the code in those methods in Listing 15.

Animate the universe

Listing 4 shows the beginning of the code that is executed to animate the universe.

Listing 4. Beginning of code used to animate the universe.

      animateYellowSphere();

Listing 4 calls a method named animateYellowSphere, which controls the animation of the yellow sphere shown in Figures 1 and 2. The animation of the yellow sphere is independent of user input.

As you will see if you examine Listing 15, the animateYellowSphere method simply calls another method named rotate to cause the yellow sphere to spin around the vertical axis in 3D space. The rotate method is used extensively in this program and will be explained in detail later.

Beginning of code to animate the white sphere

The animation of the white sphere depends on the button selected by the user in the input GUI. Listing 5 shows the beginning of an if-else statement that executes one of three possible behaviors depending on the button selection.

Listing 5. Beginning of code to animate the white sphere.

      if(buttonLabel.equals("a")){
        //Begin rotation of white sphere.
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(4,2500));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        mainBranchGroup.addChild(whiteTransXformGroup);

The code in Listing 5 will rotate the white sphere around the vertical axis in 3D space and then translate the rotating sphere away from the origin in 3D space. This causes the white sphere to remain stationary while spinning around its own axis.

The rotate and translate methods

In an attempt to make the order of rotation and translation easier to see in the source code, I defined two new methods in this program named rotate and translate. As you can see, the code in Listing 5 calls the rotate method first followed by a call to the translate method. The value returned by the rotate method is passed as a parameter to the translate method. Thus, the behavior of the white sphere in this case is caused by rotation followed by translation.

The method named rotate

At this point, I will put the explanation of the constructor for the class named TheScene on the back burner for awhile and explain the methods named rotate and translate. The method named rotate is shown in its entirety in Listing 6.

Listing 6. The method named rotate.

    TransformGroup rotate(Node node,Alpha alpha){

      TransformGroup xformGroup = new TransformGroup();
      xformGroup.setCapability(
                    TransformGroup.ALLOW_TRANSFORM_WRITE);

      //Create an interpolator for rotating the node.
      RotationInterpolator interpolator = 
               new RotationInterpolator(alpha,xformGroup);

      //Establish the animation region for this
      // interpolator.
      interpolator.setSchedulingBounds(new BoundingSphere(
                           new Point3d(0.0,0.0,0.0),1.0));

      //Populate the xform group.
      xformGroup.addChild(interpolator);
      xformGroup.addChild(node);

      return xformGroup;

    }//end rotate

Not much that is new here

Except for packaging this code in a separate method and using an object of the class Node, there is nothing in Listing 6 that is new to this lesson. I explained all of the code in Listing 6 in the earlier lesson titled “Simple Animation with the Java 3D API” (see Resources).

Behavior of the method named rotate

Given an incoming Node object and an Alpha object, this method will construct and return a TransformGroup object that is designed to rotate the node around the vertical axis in 3D space according to the number of cycles and cycle time specified by the Alpha object.

A few words about the use of the Node class

In some cases, Java 3D code is written to rotate visual objects such as objects of the classes Sphere, Box, ColorCube, and Cone. In other cases, the code is written to rotate visual objects contained in objects of the class TransformGroup which have been populated with visual objects, or perhaps with other transform groups that have been populated with visual objects.

As it turns out, classes for visual objects such as the classes Sphere, Box, ColorCube, and Cone, as well as the TransformGroup class are all subclasses of the class Node. Therefore, objects of these classes can be treated as type Node.

The addChild method that is called in Listing 6 is defined in the Group class and inherited into the TransformGroup class. The method requires an incoming parameter of type Node. Therefore, the incoming parameter to the rotate method is eligible for being passed to the addChild method regardless of whether that object is actually type Sphere or type TransformGroup. As a result, this method can be used to rotate a visual object or a transform group equally well.

The method named translate

The method named translate is shown in its entirety in Listing 7.

Listing 7. The method named translate.

    TransformGroup translate(Node node,Vector3f vector){

        Transform3D transform3D = new Transform3D();
        transform3D.setTranslation(vector);
        TransformGroup transformGroup = 
                                     new TransformGroup();
        transformGroup.setTransform(transform3D);

        transformGroup.addChild(node);
        return transformGroup;
    }//end translate

Given an incoming node object and a vector object, the method named translate will construct and return a transform group designed to translate that node according to that vector. Once again, the code in Listing 7 is very similar to code that I explained in the earlier lesson titled “Simple Animation with the Java 3D API” (see Resources). Therefore I won’t repeat that explanation here. (Earlier comments related to the Node class and the addChild method apply here also.)

Code that is executed when the user clicks the “b” button

Returning now to the constructor, Listing 8 shows the code that is executed when the user clicks the button labeled “b” in Figure 1.

Listing 8. Code that is executed when the user clicks the “b” button.

      }else if(buttonLabel.equals("b")){
        //Translate the white sphere.
        TransformGroup whiteTransXformGroup = translate(
                            whiteSph,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of translated white sphere around
        // the vertical axis at the origin in 3D space.
        TransformGroup whiteRotXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        mainBranchGroup.addChild(whiteRotXformGroup);

This code will translate the white sphere and rotate the translated white sphere around the vertical axis in 3D space, causing the white sphere to orbit the yellow sphere with the same face of the white sphere oriented toward the yellow sphere at all times. This behavior was caused by translation followed by rotation.

The order of rotation and translation was reversed

If you compare the code in Listing 8 with the code in Listing 5, you will see that the order of rotation and translation was reversed in the two cases. In Listing 5, the rotate method was called passing the white sphere object as a parameter. Then the TransformGroup object returned by the rotate method was passed to the translate method.

In Listing 8, the white sphere was first passed as a parameter to the translate method. The TransformGroup object returned by the translate method was then passed as a parameter to the rotate method.

Radically different behavior

If you run this program selecting the button labeled “a” and then run it again selecting the button labeled “b”, you will see that the behavior is radically different between the two cases, illustrating the importance of the order of rotation and translation.

In the first case, the white sphere was caused to rotate around the vertical axis in 3D space, which coincided with the vertical axis of the sphere. Then the origin of the coordinate system belonging to the sphere was translated to a different location in 3D space, which had the effect of moving the rotating sphere to a different location.

In the second case, the origin of the coordinate system belonging to the white sphere was translated to a different location in 3D space. Then the white sphere was once again caused to rotate around the vertical axis in 3D space (not around its own vertical axis) causing the white sphere to orbit the yellow sphere whose origin was located at the origin in 3D space.

Code that is executed when the user clicks the “c” button

Listing 9 shows the code that is executed when the user clicks the button labeled “c” in Figure 1.

Listing 9. Code that is executed when the user clicks the “c” button.

      }else if(buttonLabel.equals("c")){
        //Begin rotation of the white sphere
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(8,1250));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of the translated rotating white
        // sphere.
        TransformGroup whiteRotGroupXformGroup =
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        mainBranchGroup.addChild(whiteRotGroupXformGroup);
      }//end if

Behavior of the code

The code in Listing 9 will rotate the white sphere, translate the rotating white sphere away from the origin in 3d space, and then rotate the translated rotating white sphere around the origin in 3D space causing the white sphere to orbit the yellow sphere and to spin about its own vertical axis at the same time. This behavior is rotation followed by translation followed by another rotation.

The first two statements in Listing 9 are the same as the two statements in Listing 5, which left the white sphere rotating in a stationary position in 3D space. However, Listing 9 adds another call to the rotation method (shown in Italics in Listing 9) causing the white sphere, (which is already rotating around its own vertical axis) to also rotate around the vertical axis in 3D space. This causes the rotating white sphere to appear to orbit the yellow sphere, which is located at the origin in 3D space.

Complete the constructor for the class named TheScene

Listing 10 contains the remaining code in the constructor for the class named TheScene.

Listing 10. Complete the constructor for the class named TheScene.

      //Finish populating the mainBranchGroup.
      mainBranchGroup.addChild(greenTransformGroup);
      mainBranchGroup.addChild(pointLight);
      mainBranchGroup.addChild(yellowRotXformGroup);

      //Populate the universe by adding the branch group
      // that contains the objects.
      simpleUniverse.addBranchGraph(mainBranchGroup);

      //Do the normal GUI stuff.
      setTitle("Copyright 2007, R.G.Baldwin");
      setBounds(0,0,235,235);
      setVisible(true);

      //This listener is used to terminate the program
      // when the user clicks the X-button on the Frame.
      addWindowListener(
        new WindowAdapter(){
          public void windowClosing(WindowEvent e){
            System.exit(0);
          }//end windowClosing
        }//end new WindowAdapter
      );//end addWindowListener

    }//end constructor

The code in Listing 10 is very similar to code that I have explained in earlier lessons in this series, so I won’t repeat those explanations again here.

The remaining code for the program named Java3D008

The remaining code in this program consists of the definitions of the following methods:

  • createACanvas
  • createYellowSphere
  • createWhiteSphere
  • createGreenSphere
  • createPointLight
  • createTheUniverse
  • animateYellowSphere

The code in all of these methods is very similar to code that I have explained in earlier lessons. You can view these methods in Listing 15. That brings us to the program named Java3D009.

The program named Java3D009

A complete listing of this program is presented in Listing 16. I will also present and explain this program in fragments.

Much of the code in this lesson is very similar or identical to code that I explained in the earlier lessons on Java 3D (see Resources) or in the explanation of the program named Java3D008 above. I won’t repeat those explanations here.

Early code in this program

The early code in this program is essentially the same as the code in Listings 1 through 5 above, so I won’t repeat it here. You can view that code in Listing 16.

Code that is executed when the user clicks the “a” button

As before, the animation of the white sphere depends on the button selected by the user in the input GUI (see the GUI in Figure 1).

The two programs differ in terms of the code in the decision structure in the constructor, which uses the identification of the button that was clicked by the user to establish the behavior of the white sphere.

When the user clicks the button labeled “a”

Listing 11 shows the code that is executed when the user clicks the button labeled “a”.  (Compare this with the code in Listing 5.)

Listing 11. Code that is executed when the user clicks the “a” button.

      if(buttonLabel.equals("a")){
        //Begin rotation of white sphere.
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(4,2500));
        TransformGroup tiltedGroup = 
                      tiltTheAxes(whiteRotXformGroup,
                      0.0d,//x-axis
                      0.0d,//y-axis
                      Math.PI/8.0d);//z-axis

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            tiltedGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        mainBranchGroup.addChild(whiteTransXformGroup);

The code in Listing 11 will rotate the white sphere around the vertical axis and translate the rotating sphere away from the origin in 3D space causing it to remain stationary while spinning in 3D space. Unlike the previous program, the vertical axis belonging to the white sphere is tilted relative to the vertical axis in 3D space and the sphere rotates about that tilted axis.

How does this code differ from before?

Don’t get confused here
The code in the method named rotate causes an object to rotate about the vertical axis belonging to the coordinate system for the universe in a continuous animation. The method named tiltTheAxes causes the axes belonging to an object to be changed one time only relative to their current state.

The code in Listing 11 differs from the earlier code in Listing 5 by the addition of the statement highlighted in boldface in Figure 11. This code calls the new method named tiltTheAxes to perform a one-time rotational transform on the whiteRotXformGroup (the object returned by the original call to the rotate method). This call rotates the group around its z-axis only. (Note that the first two parameters passed to the method named tiltTheAxes have values of zero.) As you will see, this causes the vertical axis of the white sphere to tilt counter-clockwise by 22.5 degrees (Math.PI/8.0d). The white sphere will then spin around that tilted axis after it is translated.

The method named tiltTheAxes

Once again, I will put the discussion of the constructor code on the back burner long enough to explain the behavior of the method named tiltTheAxes. The code for this method is shown in Listing 12.

Listing 12. The method named tiltTheAxes.

    TransformGroup tiltTheAxes(Node node,
                               double xAngle,
                               double yAngle,
                               double zAngle){

      Transform3D tiltAxisXform = new Transform3D();
      Transform3D tempTiltAxisXform = new Transform3D();

      //Construct and then multiply two rotation transform
      // matrices..
      tiltAxisXform.rotX(xAngle);
      tempTiltAxisXform.rotY(yAngle);
      tiltAxisXform.mul(tempTiltAxisXform);

      //Construct the third rotation transform matrix and
      // multiply it by the result of previously 
      // multiplying the two earlier matrices.
      tempTiltAxisXform.rotZ(zAngle);
      tiltAxisXform.mul(tempTiltAxisXform);

      TransformGroup tiltedGroup = new TransformGroup(
                                           tiltAxisXform);
      tiltedGroup.addChild(node);

      return tiltedGroup;
    }//end tiltTheAxes

The purpose of this method is to create and return a TransformGroup object that is designed to perform a counter- clockwise rotation about the x, y, and z axes belonging to an incoming node. The three incoming angle values must be specified in radians. (Note that the first two angle parameters passed to this method in Listing 11 have a value of zero, causing the rotational behavior of the method in this case to apply only to the z-axis.)

Not so easy to understand

Unfortunately, a complete understanding of the code in Listing 12 requires an understanding of the class named Transform3D. This, in turn, requires some knowledge of matrix arithmetic along with an understanding of trigonometry and vectors in 3D space. This is a fairly complex topic. I plan to publish a future lesson that is dedicated to an understanding of the class named Transform3D, so I won’t attempt to explain the technical details here.

Suffice it to say…

Suffice it to say at this point that the Transform3D class contains the following three methods.

Each of these methods constructs and returns a Transform3D object that encapsulates a matrix that is designed to accomplish axis rotation around the x, y, and z axes respectively.

The matrices returned by the three methods can be multiplied together to create a resultant matrix that implements the specified rotation around all three axes.

The resultant matrix can be used to rotate the axes of a node by adding the node and the resultant matrix as children of a TransformGroup object.

Thus, this method receives a node along with three angles as incoming parameters. It uses the three angles along with the methods in the above list to construct the resultant matrix encapsulated in a Transform3D object. Then it adds that Transform3D object along with the incoming node as children to a new TransformGroup object, and returns a reference to the new TransformGroup object.

Now back to the constructor

Getting back to the constructor code in Listing 11, the TransformGroup object returned from the tiltTheAxes method (referred to by the variable named tiltedGroup) is then passed as a parameter to the translate method. Thus, unlike the previous program (see Listing 5), the group that is translated to the new location contains a white sphere that is spinning around a vertical axis that has been tilted counter-clockwise by 22.5 degrees. This produces the image shown in Figure 3 where the facets on the white sphere show that its vertical axis has been tilted counter-clockwise (as compared to the facets on the white sphere shown in Figure 1).

Code that is executed when the user clicks the “b” button

Listing 13 shows the code that is executed when the user clicks the button labeled “b”.  (Compare this code with the code in Listing 8.)

Listing 13. Code that is executed when the user clicks the “b” button.

      }else if(buttonLabel.equals("b")){
        //Translate the white sphere.
        TransformGroup whiteTransXformGroup = translate(
                            whiteSph,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of translated white sphere around
        // the vertical axis at the origin in 3D space.
        TransformGroup whiteRotXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));
        TransformGroup tiltedGroup = 
                      tiltTheAxes(whiteRotXformGroup,
                      0.0d,//x-axis
                      0.0d,//y-axis
                      Math.PI/4.0d);//z-axis

        mainBranchGroup.addChild(tiltedGroup);

This code will translate the white sphere and rotate the translated white sphere around the origin in 3D space, causing the white sphere to orbit the yellow sphere with the same face of the white sphere oriented toward the yellow sphere at all times. Up to this point, the behavior is the same as in the previous program shown in Listing 8.

Tilt the axis

However, the code highlighted in boldface is then added to the code in Listing 13 (as compared to Listing 8). The call to the method named tiltTheAxes in the boldface code will perform a one-time rotational transform on the whiteRotXformGroup, rotating it around its z-axis only. (The angles that specify rotation around the x and y axes are both zero.) This will cause the white sphere to orbit the yellow sphere on a plane that is tilted by 45 degrees (Math.PI/4.0d) relative to the x-z, plane as shown in Figure 4.

Code that is executed when the user clicks the “c” button

Finally, Listing 14 shows the code that is executed when the user clicks the button labeled “c”. (Compare this with the code in Listing 9.)

Listing 14. Code that is executed when the user clicks the “c” button.

      }else if(buttonLabel.equals("c")){
        //The following code will rotate the white sphere,
        // translate the rotating white sphere away from
        // the origin in 3d space, and rotate the
        // translated rotating white sphere around the
        // origin in 3D space causing the white sphere to
        // orbit the yellow sphere and to spin about its
        // own vertical axis at the same time. The white
        // sphere will orbit in a tilted plane.

        //Begin rotation of the white sphere
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(8,1250));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of the translated rotating white
        // sphere.
        TransformGroup whiteRotGroupXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        //Now perform a one-time rotational transform on
        // the whiteRotGroupXformGroup, rotating it around
        // its x, y, and z axes.  This will cause the
        // white sphere to orbit the yellow sphere on a 
        // plane that is tilted relative to the x-y, y-z,
        // and z-x planes.
        TransformGroup tiltedGroup =
                      tiltTheAxes(whiteRotGroupXformGroup,
                      Math.PI/8.0d,
                      Math.PI/2.0d,
                      Math.PI/4.0d);

        mainBranchGroup.addChild(tiltedGroup);

      }//end else-if

When you compare this code with the code in Listing 9, you will see that as was the case in Listing 13, this code contains a call to the method named tiltTheAxes after the code to rotate, translate, and rotate the white sphere has been executed.

A simple screen shot won’t illustrate this behavior

Note that none of the angle values passed to the tiltTheAxes method have a value of zero. As a result, the white sphere for this case orbits in a plane that is tilted relative to all three planes belonging to the universe. In other words, the white sphere orbits in a plane that cuts through the yellow sphere at a non-zero angle relative to all three axes. (A simple screen shot won’t illustrate the difference. You will need to run the program and observe the behavior of the white sphere to appreciate the difference.)

The remaining code

The remaining code for this program is very similar to code that I have already explained. Therefore, I won’t repeat that explanation. You can view that code in Listing 16.

Run the programs

I encourage you to compile and execute the code from Listing 15 and Listing 16. Experiment with the code, making changes, and observing the results of your changes.

Remember, you will need to download and install the Java 3D API plus either Microsoft DirectX or OpenGL to compile and execute these programs. See Download for links to the web sites from which this material can be downloaded.

Summary

Understanding rotation in Java 3D is not too difficult. Similarly, it is not too difficult to understand translation in Java 3D. However, when you combine rotation with translation, things become very complicated very quickly. If you don’t really understand what you are doing when you combine the two, the chance that you will get it right the first time is probably less than fifty percent. In this lesson, I taught you how to get it right the first time by helping you to understand the impact of the order of rotation and translation on the behavior of the code.

What’s next?

The topics for future lessons include interactive Java 3D programs, advanced animation, and surfaces.

Download

Resources

Complete program listings


Complete listings of the programs presented and explained in this lesson are shown in Listing 15 and Listing 16 below.

Listing 15. Program listing for the program named Java3D008.

/*File Java3D008.java
Copyright 2007, R.G.Baldwin

The purpose of this program is to illustrate the 
behavior imparted by the order of rotation and
translation when used together.

The universe contains a yellow sphere, a green sphere, and
a white sphere.

The yellow sphere slowly rotates around the vertical axis
in 3D space for a specified period of time. The center of
the yellow sphere is at the origin in 3D space. Therefore,
the yellow sphere appears to rotate around its own 
vertical axis.

The green sphere is translated to a location slightly 
above, to the right of, and behind the yellow sphere.  The
green sphere maintains its translated location throughout 
the time that the program is running.

The behavior of the white sphere depend on input via a
user input GUI.  The input GUI contains three buttons
labeled "a", "b", and "c"

Depending on the user input the white sphere is animated 
in one of three ways:

1. If the user clicks the "a" button, the white sphere is 
translated to a location just outside the yellow sphere on
the positive z-axis (0.0f,0.0f,0.7f) where it maintains 
its location in space and rotates about its own vertical 
axis.  This is the result of rotation followed by 
translation.

2. If the user clicks the "b" button, the white sphere is 
translated to the same location in space as for the "a"
button and then orbits the yellow sphere with the same 
face of the white sphere always toward the yellow sphere.
This is the result of translation followed by rotation.

3. If the user clicks the "c" button, the white sphere 
orbits the yellow sphere as in 2 above.  However, for this
case, the white sphere also rotates on its own vertical 
axis while orbiting the yellow sphere.  This is the result
of rotation followed by translation followed by another 
rotation.

Note:  On my relatively slow laptop, the first animation 
cycle is perhaps 25-percent complete before the first 
image appears on the screen.  I was unable to discover any
way to prevent this from happening. In addition, there are
sporadic undesirable pauses in the animation.

Tested using Java SE 6, and Java 3D 1.5.0 running under
Windows XP.
*********************************************************/
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.geometry.Primitive;
import javax.media.j3d.Appearance;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Alpha;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Node;
import javax.vecmath.Vector3f;
import javax.vecmath.Point3f;
import javax.vecmath.Point3d;
import javax.vecmath.Color3f;
import java.awt.Frame;
import java.awt.Button;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//This is the top-level driver class for this program.
public class Java3D008 extends Frame{
  TheScene theScene;
  
  public static void main(String[] args){
    Java3D008 thisObj = new Java3D008();
  }//end main
  //----------------------------------------------------//

  public Java3D008(){//top-level constructor
    setLayout(new GridLayout(1,3));
    Button aButton = new Button("a");
    Button bButton = new Button("b");
    Button cButton = new Button("c");

    add(aButton);
    add(bButton);
    add(cButton);

    aButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("a");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    bButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("b");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    cButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("c");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    setTitle("Copyright 2007, R.G.Baldwin");
    setBounds(236,0,235,75);
    setVisible(true);

    //This window listener is used to terminate the
    // program when the user clicks the X button.
    addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end new WindowAdapter
    );//end addWindowListener

  }//end constructor
  //----------------------------------------------------//

  //This is an inner class, from which the universe will
  // be  instantiated and animated.
  class TheScene extends Frame{

    //Declare instance variables that are used later by
    // the program.
    Canvas3D canvas3D;
    Sphere yellowSph;
    Sphere whiteSph;
    Sphere greenSph;
    PointLight pointLight;
    SimpleUniverse simpleUniverse;

    BranchGroup mainBranchGroup = new BranchGroup();

    TransformGroup greenTransformGroup = 
                                     new TransformGroup();
    TransformGroup yellowRotXformGroup = 
                                     new TransformGroup();

    String buttonLabel;
    //--------------------------------------------------//

    TheScene(String input){//constructor
      //Save the incoming parameter to be used later to
      // decide which behavior to execute.
      buttonLabel = input;

      //Construct the universe.
      createACanvas();
      createTheUniverse();

      //Construct the objects that will occupy the
      // universe.
      createYellowSphere();
      createWhiteSphere();
      createGreenSphere();
      createPointLight();

      //Animate the objects.

      //Animation of the yellow sphere is independent of
      // user input.
      animateYellowSphere();

      //Animation of the white sphere depends on the
      // button selected by the user in the input GUI.
      if(buttonLabel.equals("a")){
        //This code will rotate the white sphere around
        // the vertical axis and translate the rotating
        // sphere away from the origin in 3D space causing
        // it to remain stationary while spinning in 3D
        // space.  This behavior is rotation followed by
        // translation.

        //Begin rotation of white sphere.
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(4,2500));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        mainBranchGroup.addChild(whiteTransXformGroup);

      }else if(buttonLabel.equals("b")){
        //This code will translate the white sphere and
        // rotate the translated white sphere around the
        // vertical axis in 3D space, causing the white
        // sphere to orbit the yellow sphere with the same
        // face of the white sphere toward the yellow
        // sphere at all times.  This behavior is
        // translation followed by rotation.

        //Translate the white sphere.
        TransformGroup whiteTransXformGroup = translate(
                            whiteSph,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of translated white sphere around
        // the vertical axis at the origin in 3D space.
        TransformGroup whiteRotXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        mainBranchGroup.addChild(whiteRotXformGroup);

      }else if(buttonLabel.equals("c")){
        //The following code will rotate the white sphere,
        // translate the rotating white sphere away from
        // the origin in 3d space, and rotate the
        // translated rotating white sphere around the
        // origin in 3D space causing the white sphere to
        // orbit the yellow sphere and to spin about its
        // own vertical axis at the same time.  This
        // behavior is rotation followed by translation
        // followed by another rotation.

        //Begin rotation of the white sphere
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(8,1250));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of the translated rotating white
        // sphere.
        TransformGroup whiteRotGroupXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        mainBranchGroup.addChild(whiteRotGroupXformGroup);
      }//end if

      //Finish populating the mainBranchGroup.
      mainBranchGroup.addChild(greenTransformGroup);
      mainBranchGroup.addChild(pointLight);
      mainBranchGroup.addChild(yellowRotXformGroup);

      //Populate the universe by adding the branch group
      // that contains the objects.
      simpleUniverse.addBranchGraph(mainBranchGroup);

      //Do the normal GUI stuff.
      setTitle("Copyright 2007, R.G.Baldwin");
      setBounds(0,0,235,235);
      setVisible(true);

      //This listener is used to terminate the program 
      // when the user clicks the X-button on the Frame.
      addWindowListener(
        new WindowAdapter(){
          public void windowClosing(WindowEvent e){
            System.exit(0);
          }//end windowClosing
        }//end new WindowAdapter
      );//end addWindowListener
      
    }//end constructor
    //--------------------------------------------------//

    //Create a Canvas3D object to be used for rendering
    // the Java 3D universe.  Place it in the CENTER of
    // the Frame.
    void createACanvas(){
      canvas3D = new Canvas3D(
              SimpleUniverse.getPreferredConfiguration());
      add(BorderLayout.CENTER,canvas3D);
    }//end createACanvas
    //--------------------------------------------------//

    //Create and set properties for the large yellow
    // sphere.
    void createYellowSphere(){
      //Begin by describing the appearance of the surface
      // of the large sphere.  Make the color of the large
      // sphere yellow.
      Material yellowSphMaterial = new Material();
      yellowSphMaterial.setDiffuseColor(1.0f,1.0f,0.0f);
      Appearance yellowSphAppearance = new Appearance();
      yellowSphAppearance.setMaterial(yellowSphMaterial);

      //Now instantiate the large yellow sphere with 9
      // divisions.  Set the radius to 0.5. The reason for
      // setting GENERATE_NORMALS is unclear at this time.
      yellowSph = new Sphere(0.5f,
                             Primitive.GENERATE_NORMALS,
                             9,
                             yellowSphAppearance);
    }//end createYellowSphere
    //--------------------------------------------------//

    //Create a white sphere with 8 divisions.  Make the
    // number of divisions small so that the fact that it
    // is spinning will be visually obvious.    
    void createWhiteSphere(){
      Material whiteSphMaterial = new Material();
      whiteSphMaterial.setDiffuseColor(1.0f,1.0f,1.0f);
      Appearance whiteSphAppearance = new Appearance();
      whiteSphAppearance.setMaterial(whiteSphMaterial);
      whiteSph = new Sphere(0.2f,
                            Primitive.GENERATE_NORMALS,
                            8,
                            whiteSphAppearance);
    }//end createWhiteSphere
    //--------------------------------------------------//

    //Create a small green sphere located up to the
    // right and behind the yellow sphere.    
    void createGreenSphere(){
      Material greenSphMaterial = new Material();
      greenSphMaterial.setDiffuseColor(0.0f,1.0f,0.0f);
      Appearance greenSphAppearance = new Appearance();
      greenSphAppearance.setMaterial(greenSphMaterial);
      greenSph = new Sphere(0.10f,
                            Primitive.GENERATE_NORMALS,
                            50,
                            greenSphAppearance);
                            
      //Translate the green sphere.
      greenTransformGroup = translate(
                           greenSph,
                           new Vector3f(0.5f,0.5f,-0.5f));
    }//end createGreenSphere
    //--------------------------------------------------//
    //Create a white point light, located over the
    // viewer's right shoulder.    
    void createPointLight(){
      Color3f pointLightColor = 
                              new Color3f(1.0f,1.0f,1.0f);
      Point3f pointLightPosition = 
                              new Point3f(1.0f,1.0f,2.0f);
      Point3f pointLightAttenuation = 
                              new Point3f(1.0f,0.0f,0.0f);

      pointLight = new PointLight(pointLightColor,
                                  pointLightPosition,
                                  pointLightAttenuation);
                                  
      //Create a BoundingSphere object and use it to the
      // define the illumination region. Illuminate all of
      // the objects within a radius of one unit from
      // the origin in 3D space.
      pointLight.setInfluencingBounds(new BoundingSphere(
                           new Point3d(0.0,0.0,0.0),1.0));
    }//end createPointLight
    //--------------------------------------------------//

    //Create an empty Java 3D universe and associate it 
    // with the Canvas3D object in the CENTER of the
    // frame.  Also specify the apparent location of the
    // viewer's eye.
    void createTheUniverse(){
      simpleUniverse = new SimpleUniverse(canvas3D);
      simpleUniverse.getViewingPlatform().
                             setNominalViewingTransform();
    }//end createTheUniverse
    //--------------------------------------------------//

    //This method causes the yellow sphere to rotate
    // around the vertical axis in 3D space.
    void animateYellowSphere(){
      yellowRotXformGroup = 
                      rotate(yellowSph,new Alpha(2,5000));
    }//end animateYellowSphere
    //--------------------------------------------------//

    //Given an incoming node object and an Alpha object,
    // this method will return a TransformGroup object
    // that is designed to rotate the node around the
    // vertical axis in 3D space according to the number
    // of cycles and cycle time specified by the Alpha
    // object.
    TransformGroup rotate(Node node,Alpha alpha){

      TransformGroup xformGroup = new TransformGroup();
      xformGroup.setCapability(
                    TransformGroup.ALLOW_TRANSFORM_WRITE);

      //Create an interpolator for rotating the node.
      RotationInterpolator interpolator = 
               new RotationInterpolator(alpha,xformGroup);

      //Establish the animation region for this
      // interpolator.
      interpolator.setSchedulingBounds(new BoundingSphere(
                           new Point3d(0.0,0.0,0.0),1.0));

      //Populate the xform group.
      xformGroup.addChild(interpolator);
      xformGroup.addChild(node);

      return xformGroup;

    }//end rotate
    //--------------------------------------------------//

    //Given an incoming node object and a vector object,
    // this method will return a transform group designed
    // to translate that node according to that vector.
    TransformGroup translate(Node node,Vector3f vector){

        Transform3D transform3D = new Transform3D();
        transform3D.setTranslation(vector);
        TransformGroup transformGroup = 
                                     new TransformGroup();
        transformGroup.setTransform(transform3D);

        transformGroup.addChild(node);
        return transformGroup;
    }//end translate
    //--------------------------------------------------//

  }//end inner class TheScene

}//end class Java3D008

 

Listing 16. Program listing for the program named Java3D009.

/*File Java3D009.java
Copyright 2007, R.G.Baldwin

This program is an update to the program named Java3D008.

The purpose of this update is to illustrate one approach
to using translation and rotation in combination to cause
different behaviors.  In one case, a white sphere will
spin around an axis that is tilted counter-clockwise. In
two cases, a white sphere orbits around a yellow sphere on
a plane that is tilted relative to the X-Y, Y-Z, and Z-X 
planes.

The universe contains a yellow sphere, a green sphere, and
a white sphere.

The yellow sphere slowly rotates around the vertical axis
in 3D space for a specified period of time. The center of
the yellow sphere is at the origin in 3D space. Therefore,
the yellow sphere appears to rotate around its own 
vertical axis.

The green sphere is translated to a location slightly 
above, to the right of, and behind the yellow sphere.  The
green sphere maintains its translated location throughout 
the time that the program is running.

The behavior of the white sphere depends on input via a
user input GUI.  The input GUI contains three buttons
labeled "a", "b", and "c"

Depending on the user input the white sphere is animated 
in one of three ways:

1. If the user clicks the "a" button, the white sphere is 
translated to a location just outside the yellow sphere on
the positive z-axis (0.0f,0.0f,0.7f) where it maintains 
its location in space and rotates about its own vertical 
axis.  However, its vertical axis is tilted relative to
the direction of the vertical axis of 3D space. This is 
the result of rotation followed by axis transformation
followed by translation.

2. If the user clicks the "b" button, the white sphere is 
translated to the same location in space as for the "a"
button and then orbits the yellow sphere with the same 
face of the white sphere always toward the yellow sphere.
However, the orbit is on a plane that is tilted relative 
to  the X-Y, Y-Z, and Z-X planes. This is the result 
of translation followed by rotation followed by axis
transformation.

3. If the user clicks the "c" button, the white sphere 
orbits the yellow sphere as in 2 above.  However, for this
case, the white sphere also rotates on its own vertical 
axis while orbiting the yellow sphere.  As before, the 
orbit is on a plane that is tilted relative to  the 
X-Y, Y-Z, and Z-X planes. This is the result of rotation 
followed by translation followed by another rotation 
followed by axis transformation.

Note:  On my relatively slow laptop, the first animation 
cycle is perhaps 25-percent complete before the first 
image appears on the screen.  I was unable to discover any
way to prevent this from happening. In addition, there are
sporadic undesirable pauses in the animation.

Tested using Java SE 6, and Java 3D 1.5.0 running under
Windows XP.
*********************************************************/
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.geometry.Primitive;
import javax.media.j3d.Appearance;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Alpha;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Node;
import javax.vecmath.Vector3f;
import javax.vecmath.Point3f;
import javax.vecmath.Point3d;
import javax.vecmath.Color3f;
import java.awt.Frame;
import java.awt.Button;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//This is the top-level driver class for this program.
public class Java3D009 extends Frame{
  TheScene theScene;

  public static void main(String[] args){
    Java3D009 thisObj = new Java3D009();
  }//end main
  //----------------------------------------------------//

  public Java3D009(){//top-level constructor
    setLayout(new GridLayout(1,3));
    Button aButton = new Button("a");
    Button bButton = new Button("b");
    Button cButton = new Button("c");

    add(aButton);
    add(bButton);
    add(cButton);

    aButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("a");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    bButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("b");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    cButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(ActionEvent e){
          theScene = new TheScene("c");
        }//end actionPerformed
      }//end new ActionListener
    );//end addActionListener

    setTitle("Copyright 2007, R.G.Baldwin");
    setBounds(236,0,235,75);
    setVisible(true);

    //This window listener is used to terminate the
    // program when the user clicks the X button.
    addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end new WindowAdapter
    );//end addWindowListener

  }//end constructor
  //----------------------------------------------------//

  //This is an inner class, from which the universe will
  // be  instantiated and animated.
  class TheScene extends Frame{

    //Declare instance variables that are used later by
    // the program.
    Canvas3D canvas3D;
    Sphere yellowSph;
    Sphere whiteSph;
    Sphere greenSph;
    PointLight pointLight;
    SimpleUniverse simpleUniverse;

    BranchGroup mainBranchGroup = new BranchGroup();

    TransformGroup greenTransformGroup = 
                                     new TransformGroup();
    TransformGroup yellowRotXformGroup = 
                                     new TransformGroup();

    String buttonLabel;
    //--------------------------------------------------//

    TheScene(String input){//constructor
      //Save the incoming parameter to be used later to
      // decide which behavior to execute.
      buttonLabel = input;

      //Construct the universe.
      createACanvas();
      createTheUniverse();

      //Construct the objects that will occupy the
      // universe.
      createYellowSphere();
      createWhiteSphere();
      createGreenSphere();
      createPointLight();

      //Animate the objects.

      //Animation of the yellow sphere is independent of
      // user input.
      animateYellowSphere();

      //Animation of the white sphere depends on the
      // button selected by the user in the input GUI.
      if(buttonLabel.equals("a")){
        //This code will rotate the white sphere around
        // the vertical axis and translate the rotating
        // sphere away from the origin in 3D space causing
        // it to remain stationary while spinning in 3D
        // space. The vertical axis belonging to the white
        // sphere is tilted relative to the vertical axis
        // in 3D space and the sphere rotates about that
        // tilted axis.

        //Begin rotation of white sphere.
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(4,2500));

        //Now perform a one-time rotational transform on
        // the whiteRotXformGroup, rotating it around
        // its z axis only.  This will cause the vertical
        // axis of the white sphere to tilt counter-
        // clockwise by 22.5 degrees.  It will then spin
        // around that tilted axis after it is translated.
        TransformGroup tiltedGroup = 
                      tiltTheAxes(whiteRotXformGroup,
                      0.0d,//x-axis
                      0.0d,//y-axis
                      Math.PI/8.0d);//z-axis

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            tiltedGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        mainBranchGroup.addChild(whiteTransXformGroup);

      }else if(buttonLabel.equals("b")){
        //This code will translate the white sphere and
        // rotate the translated white sphere around the
        // vertical axis in 3D space, causing the white
        // sphere to orbit the yellow sphere with the same
        // face of the white sphere toward the yellow
        // sphere at all times.  The white sphere will 
        // orbit in a tilted plane.

        //Translate the white sphere.
        TransformGroup whiteTransXformGroup = translate(
                            whiteSph,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of translated white sphere around
        // the vertical axis at the origin in 3D space.
        TransformGroup whiteRotXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        //Now perform a one-time rotational transform on
        // the whiteRotXformGroup, rotating it around
        // its z axis only.  This will cause the
        // white sphere to orbit the yellow sphere on a 
        // plane that is tilted relative to the x-z,
        // plane.
        TransformGroup tiltedGroup = 
                      tiltTheAxes(whiteRotXformGroup,
                      0.0d,//x-axis
                      0.0d,//y-axis
                      Math.PI/4.0d);//z-axis

        mainBranchGroup.addChild(tiltedGroup);

      }else if(buttonLabel.equals("c")){
        //The following code will rotate the white sphere,
        // translate the rotating white sphere away from
        // the origin in 3d space, and rotate the
        // translated rotating white sphere around the
        // origin in 3D space causing the white sphere to
        // orbit the yellow sphere and to spin about its
        // own vertical axis at the same time. The white
        // sphere will orbit in a tilted plane.

        //Begin rotation of the white sphere
        TransformGroup whiteRotXformGroup = 
                       rotate(whiteSph,new Alpha(8,1250));

        //Translate the rotating white sphere
        TransformGroup whiteTransXformGroup = translate(
                            whiteRotXformGroup,
                            new Vector3f(0.0f,0.0f,0.7f));

        //Begin rotation of the translated rotating white
        // sphere.
        TransformGroup whiteRotGroupXformGroup = 
           rotate(whiteTransXformGroup,new Alpha(2,5000));

        //Now perform a one-time rotational transform on
        // the whiteRotGroupXformGroup, rotating it around
        // its x, y, and z axes.  This will cause the
        // white sphere to orbit the yellow sphere on a 
        // plane that is tilted relative to the x-y, y-z,
        // and z-x planes.
        TransformGroup tiltedGroup = 
                      tiltTheAxes(whiteRotGroupXformGroup,
                      Math.PI/8.0d,
                      Math.PI/2.0d,
                      Math.PI/4.0d);

        mainBranchGroup.addChild(tiltedGroup);

      }//end else-if

      //Finish populating the mainBranchGroup.
      mainBranchGroup.addChild(greenTransformGroup);
      mainBranchGroup.addChild(pointLight);
      mainBranchGroup.addChild(yellowRotXformGroup);

      //Populate the universe by adding the branch group
      // that contains the objects.
      simpleUniverse.addBranchGraph(mainBranchGroup);

      //Do the normal GUI stuff.
      setTitle("Copyright 2007, R.G.Baldwin");
      setBounds(0,0,235,235);
      setVisible(true);

      //This listener is used to terminate the program 
      // when the user clicks the X-button on the Frame.
      addWindowListener(
        new WindowAdapter(){
          public void windowClosing(WindowEvent e){
            System.exit(0);
          }//end windowClosing
        }//end new WindowAdapter
      );//end addWindowListener

    }//end constructor
    //--------------------------------------------------//

    //Create a Canvas3D object to be used for rendering
    // the Java 3D universe.  Place it in the CENTER of
    // the Frame.
    void createACanvas(){
      canvas3D = new Canvas3D(
              SimpleUniverse.getPreferredConfiguration());
      add(BorderLayout.CENTER,canvas3D);
    }//end createACanvas
    //--------------------------------------------------//

    //Create and set properties for the large yellow
    // sphere.
    void createYellowSphere(){
      //Begin by describing the appearance of the surface
      // of the large sphere.  Make the color of the large
      // sphere yellow.
      Material yellowSphMaterial = new Material();
      yellowSphMaterial.setDiffuseColor(1.0f,1.0f,0.0f);
      Appearance yellowSphAppearance = new Appearance();
      yellowSphAppearance.setMaterial(yellowSphMaterial);

      //Now instantiate the large yellow sphere with 9
      // divisions.  Set the radius to 0.5. The reason for
      // setting GENERATE_NORMALS is unclear at this time.
      yellowSph = new Sphere(0.5f,
                             Primitive.GENERATE_NORMALS,
                             9,
                             yellowSphAppearance);
    }//end createYellowSphere
    //--------------------------------------------------//

    //Create a white sphere with 8 divisions.  Make the
    // number of divisions small so that the fact that it
    // is spinning will be visually obvious.    
    void createWhiteSphere(){
      Material whiteSphMaterial = new Material();
      whiteSphMaterial.setDiffuseColor(1.0f,1.0f,1.0f);
      Appearance whiteSphAppearance = new Appearance();
      whiteSphAppearance.setMaterial(whiteSphMaterial);
      whiteSph = new Sphere(0.2f,
                            Primitive.GENERATE_NORMALS,
                            8,
                            whiteSphAppearance);
    }//end createWhiteSphere
    //--------------------------------------------------//

    //Create a small green sphere located up to the
    // right and behind the yellow sphere.    
    void createGreenSphere(){
      Material greenSphMaterial = new Material();
      greenSphMaterial.setDiffuseColor(0.0f,1.0f,0.0f);
      Appearance greenSphAppearance = new Appearance();
      greenSphAppearance.setMaterial(greenSphMaterial);
      greenSph = new Sphere(0.10f,
                            Primitive.GENERATE_NORMALS,
                            50,
                            greenSphAppearance);
                            
      //Translate the green sphere.
      greenTransformGroup = translate(
                           greenSph,
                           new Vector3f(0.5f,0.5f,-0.5f));
    }//end createGreenSphere
    //--------------------------------------------------//
    //Create a white point light, located over the
    // viewer's right shoulder.    
    void createPointLight(){
      Color3f pointLightColor = 
                              new Color3f(1.0f,1.0f,1.0f);
      Point3f pointLightPosition = 
                              new Point3f(1.0f,1.0f,2.0f);
      Point3f pointLightAttenuation = 
                              new Point3f(1.0f,0.0f,0.0f);

      pointLight = new PointLight(pointLightColor,
                                  pointLightPosition,
                                  pointLightAttenuation);

      //Create a BoundingSphere object and use it to the
      // define the illumination region. Illuminate all of
      // the objects within a radius of one unit from
      // the origin in 3D space.
      pointLight.setInfluencingBounds(new BoundingSphere(
                           new Point3d(0.0,0.0,0.0),1.0));
    }//end createPointLight
    //--------------------------------------------------//

    //Create an empty Java 3D universe and associate it 
    // with the Canvas3D object in the CENTER of the
    // frame.  Also specify the apparent location of the
    // viewer's eye.
    void createTheUniverse(){
      simpleUniverse = new SimpleUniverse(canvas3D);
      simpleUniverse.getViewingPlatform().
                             setNominalViewingTransform();
    }//end createTheUniverse
    //--------------------------------------------------//

    //This method causes the yellow sphere to rotate
    // around the vertical axis in 3D space.
    void animateYellowSphere(){
      yellowRotXformGroup = 
                      rotate(yellowSph,new Alpha(2,5000));
    }//end animateYellowSphere
    //--------------------------------------------------//

    //Given an incoming node object and an Alpha object,
    // this method will return a TransformGroup object
    // that is designed to rotate the node around the
    // vertical axis in 3D space according to the number
    // of cycles and cycle time specified by the Alpha
    // object.
    TransformGroup rotate(Node node,Alpha alpha){

      TransformGroup xformGroup = new TransformGroup();
      xformGroup.setCapability(
                    TransformGroup.ALLOW_TRANSFORM_WRITE);

      //Create an interpolator for rotating the node.
      RotationInterpolator interpolator = 
               new RotationInterpolator(alpha,xformGroup);

      //Establish the animation region for this
      // interpolator.
      interpolator.setSchedulingBounds(new BoundingSphere(
                           new Point3d(0.0,0.0,0.0),1.0));

      //Populate the xform group.
      xformGroup.addChild(interpolator);
      xformGroup.addChild(node);

      return xformGroup;

    }//end rotate
    //--------------------------------------------------//

    //Given an incoming node object and a vector object,
    // this method will return a transform group designed
    // to translate that node according to that vector.
    TransformGroup translate(Node node,Vector3f vector){

        Transform3D transform3D = new Transform3D();
        transform3D.setTranslation(vector);
        TransformGroup transformGroup = 
                                     new TransformGroup();
        transformGroup.setTransform(transform3D);

        transformGroup.addChild(node);
        return transformGroup;
    }//end translate
    //--------------------------------------------------//

    //The purpose of this method is to create and return
    // a transform group designed to perform a counter-
    // clockwise rotation about the x, y, and z axes 
    // belonging to an incoming node.  The three incoming
    // angle values must be specified in radians. Don't
    // confuse this with a RotationInterpolator.  This is
    // not an interpolation operation.  Rather, it is a
    // one-time transform.
    TransformGroup tiltTheAxes(Node node,
                               double xAngle,
                               double yAngle,
                               double zAngle){

      Transform3D tiltAxisXform = new Transform3D();
      Transform3D tempTiltAxisXform = new Transform3D();

      //Construct and then multiply two rotation transform
      // matrices..
      tiltAxisXform.rotX(xAngle);
      tempTiltAxisXform.rotY(yAngle);
      tiltAxisXform.mul(tempTiltAxisXform);

      //Construct the third rotation transform matrix and
      // multiply it by the result of previously 
      // multiplying the two earlier matrices.
      tempTiltAxisXform.rotZ(zAngle);
      tiltAxisXform.mul(tempTiltAxisXform);

      TransformGroup tiltedGroup = new TransformGroup(
                                           tiltAxisXform);
      tiltedGroup.addChild(node);

      return tiltedGroup;
    }//end tiltTheAxes
    //==================================================//
    
  }//end inner class TheScene

}//end class Java3D009

Copyright

Copyright 2007, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin’s ProgrammingTutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor’s degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories