Grouping Scene Graph Elements

So far, we have only worked with BranchGroup objects, but—as mentioned previously—six subclasses of the Group node can be used to achieve different organization/functional groupings. We examine these other subclasses of Group here.

TransformGroup

Although the TransformGroup is not listed among the mandatory objects in any Java 3D program, it is effectively mandatory in the sense that spatial transformation is at the heart of 3D graphics, and TransformGroups are the mechanism by which spatial transformations are made in Java 3D.

If we wanted to add a second box to our simple scene in Listing 11.1, we could add the following lines to the createScene() method:

Public BranchGroup createScene() {
. . .
  // Make a second object, a bigger box; use same material
  Box bigbox = new Box(6.f, 6.f, 6,f, app);
  ObjRoot.add(bigbox);
}

Obviously, this step isn't very useful because we cannot see the small box inside the big box we just created. At any rate, we are going to eventually want to distribute our objects around the scene and move different groups of them based on user actions.

Moving, rotating, and otherwise spatially transforming scene objects is accomplished through the use of the Transform3D class in conjunction with one or more TransformGroups. To see how these objects work together, add the following lines to the createScene() method:

//instantiate the TransformGroup and set its Capability bits
TransformGroup bigboxTG = new TransformGroup();

//add bigbox to the TransformGroup
bigboxTG.addChild(bigbox);

//set up a Transform3D object and translate the object in x
Transform3D bbtran =  new Transform3D()
bbtran.setTranslation(new Vector3f(7.f, 0.f, 0.f));1
//set the transform of the TransformGroup to bbtran
bigboxTG.setTransform(bbtran);

In order to invoke the preceding spatial transformation, we need to change the parent-child relationships of the over-simplified scene graph by adding bigbox to the newly created TransformGroup instead of directly to the BranchGroup as we did in the first example. In turn, we add the new TransformGroup to the BranchGroup. Indeed, it is less common to add scene elements (Leaf nodes) directly to the BranchGroup. The more usual case is to add the scene element to a TransformGroup and add the TransformGroup to the BranchGroup.

Now our scene graph looks as shown in Figure 11.3 and our scene looks like Figure 11.4.

Figure 11.3. Reorganized scene graph from BasicRecipeJ3D.java showing additional TransformGroup and alteration in the parent-child relationships.


Figure 11.4. Screenshot from the application after adding and translating a second box using a TransformGroup.


In this context, the TransformGroup seems like an unnecessary branch to the scene graph. Why not just translate the geometry directly?

When we really start designing a simulation or a virtual environment, we will see that the TransformGroup is quite useful. If we wanted to make a wall with a window and some paintings on it, it would be much easier to attach the window and paintings to the wall and then move the entire group of objects with a single transform. A natural grouping will shake out of our design. Without having a formal structure for grouping transformations, things will get messy in a hurry. Moreover, without grouping, when the programmer wants to move the wall and its contents in the future, a new transform will have to be computed for each object. If the objects are moveable by the user, the programming is even more complicated and rapidly becomes inefficient. The answer to this problem is to group the elements together in a TransformGroup. Finally, we note that TransformGroups are the basis of many of the optimizations that can be achieved.

Chains of TransformGroups

TransformGroups can be added to other TransformGroups to set up a hierarchy of transforms. A nice example of this would be making a visualization of a skeletal arm. Each individual joint of the arm can naturally be thought of in terms of a TransformGroup. The wrist, for example, would rather naturally form a TransformGroup that has as its children the bones of the hand (the carpal, metacarpal) and the bones of the fingers (phalanges). By changing the transform of the wrist (that is, by rotating or otherwise displacing it), the hand and fingers will move with it. The wrist with the forearm (radius and ulna) would, in turn, be added as a child to the TransformGroup of the elbow. Performing a rotation of the elbow would thus cause the wrist with all of its children and grandchildren to follow. Likewise, a TransformGroup of the shoulder (humerus) should be the parent of the TransformGroup of the elbow and its children such that when the shoulder rotates, the elbow will follow, as will the wrist. This hierarchical arrangement can be described further, of course, but the analogy has already served its purpose.

Adding the ViewPlatform to a TransformGroup

Note that the ViewPlatform can also be added to a TransformGroup and manipulated as well as in Listing 11.4. As we did in Listing 11.1, we will instantiate a BranchGroup object in the createViewGraph() method. However, this time we will also create a TransformGroup called vpTrans, and add the ViewPlatform to it. We can then translate the ViewPlatform back a bit getting and setting the TransformGroup's Transform3D. Finally, we add the TransformGroup to the BranchGroup thereby producing the subgraph shown in Figure 11.5.

