Java 3D Lecture

This lecture is courtesy of Petros Komodromos.

Topics

  1. Introduction to Java® 3D
  2. Java® 3D References
  3. Examples and Applications
  4. Scene Graph Structure and basic Java® 3D concepts and classes
  5. A simple Java® 3D program
  6. Performance of Java® 3D

1. Introduction to Java® 3D

Java® 3D is a general-purpose, platform-independent, object-oriented API for 3D-graphics that enables high-level development of Java® applications and applets with 3D interactive rendering capabilities. With Java® 3D, 3D scenes can be built programmatically, or, alternatively, 3D content can be loaded from VRML or other external files. Java® 3D, as a part of the Java® Media APIs, integrates well with the other Java® technologies and APIs. For example, Java® 2D API can be used to plot selected results, while the Java® Media Framework (JMF) API can be used to capture and stream audio and video.

Java® 3D is based on a directed acyclic graph-based scene structure, known as scene graph, that is used for representing and rendering the scene. The scene structure is a tree-like diagram that contains nodes with all the necessary information to create and render the scene. In particular, the scene graph contains the nodes that are used to represent and transform all objects in the scene, and all viewing control parameters, i.e. all objects with information related to the viewing of the scene. The scene graph can be manipulated very easily and quickly allowing efficient rendering by following a certain optimal order and bypassing hidden parts of objects in the scene.

Java® 3D API has been developed under a joint collaboration between Intel, Silicon Graphics, Apple, and Sun, combining the related knowledge of these companies. It has been designed to be a platform-independent API concerning the host's operating system (PC/Solaris/Irix/HPUX/Linux) and graphics (OpenGL/Direct3D) platform, as well as the input and output (display) devices. The implementation of Java® 3D is built on top of OpenGL, or Direct3D. The high level Java® 3D API allows rapid application development which is very critical, especially nowadays.

However, Java® 3D has some weaknesses such as the performance, which is inferior to that of OpenGL, and the limited access to the rendering pipeline details. It is also still under development and several bugs need to be fixed. Although Java® 3D cannot achieve peak performance, portability and rapid development advantages may overweigh the slight performance penalty for many applications.

The current version of the Java® 3D API is the Version 1.2, which works together with the Java® 2 Platform. Both APIs can be downloaded for free from the Java® products page of Sun.


 

2. Java® 3D References

The following list includes many links related to the Java® 3D API

Java® 3D is specified in the packages: javax.media.j3d and javax.vecmath. Supporting classes and utilities are provided in the com.sun.j3d packages.

3. Examples and Applications

The following are examples provided by Java® 3D in directories with the names as follows under the subdirectory java3d of the directory demo.

  • AlternateAppearance
  • Appearance
  • AppearanceMixed
  • AWT_Interaction: java AWTInteraction
  • Background
  • Billboard
  • ConicWorld: java  SimpleCylinder ; java  TexturedSphere
  • FourByFour: appletviewer fbf.html
  • GearTest: java GearBox
  • GeometryByReference
  • GeometryCompression
  • HelloUniverse
  • Lightwave
  • LOD
  • ModelClip
  • Morphing
  • ObjLoad
  • OffScreenCanvas3D
  • OrientedShape3D
  • PackageInfo
  • PickTest
  • PickText3D: java PickText3DGeometry
  • PlatformGeometry
  • PureImmediate
  • ReadRaster
  • Sound
  • SphereMotion: appletviewer SphereMotion.html
  • SplineAnim
  • Text2D
  • Text3D
  • TextureByReference
  • TextureTest
  • TickTockCollision:  java TickTockCollision
  • TickTockPicking
  • VirtualInputDevice

For example, on a Sun Ultra 10 workstation the files for the GearTest example are located under the subdirectory:

mit/java_v1.2ref/distrib/sun4x_56/demo/java3d/GearTest

Similarly, if you download Java® 3D on your computer, the examples are typically stored in subdirectories in the subdirectory demo\java3d of the directory where Java® has been downloaded, e.g. at C:\Java\jdk1.3\demo\java3d.

There are many fields in which Java® 3D can be used. The following are just a small selection of Java® 3D applications that are available on the net.

4. Scene Graph Structure and Basic Java® 3D Concepts and Classes

Scene graph: Content-View Branches

A Java® 3D scene is created as a tree-like graph structure, which is traversed during rendering. The scene graph structure contains nodes that represent either the actual objects of the scene, or, specifications that describe how to view the objects. Usually, there are two  branches in Java® 3D, the content branch, which contains the nodes that describe the actual objects in the scene, and  the view branch, which contains nodes that specify viewing related conditions. Usually, the content branch contains much larger number of nodes than the view branch.

The following image shows a basic Java® 3D graph scene, where the content branch is located on the left and the view branch on the right side of the graph:

A basic Java3D graph scene.

Java® 3D applications construct individual graphic components as separate objects, called nodes, and connects them together into a tree-like scene graph, in which the objects and the viewing of them can easily be manipulated. The scene graph structure contains the description of the virtual universe, which represents the entire scene. All information concerning geometric objects, their attributes, position and orientation, as well as the viewing information are all contained into the scene graph.

The above scene graph consists of superstructure components, in particular a VirtualUniverse and a Locale object, and a two BranchGroup objects, which are attached to the superstructure. The one branch graph, rooted at the left BranchGroup node, is a content branch, containing all the relevant to the contents of the scene objects. The other branch, known as view branch, contains all the information related to the viewing and the rendering details of the scene.

The state of a shape node, or any other leaf node, is defined, during rendering, by the nodes that lie in the direct path between that node and the root node, i.e. the VirtualUniverse. For example, a TransformGroup node in a path between a leaf node and the scene's root can change the position, orientation, and scale of the object represented by the leaf node.

SceneGraphObject Hierarchy

The Java® 3D node objects of a Java® 3D scene graph, which are instances of the Node class, may reference node component objects, which are instances of the class NodeComponent. The Node and NodeComponent classes are subclasses of the SceneGraphObject abstract class. Almost all objects that may be included in a scene graph are instances of subclasses of the SceneGraphObject class. A scene graph object is constructed by instantiating the corresponding class, and then, it can be accessed and manipulated using the provided set and get methods.

The following graph shows the class hierarchy of the major subclasses of the SceneGraphObject class:

Class hierarchy of the major subclasses of the SceneGraphObject class.

Class Node and its subclasses

The abstract Class Node is the base class for almost all objects that constitute the scene graph. It has two subclasses the Group, and Leaf classes, which have many useful subclasses. Class Group is a superclass of, among others, the classes BranchGroup and TransformGroup. Class Leaf, which is used for nodes with no children, is a superclass of, among others, the classes Behavior, Light, Shape3D, and ViewPlatform. The ViewPlatform node is used to define from where the scene is viewed. In particular, it can be used to specify the location and the orientation of the point of view.

Class NodeComponent and its subclasses

Class NodeComponent is the base class for classes that represent attributes associated with the nodes of the scene graph. It is the superclass of all scene graph node component classes, such as the Appearance, Geometry, PointAttributes, and PolygonAttributes classes. NodeComponent objects are used to specify attributes for a node, such as the color and geometry of a shape node, i.e. a Shape3D node. In particular, a Shape3D node uses an Appearance and a Geometry objects, where the Appearance object is used to control how the associated geometry should be rendered by Java® 3D.

The geometry component information of a Shape3D node, i.e. its geometry and topology, can be specified in an instance of a subclass of the abstract Geometry class. A Geometry object is used as a component object of a Shape3D leaf node. Geometry objects consist of the following four generic geometric types. Each of these geometric types defines a visible object, or a set of objects.

For example, the GeometryArray is a subclass of the class Geometry, which itself extends the NodeComponent class, that is extended to create the various primitive types such as lines, triangle strips and quadrilaterals.

Class Node Component and its subclasses.

The IndexedGeometryArray object above contains separate integer arrays that index, among others, into arrays of positional coordinates specifying how vertices are connected to form geometry primitives. This class is extended to create the various indexed primitive types, such as IndexedLineArray, IndexedPointArray, and IndexedQuadArray.

Vertex data may be passed to the geometry array either by copying the data into the array using the existing methods, which is the default mode, or by passing a reference to the data.

The methods for setting positional coordinates, colors, normals, and texture coordinates, such as the method setCoordinates(), copy the data into the GeometryArray, which offers much flexibility in organizing the data.

Another set of methods allows data to be passed and accessed by reference, such as the setCoordRef3d() method, set a reference to user-supplied data, e.g. coordinate arrays. In order to enable the passing of data by reference, the BY_REFERENCE bit in the vertexFormat field of the constructor for the corresponding GeometryArray must be set accordingly. Data in any array that is referenced by a live or compiled GeometryArray object may only be modified using the updateData method assuming that the ALLOW_REF_DATA_WRITE capability bit is set accordingly, which can be done using the setCapability method.

The Appearance object defines all rendering state that control the way the associated geometry should be rendered. The rendering state consists of the following:

  • Point attributes: a PointAttributes object defines attributes used to define points, such as the size to be used
  • Line attributes: using a LineAttributes object attributes used to define lines, such as the width and pattern, can be defined
  • Polygon attributes: using a PolygonAttributes object the attributes used to define polygons, such as rasterization mode (i.e.. filled, lines, or points) are defined
  • Coloring attributes: a ColoringAttributes object is used to defines attributes used in color selection and shading.
  • Rendering attributes: defines rendering operations, such as whether invisible objects are rendered, using a RenderingAttributes object.
  • Transparency attributes: a TransparencyAttributes defines the attributes that affect transparency of the object
  • Material: a Material object defines the appearance of an object under illumination, such as the ambient color, specular color, diffuse color, emissive color, and shininess. It is used to control the color of the shape.
  • Texture: the texture image and filtering parameters used, when texture mapping is enabled, can be defined in a Texture object.
  • Texture attributes: a TextureAttributes object can be used to define the attributes that apply to texture mapping, such as the texture mode, texture transform, blend color, and perspective correction mode.
  • Texture coordinate generation: the attributes that apply to texture coordinate generation can be defined in a TexCoordGeneration object.
  • Texture unit state: array that defines texture state for each of N separate texture units allowing multiple textures to be applied to geometry. Each TextureUnitState object contains a Texture object, TextureAttributes, and TexCoordGeneration object for one texture unit.

VirtualUniverse

and Locale

 

After constructing a subgraph, it can be attached to a VirtualUniverse object through a high-resolution Locale object, which is itself attached to the virtual universe. The VirtualUniverse is the root of all Java® 3D scenes, while Locale objects are used for basic spatial placement. The attachment to a Locale object makes all objects in the attached subgraph live (i.e. drawable), while removing it from the locale reverses the effect. Any node added to a live scene graph becomes live. However, in order to be able to modify a live node the corresponding capability bits should be set accordingly.

Typically, a Java® 3D program has only one VirtualUniverse which consists of one, or more, Locale objects that may contain collections of subgraphs of the scene graph that are rooted by BranchGroup nodes, i.e. a large number of  branch graphs. Although a Locale has no explicit children, it may reference an arbitrary number of BranchGroup nodes. The subgraphs contain all the scene graph nodes that exist in the universe. A Locale node is used to accurately position a branch graph in a universe specifying a location within the virtual universe using high-resolution coordinates (HiResCoord), which represent 768 bits of floating point values. A Locale is positioned in a single VirtualUniverse node using one of its constructors.

The VirtualUniverse and Locale classes, as well as the View class, are subclasses of the basic superclass Object, as shown below:

The VirtualUniverse, Locale and View class as subclasses of the basic superclass Object.

 

Branch Graphs

A branch graph is a scene graph rooted in a BranchGroup node and can be used to point to the root of a scene graph branch. A graph branch can be added to the list of branch graphs of a Locale node using its addBranchGraph(BranchGroup bg) method. BranchGroup objects are the only objects that can be inserted into a Locale's list of objects.

BranchGroup may be compiled by calling its compile method, which causes the entire subgraph to be compiled including any BranchGroup nodes that may be contained within the subgraph. A graph branch, rooted by a  BranchGroup node, becomes live when inserted into a virtual universe by attaching it to a Locale. However, if a BranchGroup is contained in another subgraph as a child of some other group node, it may not be attached to a Locale node.

Capability Bits, Making Live and Compiling

Certain optimizations can be done to achieve better performance by compiling a subgraph into an optimized internal format, prior to its attachment to a virtual universe. However, many set and get methods of objects that are part of a live or compiled scene graph cannot be accessed. In general, the set and get methods can be used only during the creation of a scene graph, except where explicitly allowed, in order to allow certain optimizations during rendering. The set and get methods that can be used when the object is live or compiled should be specified using a set of capability bits, which by default are disabled, prior to compiling or making live the object. The methods isCompiled() and isLive() can be used to find out whether a scene graph object is compiled or live. The methods setCapability() and getCapability() can be used to set properly the capability bits to allow access to the object's methods. However, the less the capability bits that are enabled, the more optimizations can be performed during rendering.

Viewing Branch: ViewPlatform View, Screen3D

The view branch has usually the following structure, consisting of nodes that control the viewing of the scene.

Structure of the view branch.

The view branch contains some scene graph viewing objects that can be used to define the viewing parameters and details, such as the ViewPlatformView, Screen3DPhysicalBody, and PhysicalEnvironment classes.

Java® 3D uses a viewing model that can be used to transform the position and direction of the viewing while the content branch remains unmodified. This is achieved with the use of the ViewPlatform and the View classes, to specify from where and how, respectively, the scene is being viewed.

The ViewPlatform node controls the position, orientation and scale of the viewer. A viewer can navigate through the virtual universe by changing the transformation in the scene graph hierarchy above the ViewPlatform node. The location of the viewer can be set using a TransformGroup node above the ViewPlatform node. The ViewPlatform node has an activation radius that is used together with the bounding volumes of BehaviorBackground and other nodes in order to determine whether the latter nodes should be scheduled, or turned on, respectively. The method setActivationRadius() can be used to set the activation radius.

View object connects to the ViewPlatform node in the scene graph, and specifies all viewing parameters of the rendering process of a 3D scene. Although it exists outside of the scene graph, it attaches to a ViewPlatform leaf node in the scene graph, using the method attachViewPlatform(ViewPlatform vp). A View object contains references to a PhysicalBody and a PhysicalEnvironment object, which can be set using the methods setPhysicalBody() and setPhysicalEnvironment(), respectively.

View object contains a list of Canvas3D objects where rendering of the view is done. The method addCanvas3D(Canvas3D c) of the class View can be used to add the provided Canvas3D object to the list of canvases of the View object.

Canwas and Canvas3D.

Class Canvas3D extends the heavyweight class Canvas in order to achieve hardware acceleration, since a low rendering library, such as OpenGL, requires the rendering to be done in a native window to enable hardware acceleration.

Finally, all Canvas3D objects on the same physical display device refer to a Screen3D object, which contains all information about that particular display device. Screen3D can be obtained from the Canvas3D using the getScreen3D() method.

Default Coordinate System

The default coordinate system is a right-handed Cartesian coordinate system centered on the screen with the x and y-axes directed towards the right and top of the screen, respectively. The z-axis is, by default directed out of the screen towards the viewer, as shown below. The default distances are in meter and the angles in radians.
 

The default coordinate system.

Transformations

Class TransformGroup which extends the class Group can be used to set a spatial transformation, such as positioning, orientation, and scaling of its children through the use of a Transform3D object. A TransformGroup node enables the setting and use of a coordinate system relative to its parent coordinate system.

The Transform3D object of a TransformGroup object can be set using the method setTransform(Transform3D  t), which is used to set the transformation components of the Transform3D object to the ones of the passed parameter.

Transform3D object is a 4x4 double-precision matrix that is used to determine the transformations of a TransformGroup node, as shown in the following equation. The elements T00, T01, T02, T10, T11, T12,T20, T21, and T22 are used to set the rotation and scaling, and the T03, T13, and T23 are used to set the translation.

A Transform3D object as a 4x4 double-precision matrix.

As the scene graph is traversed by the Java® 3D renderer, the transformations specified by any transformation nodes accumulate. The transformations closer to the geometry nodes executed prior to the ones closer to the virtual universe node.

5. A Simple Java® 3D Program

A Java® 3D program builds a scene graph, using Java® 3D classes and methods, that can be rendered onto the screen.

The following program creates 2 color cubes and a sphere as shown to the snapshot that follows the code.

import java.awt.*;
import javax.swing.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.event.*;
import com.sun.j3d.utils.geometry.*;

 public class MyJava3D extends JFrame
{
   //  Virtual Universe object.
   private VirtualUniverse universe;

   //  Locale of the scene graph.
   private Locale locale;
 

   // BranchGroup for the Content Branch of the scene
   private BranchGroup contentBranch;

   //  TransformGroup  node of the scene contents
   private TransformGroup contentsTransGr;
 

   // BranchGroup for the View Branch of the scene
   private BranchGroup viewBranch;

   // ViewPlatform node, defines from where the scene is viewed.
   private ViewPlatform viewPlatform;

   //  Transform group for the ViewPlatform node
   private TransformGroup vpTransGr;

   //  View node, defines the View parameters.
   private View view;

   // A PhysicalBody object can specify the user's head
   PhysicalBody body;

   // A PhysicalEnvironment object can specify the physical
   // environment in which the view will be generated
   PhysicalEnvironment environment;

   // Drawing canvas for 3D rendering
   private Canvas3D canvas;

   // Screen3D Object contains screen's information
   private Screen3D screen;

   private Bounds bounds;
 

  public MyJava3D()
  {
    super("My First Java3D Example");

    // Creating and setting the Canvas3D
    canvas = new Canvas3D(null);
    getContentPane().setLayout( new BorderLayout( ) );
    getContentPane().add(canvas, "Center");

    // Setting the VirtualUniverse and the Locale nodes
    setUniverse();

    // Setting the content branch
    setContent();

    // Setting the view branch
    setViewing();

    // To avoid problems between Java3D and Swing
    JPopupMenu.setDefaultLightWeightPopupEnabled(false);

    // enabling window closing
    addWindowListener(new WindowAdapter() {
                       public void windowClosing(WindowEvent e)
                                           {System.exit(0); }   });
    setSize(600, 600);
    bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), Double.MAX_VALUE);
  }
 

  private void setUniverse()
    {
        // Creating the VirtualUniverse and the Locale nodes
        universe = new VirtualUniverse();
        locale = new Locale(universe);
    }

  private void setContent()
    {
        // Creating the content branch

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

        setLighting();

        ColorCube cube1 = new ColorCube(0.1);

        Appearance appearance = new Appearance();
        cube1.setAppearance(appearance);

        contentsTransGr.addChild(cube1);
 

        ColorCube cube2 = new ColorCube(0.25);

        Transform3D t1 = new Transform3D();
        t1.rotZ(0.5);
        Transform3D t2 = new Transform3D();
        t2.set(new Vector3f(0.7f, 0.6f,-1.0f));
        t2.mul(t1);
        TransformGroup trans2 = new TransformGroup(t2);
        trans2.addChild(cube2);
        contentsTransGr.addChild(trans2);
 

        Sphere sphere = new Sphere(0.2f);
        Transform3D t3 = new Transform3D();
        t3.set(new Vector3f(-0.2f, 0.5f,-0.2f));
        TransformGroup trans3 = new TransformGroup(t3);

        Appearance appearance3 = new Appearance();

        Material mat = new Material();
        mat.setEmissiveColor(-0.2f, 1.5f, 0.1f);
        mat.setShininess(5.0f);
        appearance3.setMaterial(mat);
        sphere.setAppearance(appearance3);
        trans3.addChild(sphere);
        contentsTransGr.addChild(trans3);
 

        contentBranch = new BranchGroup();
        contentBranch.addChild(contentsTransGr);
        // Compiling the branch graph before making it live
        contentBranch .compile();

        // Adding a branch graph into a locale makes its nodes live (drawable)
        locale.addBranchGraph(contentBranch);
    }

   private void setLighting()
    {
        AmbientLight ambientLight =  new AmbientLight();
        ambientLight.setEnable(true);
        ambientLight.setColor(new Color3f(0.10f, 0.1f, 1.0f) );
        ambientLight.setCapability(AmbientLight.ALLOW_STATE_READ);
        ambientLight.setCapability(AmbientLight.ALLOW_STATE_WRITE);
        ambientLight.setInfluencingBounds(bounds);
        contentsTransGr.addChild(ambientLight);

        DirectionalLight dirLight =  new DirectionalLight();
        dirLight.setEnable(true);
        dirLight.setColor( new Color3f( 1.0f, 0.0f, 0.0f ) );
        dirLight.setDirection( new Vector3f( 1.0f, -0.5f, -0.5f ) );
        dirLight.setCapability( AmbientLight.ALLOW_STATE_WRITE );
        dirLight.setInfluencingBounds(bounds);
        contentsTransGr.addChild(dirLight);
    }

  private void setViewing()
     {
        // Creating the viewing branch

         viewBranch = new BranchGroup();

         // Setting the viewPlatform
         viewPlatform = new ViewPlatform();
         viewPlatform.setActivationRadius(Float.MAX_VALUE);
         viewPlatform.setBounds(bounds);

         Transform3D t = new Transform3D();
         t.set(new Vector3f(0.3f, 0.7f, 3.0f));
         vpTransGr = new TransformGroup(t);

     // Node capabilities control (granding permission) read and write access
    //  after a node is live or compiled
     //  The number of capabilities small to allow more optimizations during compilation
         vpTransGr.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
         vpTransGr.setCapability( TransformGroup.ALLOW_TRANSFORM_READ);

         vpTransGr.addChild(viewPlatform);
         viewBranch.addChild(vpTransGr);

         // Setting the view
          view = new View();
          view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION );
          view.addCanvas3D(canvas);

          body = new PhysicalBody();
          view.setPhysicalBody(body);
          environment = new PhysicalEnvironment();
          view.setPhysicalEnvironment(environment);

          view.attachViewPlatform(viewPlatform);

          view.setWindowResizePolicy(View.PHYSICAL_WORLD);

          locale.addBranchGraph(viewBranch);
     }

  public static void main(String[] args)
  {
    JFrame frame = new MyJava3D();
    frame.setVisible(true);

  }
}My First Java3D Example.

 

A utility class, called SimpleUniverse, can alternatively be used to automatically build a common arrangement of a universe, locale, and viewing classes, avoiding the need to create explicitly the viewing branch. Then, a branch is added into the simple universe to make its nodes live (i.e. drawable).

SimpleUniverse simpleUniverse = new SimpleUniverse(canvas);
simpleUniverse.addBranchGraph(contentBranch);

6. More on Java® 3D

Java® 3D and Swing

Since the Canvas3D extends the heavyweight AWT class Canvas, it should be handled with care if Swing is used. The information provided when mixing AWT and Swing  components should be followed. The main problem is that there is one-to-one correspondence between heavyweight components and their window system peers, i.e. native OS window components. In contrast, a lightweight component expects to use the peer of its enclosing container since it does not have a peer.

When lightweight components overlap with heavyweight components, the heavyweight components are always painted on top. In general, the heavyweight Canvas3D of Java® 3D should be kept apart from lightweight Swing components using different containers to avoid problems.

To avoid heavyweight components overlapping Swing popup menus, which are lightweight, the popup menus  can be forced to be heavyweight using the method setLightWeightPopupEnabled() of the JPopupMenu class.

Similarly, problems with tooltips can be avoided by invoking the following method

ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false)

Behaviors

Behaviors are essentially Java® methods that are scheduled to run only when certain requirements are satisfied according to wakeup conditions. Although a Behavior object is connected to the scene it is kept in a separate area of the Java® 3D runtime environment and it is not considered part of the scene graph. The runtime environment treats a Behavior object differently ensuring that certain actions take place. All behaviors in Java® 3D extend the Behavior class, which is an abstract class that itself extends the Leaf class. The Behavior class provides a way to execute certain statements, provided in the processStimulus() method, in order to modify the scene graph when specified criteria are satisfied.

The Behavior class has two major methods, in particular the initialize() method, which is called when the behavior becomes live, and the processStimulus() method, which is called by the Java® 3D scheduler whenever appropriate,  and  a scheduling region. Typically, in order to create a custom behavior, the class Behavior is extended and referenced by the scene graph from an appropriate place that should be able to effect. A custom behavior that extends the Behavior class should implement the initialize() and processStimulus() methods, and provide other methods and constructors that may be needed. The Behavior class object contains the state information that is needed by its initialize()and the processStimulus() methods. A constructor or another method may be used to set references to the scene graph objects upon which the behavior acts. In addition, the Behavior class is extended by the following three classes Billboard, Interpolator, and LOD.

The initialize() method is called once when the behavior becomes "live", i.e. when its BranchGroup node is added to a VirtualUniverse, to initialize this behavior. The Java® 3D behavior scheduler calls the initialize() method, which should never be called directly. The initialize() method is used to set a Behavior object, which has been "added" to the scene graph, into a "known" condition and register the criteria to be used to decide on its execution. Classes that extend Behavior must provide their own initialize() method. The initialize() method allows a Behavior object to initialize its internal state and specify its initial wakeup conditions. Java® 3D automatically invokes a behavior's initialize code when a BranchGroup node that contains the behavior is added to the virtual universe, i.e. becomes live. The initialize() method should return, since Java® 3D does not invoke the initialize method in a new thread, and therefore it must regain control. Finally, a wakeup condition must be set in order to be able to invoke the processStimulus() method of the behavior.

However, a Behavior object is considered active only when its scheduling bounds intersect the activation volume of a ViewPlatform node. Therefore, the scheduling bounds should be provided for a behavior in order to be able to receive stimuli. The scheduling bounds of a behavior can be specified as a bounded spatial volume, such as a sphere, using the method setSchedulingBounds(Bounds region). Bounds are used for selective scheduling to improve performance. Bounds are used to decide whether a behavior should be added to the list of scheduled behaviors.

The processStimulus() method is called whenever the wakeup criteria are satisfied and the ViewPlatform's activation region intersect with the Behavior's scheduling region. The method is called by the Java® 3D behavior scheduler when something happens that causes the behavior to execute. A stimulus, i.e. a notification, informs the behavior that it should execute its processStimulus() method. Therefore, applications should not call explicitly this method. Classes that extend the Behavior class must provide their own  processStimulus() method. The scheduling region defines a spatial volume that serves to enable the scheduling of Behavior nodes. A Behavior node is active, i.e. it can receive stimuli, whenever its scheduling region intersects the activation volume of a ViewPlatform.

The Java® 3D behavior scheduler invokes the processStimulus() method of a Behavior node when its scheduling region intersects the  activation volume of a ViewPlatform node and all wakeup criteria  of that behavior are satisfied. Then, the statements in the processStimulus() method may perform any computations and actions, such as including the registration of state change information that could cause Java® 3D to wake other Behavior objects and modify node values within the scene graph, change the internal state of the behavior, specify its next wakeup conditions, and exit. It is allowed to a Behavior object to change its next trigger event. The processStimulus() method, typically, manipulates scene graph elements, as long as the associated capabilities bits are set accordingly. For example, a Behavior node can be used to repeatedly modify a TransformGroup node in order to animate the associated with the TransformGroup node objects.

The amount of work done in a processStimulus() method should be limited since the method may lower the frame rate of the renderer. Java® 3D assumes that Behavior methods run to completion and if necessary they spawn threads.

The application must provide the Behavior object with references to those scene graph elements that the Behavior object will manipulate. This is achieved by providing those references as arguments to the constructor of the behavior when the Behavior object is created. Alternatively, the Behavior object itself can obtain access to the relevant scene graph elements either when Java® 3D invokes its initialize() method or each time Java® 3D invokes its processStimulus() method. Typically, the application provides references to the scene graph objects that a behavior should be able to access as arguments to its constructor when the Behavior is instantiated.

Java® 3D assumes that Behavior methods always run to completion and that if needed they can spawn threads. The structure of each Behavior method consists of the following parts:

  • code to decode and extract references from the WakeupCondition enumeration that awoke the object
  • code to perform the manipulations associated with the WakeupCondition
  • code to establish new WakeupCondition for this behavior
  • a path to exit, so that execution returns to the Java® 3D behavior scheduler

The WakeupCondition class is an abstract class that specifies a single wakeup condition. It is specialized to 14 different WakeupCriterion subclasses and to 4 subclasses that can be used to create complex wakeup conditions using boolean logic combinations of individual  WakeupCriterion objects. A Behavior node provides a WakeupCondition object to the Java® 3D behavior scheduler using its wakeupOn() method. When that WakeupCondition is satisfied, while the scheduling region intersects the activation volume of a ViewPlatform node, the behavior scheduler passes that same WakeupCondition back to the Behavior via an enumeration.

Java® 3D provides the following wakeup criteria that Behavior objects can use to specify a complex WakeupCondition. All of the following are subclasses of the WakeupCriterion class, which itself is a subclass of the WakeupCondition class.

A Behavior object constructs a WakeupCriterion by providing the appropriate arguments, such as a reference to some scene graph object and a region of interest.

Multiple criteria can be combined using the following classes to form complex wakeup conditions.

  • WakeupOr: specifies any number of wakeup conditions logically ORed together
  • WakeupAnd: specifies any number of wakeup conditions logically ANDed together
  • WakeupOrOfAnds: specifies any number of OR wakeup conditions logically ANDed together
  • WakeupAndOfOr:  specifies any number of AND wakeup conditions logically ORed together

The class hierarchy of the WakeupCondition class is shown below:

Class hierarchy of the WakeupCondition class.

The following code provides an example of setting a WakeupCondition object

    public void initialize()
       {
           WakeupCriterion criteria[] = new WakeupCriterion[2];
           criteria[0] = new WakeupOnElapsedFrames(3);
           criteria[0] = new WakeupOnElapsedTime(500);

            WakeupCondition condition = new WakeupOr(criteria);
           wakeupOn(condition);
      }

Behavior node provides a WakeupCondition object to the behavior scheduler via its wakeupOn() method and the behavior scheduler provides an enumeration of that WakeupCondition. The wakeupOn() method should be called from the initialize() and processStimulus() methods, just prior of exiting these methods.

In the current Java® 3D implementation the behavior scheduler, and, therefore, the processStimulus method of the Behavior class as well, run concurrently with the rendering thread. However, a new thread will not start until both the renderer, which may be working on the previous frame, and the behavior scheduler are done.

Java® 3D guarantees that all behaviors with a WakeupOnElapsedFrames will be executed before the next frame starts rendering, i.e. the rendering thread will wait until all behaviors are done with their processStimulus methods before drawing the next frame. In addition, Java® 3D guarantees that all scene graph updates that occur from within a single Behavior object will be reflected in the same frame for consistency purposes.

Finally, Interpolator objects can be used for simple behaviors where a parameter can be varied between a starting and an ending value during a certain time interval.

Lights

Lights can be used in order to achieve higher quality and realism of the graphics. Lighting capabilities is provided by the class Light and its subclasses. All light objects have a color, an on/off state, and a bounding volume that controls their illumination range. Java3D provides the following four types of lights, which are subclasses of the class Light:

  • AmbientLight: the rays from an ambient light source object come from all directions illuminating shapes evenly
  • DirectionalLight: a directional light source object has parallel rays of light aiming at a certain direction
  • PointLight: the rays from an point light source object are emitted radially from a point to all directions
  • SpotLight: the rays from a spot light source object are emitted radially from a point to all directions but within a cone

6. Performance of Java® 3D

Java® 3D aims at achieving high performance by utilizing the available graphics libraries (OpenGL/Direct3D), using 3D-graphics acceleration where available, and supporting some rendering optimizations (such as scene reorganization and content culling). It is optimized for performance rather than quality of image rendering. Compilation of branch groups and utilization of capability bits enable speed optimizations. It is as fast and high-level as Open Inventor and VRML (Virtual Reality Modeling Language), while it offers the portability of Java® and the direct access and well integration with all other available Java® APIs. Java® 3D uses native code of certain libraries, such as the OpenGL, at the final steps of rendering to achieve satisfactory performance levels. A scene reorganization and a content culling may be used by the renderer to optimize rendering by following an optimal order that bypasses hidden parts of the scene.

Java® 3D rendering is tuned to the underlying hardware utilizing a wide range of hardware and software platforms. Java® 3D is scalable, taking advantage of multithreading capabilities of Java® when multiple processors are available. The availability of multiple processors is automatically utilized by its independent and asynchronous components, such as the rendering thread and the behavior scheduler, that can be assigned to different processors. Also, branches of the scene tree-structure can be manipulated independently and concurrently utilizing multithreading and multiprocessing.

A thread scheduler was implemented inside the Java® 3D, providing to the Java® 3D architects full control of all threads and eliminating the need to deal with thread priorities. the underlying architecture uses messages to propagate scnegraph changes into certain structures that are used to optimize a particular functionality. There are two structures for geometric objects. The one organizes the geometry spatially enabling spatial queries on the scene graph, such as picking, collisions, culling etc. The other structure is a state snapshot of the scene graph, known as render bin, which is associated with each view and is used by the renderer thread. There is also a structure associated with behaviors that spatially organizes behavior nodes, and a behavior scheduler thread that executes behaviors that need to be executed.

The thread scheduler is essentially in a big infinite loop implemented inside the Java® 3D. For each iteration, the thread scheduler runs each thread that needs to be run once, waiting for all threads to be completed before entering the next iteration. The behavior and the rendering threads may run once in a single iteration. The following operations are conceptually performed within this infinite loop.

    while(true)
            {
               process input

              if(there is a request for exit)
                 break

              perform any behaviors

              transverse scene graph and render visible objects
           }

Whenever a node of the scene graph is modified a message is generated with a value associated with it and any state necessary to reflect the specific changes, and queued with all other messages by the thread scheduler. At each iteration the messages are processed and the various structures are updated accordingly. The update time is very fast when the messages are very simple, which is typically the case. In the current implementation the rendering thread and the behavior thread can run concurrently. In particular, the behavior scheduler and therefore the processStimulus method of a Behavior object, can run concurrently with the renderer. However, a new frame will not start until both the rendering of the previous frame and the behavior scheduler are done.

Finally it offers level-of-detail (LOD) capabilities to further improve performance using a LOD object. An LOD leaf node is an abstract class that operates on a list of Switch group nodes to select one of the children of the Switch nodes. The LOD class is extended to implement various selection criteria, such as the DistanceLOD subclass.