Figure 11.5. Scene graph after adding the ViewPlatform to a TransformGroup.


Listing 11.4 BasicRecipeJ3D.java Part 4 of 4
public BranchGroup createViewGraph() {

   BranchGroup objRoot = new BranchGroup();

   Transform3D t = new Transform3D();

   t.setTranslation(new Vector3f(0.0f, 0.0f, 10.0f));
   ViewPlatform vp = new ViewPlatform();
   TransformGroup vpTrans = new TransformGroup();
   vpTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   vpTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   vpTrans.setTransform(t);
   vpTrans.addChild(vp);
   view.attachViewPlatform(vp);

   NavigationBehavior nav = new NavigationBehavior(vpTrans);
   vpTrans.addChild(nav);
   nav.setSchedulingBounds(bounds);

   objRoot.addChild(vpTrans);
   return objRoot;

 }

Thinking in Terms of Local Coordinates

From the previous discussion about the skeletal arm, it is pretty clear that TransformGroups can best be understood in terms of local coordinate systems. In other words, by placing ourselves in the local coordinate system of the wrist, we can easily understand the rotations there. It is far easier to enter the local coordinate system than to think of all transformations relative to some unifying coordinate system. One quickly develops the ability to shift coordinate systems. This concept is an integral part of many visualization problems and is basic to the KspaceModeller example, “Comprehensive Example #1: MR Physics Visualization,” that begins near the end of this chapter. A clear hierarchy of TransformGroups is set up in order to have a rotatable and scalable net vector.

Rotating an Object Around the Origin

Rotations occur about the origin of the transform, so if the transform has any non-zero translation components, the object will move about the origin (that is, orbit). The whole issue of the order of operations becomes important in this situation. If the object is rotated and then translated, the result will be completely different from the situation in which the object is translated and then rotated. In other words, T( R(x,y,z) ) is not equal to R( T(x,y,z) i ), where T is translation and R is rotation.

Switch Nodes

Switch nodes are useful for choosing which among a number of children to render. Note that any number of children can be selected including none, one (the most common), or multiple. Choosing the child or children to render is accomplished through the Switch's setWhichChild() method or by specifying a bit mask and calling the setChildMask() method.

One of the primary uses of a Switch node is level of detail rendering in which different amounts of detail are rendered, depending on how near or far the viewer is from the part of the scene to be rendered. Using a model of New York City as an example, if the user is sitting on top of the Empire State Building, a lot of the Statue of Liberty's details are wasted because the viewer can't see the fine detail from such a great distance. It might be useful to set up a series of BranchGroups—the first containing the most detailed model of the Statue of Liberty and the last containing only a large rectangle with a texture map of the Statue of Liberty. As the user navigates closer to the Statue of Liberty, the more detailed geometric model would be switched on based on a proximity switch sensor, whereas the less detailed model would be switched off.

Another application is in hiding and unveiling an object(or objects) in the scene. Listing 11.5 shows an example of switching between two simple scenes based on pressing any key on the keyboard.

Listing 11.5 SwitchExampleJ3D.java
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Sphere;
import java.util.BitSet;

import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;

public class SwitchExampleJ3D extends Applet {
  VirtualUniverse universe;
  Locale locale;
  TransformGroup vpTrans;
  View view;
  Bounds bounds;
  Switch sw;
  BitSet bitset;
    public BranchGroup createSceneGraph() {
// Create the root of the branch graph; this will be returned

        BranchGroup objRoot = new BranchGroup();
        sw = new Switch();
        sw.setCapability(Switch.ALLOW_SWITCH_READ);
        sw.setCapability(Switch.ALLOW_SWITCH_WRITE);

        sw.addChild(createScene1());
        sw.addChild(createScene2());
       //set up a bitset with 3 bits x x x
        bitset = new BitSet(3);
        bitset.set(0);
        bitset.set(1);
        objRoot.addChild(sw);

        objRoot.compile();
    return objRoot;
     }

     public BranchGroup createScene1() {

        BranchGroup scene1 = new BranchGroup();

        Color3f blue = new Color3f(0.f, 0.f, 0.9f);

        Appearance app = new Appearance();
        Material mat =  new Material();
        mat.setSpecularColor(blue);
        app.setMaterial(mat);

        TransformGroup geoTG = new TransformGroup();
        geoTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    geoTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

        BoundingSphere bounds =
             new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

        MouseRotate mouseBeh = new MouseRotate(geoTG);
    geoTG.addChild(mouseBeh);
    mouseBeh.setSchedulingBounds(bounds);

        Sphere sphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS |
                  Sphere.GENERATE_TEXTURE_COORDS, 45);

        Sphere orbitsphere = new Sphere(.3f, Sphere.GENERATE_NORMALS |
                  Sphere.GENERATE_TEXTURE_COORDS, 45);

        TransformGroup otranTG = new TransformGroup();
        Transform3D torbit = new Transform3D();
        torbit.setTranslation(new Vector3f(.5f, .5f, 1.5f));
        otranTG.setTransform(torbit);
        otranTG.addChild(orbitsphere);

        geoTG.addChild(otranTG);

        geoTG.addChild(sphere);

       // AmbientLight al = new AmbientLight(true, new Color3f(.1f,.9f, .1f));
       // AmbientLight al = new AmbientLight();

        Vector3f bluedir  = new Vector3f(0.0f, -8.0f, -8.0f);
        DirectionalLight bluelight = new DirectionalLight(blue, bluedir);


       bluelight.setInfluencingBounds(bounds);

       scene1.addChild(geoTG);

       scene1.addChild(bluelight);
       return scene1;

     }

     public BranchGroup createScene2() {

.  .//more or less the same as createScene1();

}

public SwitchExampleJ3D() {
   setLayout(new BorderLayout());
    //GraphicsConfiguration config =
         //  VirtualUniverse.getPreferredConfiguration();

     Canvas3D c = new Canvas3D(null);
     add("Center", c);
         universe = new VirtualUniverse();
     locale = new Locale(universe);
     PhysicalBody body = new PhysicalBody();
     PhysicalEnvironment environment = new PhysicalEnvironment();
     view = new View();
     view.addCanvas3D(c);
     view.setPhysicalBody(body);
         view.setPhysicalEnvironment(environment);
    // Create a simple scene and attach it to the virtual universe

         bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
         BranchGroup scene = createSceneGraph();
         BranchGroup vgraph = createViewGraph();


         KeyHandler kh = new KeyHandler(this);
         c.addKeyListener(kh);

        locale.addBranchGraph(vgraph);
    locale.addBranchGraph(scene);
    }

    public void changeScenes() {

        if (sw.getWhichChild()==0) {
            sw.setWhichChild(1);
         }
         else if (sw.getWhichChild()==1) {
           sw.setWhichChild(Switch.CHILD_MASK);
           sw.setChildMask(bitset);
         }
         else {
            sw.setWhichChild(0);
         }
    }

//make a custom keyhandler
class KeyHandler implements KeyListener {
     SwitchTextJ3D st;

     public KeyHandler(SwitchTextJ3D st) {
         this.st = st;
     }

     public void keyReleased(java.awt.event.KeyEvent p1) {
     }

     public void keyPressed(java.awt.event.KeyEvent p1) {
          st.changeScenes();
     }

     public void keyTyped(java.awt.event.KeyEvent p1) {
     }

}

Notice in the preceding code that we create a BranchGroup called objRoot and add it to the Locale object, just as we would in any Java 3D application. However, now we create two separate BranchGroups and add them to our Switch. The Switch is added to the top-level BranchGroup, objRoot.

The changeScenes() method simply switches rendering between the first BranchGroup, the second BranchGroup, and both BranchGroups, depending on the values set in the BitSet object.

Shared Groups

A SharedGroup can be used to hold shapes that are used repetitively in scenes and serves to reduce overhead (in some cases) and to simplify the program. In a road scene, for example, some elements will frequently be encountered (stop signs, yield signs, and so on). Instantiating these road elements each time would force us to have a high overhead and an unwieldy scene graph.

Importantly, SharedGroups aren't added to the scene graph directly, but instead are referenced through a Link leaf. The Link leaf is added to the scene graph.

Tip

A SharedGroup can be compiled prior to being referenced, and this often presents an important opportunity for optimization.


OrderedGroups

From our discussion of the z-buffer algorithm in Chapter 10, remember that conflicts sometimes exist about which objects belong in front of each other in the scene and that this can add artifacts to the rendering. Gamers naturally refer to this problem as z-buffer fighting. In some cases—especially those in which many near coplanar surfaces are used—an OrderedGroup can be used to explicitly state the rendering order of all children. The rendering order is the order in which the children are added. The other Group subclasses allow the Java 3D renderer to create an optimized rendering order.

DecalGroups

The DecalGroup class is a subclass of OrderedGroup and is used for defining decal geometry that is placed over other geometry. As a subclass of OrderedGroup, DecalGroup provides an order for rendering its children. Further, DecalGroup specifies that its rendering should be coplanar with the object that is to receive the decal.

The object to receive a decal holds the 0 index position. All the polygons for each of the children must face in the same direction as this index 0 object.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset