home   previous   next  contents

Shout3D™ 2.0 - User Guide


Programming Interactivity with the Shout3D API

The Shout3D Application Programming Interface

Shout3D is, at bottom, a Java class library. The methods of that library constitute the Shout3D Application Programming Interface (API). By using these methods, you can realize almost unlimited possibilities for user interactivity with Shout3D content. You can go further and extend this class library to create new classes with new methods.

The primary means of utilizing the API is through direct Java programming. The programmer extends the basic Shout3DApplet and Shout3DPanel classes to create new classes that implement interactivity using the methods in the API. This is the standard approach, and the only one suitable for commercial-quality work that will run reliably on all platforms. However, it is also possible to use JavaScript to call the methods in the Shout3D API. This approach, while convenient for testing ideas, will not generally be satisfactory for commercial work and will not work on all platforms.

The Shout3D API is documented in the online Javadocs. The Javadocs document the complete Shout3D class library, with all fields, constructors and methods for each class. A strong working familiarity with the Javadocs is the basis of all productive use of the Shout3D API.

IMPORTANT NOTE: The discussion of programming with the Shout3D API in this User Guide is high-level and assumes a working knowledge of object-oriented programming in the Java language. For a complete introduction to the use of the Shout3D API, refer to the Shout Book. The Shout Book assumes no previous knowledge of Java programming and contains extensive projects and exercises to get you started in all aspects of Shout interactivity programming.

Shout3D Applets and Panels

Although we speak extensively of Shout3d "applets," the actual Shout3DApplet class object is used as a container for the 3D viewing space that is implemented as a Shout3DPanel class object. Thus all Shout3D projects consist of a pair of classes.

A quick look through the Shout3d_runtime/codebase/applets directory of your Shout3D installation will confirm this. For example, a project that makes use of the ExamineApplet for interactivity uses the ExamineApplet.class and ExaminePanel.class pair. Note how the source code for this and all other custom applet-panel pairs is provided for your reference (e.g. ExamineApplet.java and ExaminePanel.java).

In almost all of the projects included as demos with your Shout3D installation, the Shout3DPanel 3D viewing window completely fills the space defined by the Shout3DApplet. In such a case, all of the interactivity programming occurs in the Shout3DPanel class. However, the ExamineWithButtonsDemo is an example of a project in which the Shout3DPanel is set within a larger applet. User input is implemented through buttons positioned in the surrounding applet space. In this type of project, interactivity programming occurs in both the Shout3DApplet and Shout3DPanel classes.

Extending the Shout3D Applet

Unless the applet space will be larger than that of the 3d viewing window and will implement interactivity controls, extending the Shout3DApplet is a trivial exercise. You need only open the source code of a simple applet in the applets directory and:

  1. change the name of the class in the class declaration
  2. change the name of the corresponding panel in the initShout3DPanel() method
  3. save the source code under the new class name
For example, the complete source code for a class named MyBigApplet would read:
package applets;
import  shout3d.*;

public class MyBigApplet extends Shout3DApplet {


   public void initShout3DPanel(){
      panel = new MyBigPanel(this);
   }
	
}//end of class

For a useful example of how simple button interface can be implemented in an applet space surrounding a panel, look at the source code for ExamineWithButtonsApplet.java in your applets directory. Note that Java 1.02 event handling methods are used in this source code. Even though Shout3D is a Java 1.1 class library, the Java Virtual Machines in some Java 1.1 Web browsers do not in fact support Java 1.1 event handling.

Extending the Shout3DPanel

Most interactivity programming is implemented in an extended Shout3DPanel class. The extended class can implement the DeviceObserver interface to handle mouse and keyboard events from the user. These events are handled in a method named onDeviceInput(). The extended Shout3DPanel class can also make use of the render loop through the RenderObserver interface. This interface consists of the onPreRender() and onPostRender() methods, which are automatically called immediately before or after each render cycle. Initialization duties, such as obtaining references to objects in the scene, are handled by overriding the customInitialize() method of the Shout3DPanel class.

A characteristic template (with mostly empty methods) for an extended Shout3DPanel class might look like this:

package applets;

import shout3d.*;
import shout3d.core.*;
import shout3d.math.*;


public class MyBigPanel extends Shout3DPanel implements DeviceObserver, RenderObserver{
   
   
   //fields to be placed here





   //the constructor

   public MyBigPanel (Shout3DApplet applet){
      super(applet);
   }
   
   

   //called immediately after scene is loaded

   public void customInitialize() {
      addDeviceObserver(this,"MouseInput", null);
      getRenderer().addRenderObserver(this, null);      
   }



   //cleanup actions performed when viewer closes applet

   protected void finalize()  { 
      removeDeviceObserver(this,"MouseInput");
      getRenderer().removeRenderObserver(this);
   }



   //method from DeviceObserver interface,
   // used to handle user input--
   //will only receive mouse input as registered   

   public boolean onDeviceInput(DeviceInput di, Object userData) {
      return false;      
   }


   //method from RenderObserver interface,
   // called before each frame is rendered   

   public void onPreRender (Renderer r, Object o) {

   
   }
   


   //method from RenderObserver interface,
   // called after each frame is rendered   

   public void onPostRender (Renderer r, Object o) {
   
   
   }

   

} //end of class   

Obtaining References to Scene Nodes

The essence of interactivity is control over values in class objects representing nodes in the 3D scene graph. For example, a user can move a geometric object only if the panel class has access to the Transform node controlling that geometry. The panel class must therefore create a Transform class object reference variable, and must obtain a reference to place in that variable. Once the reference is obtained, the fields of the Transform node can be changed by means of the reference.

The primary means of obtaining such a reference is the getNodeByName() method of the Shout3DPanel class. This method takes the DEF name of the desired node in the .s3d or .wrl scene file as an argument, and returns a reference to the Java class object.

Assume that the .s3d or .wrl scene file contains a Transform node as follows:

DEF TRANS Transform {
    translation  10 0 0
    ….
}

In order to take control of this Transform, the extended Shout3DPanel class must create a field to hold the reference. For example,

Transform xform;

The reference is then obtained during the initialization process.

public void customInitialize() {
   xform = (Transform) getNodeByName("TRANS");
   ….
}

Note that the name of the Java reference variable need not be the same as the DEF name of the node in the scene file. Note also the need to cast the node reference returned by the method to the specific node subclass represented by the reference.

Once the reference is obtained, it can be used to change any Transform values. For example, to move the object from (10, 0, 0) to (15, 0, 0), we could call the appropriate setter method.

xform.translation.set1Value(0, 15);

Consult the Javadocs for information about getting and setting fields in node classes (such as the translation field of a Transform node.

Handling User Input

The extended Shout3DPanel class registers to receive user input by calling the addDeviceObserver() method in its customInitialize() method. You can register to receive only mouse or keyboard input, or both.

When the user takes some action which the panel class is registered to receive, the onDeviceInput() method is called, and is passed a DeviceInput object containing information about the event. The onDeviceInput() method examines the DeviceInput object to determine what happened, and can respond accordingly.

For example, assume that the panel is registered to receive both mouse and keyboard input. The onDeviceInput() method might be structured as follows:

public boolean onDeviceInput(DeviceInput di, Object userData) {
     
      //if mouse input
      if (di instanceof MouseInput) {
         MouseInput mi = (MouseInput) di;
         switch (mi.which){

            case MouseInput.DOWN:
               //do something
               return true;
            
            case MouseInput.UP:
               //do something
               return true;
   
            case MouseInput.DRAG:

               //if left button used
               if(mi.button == 0) {
                    //do something
                  return true;
               }

                 //if right button used
               if(mi.button == 1) {
                    //do something
                  return true;
               }
         
         }//end of switch

         return false;
         
      }//end of mouse input
      

      //if keyboard input
      if (di instanceof KeyboardInput) {

         KeyboardInput ki = (KeyboardInput) di;
         if (ki.which == KeyboardInput.PRESS) {
            if (ki.key == 'n' || ki.key == 'N') {
               //do something
               return true;
            }


            if (ki.key == '1') {
               //do something
               return true;
            }

            return false;
         }//end of key PRESS

         return false;

      }//end of keyboard input

      return false;

   }//end of onDeviceInput()

Consult the Javadocs for information about the fields in the DeviceInput class and its subclasses, MouseInput and KeyboardInput.

Using the Render Loop

The concept of a render loop is central to realtime interactive 3D graphics. The renderer renders as fast as it can, given the complexity of the scene and power of the user's system. Rendering times vary even within a given scene. Accurate animation depends on measuring how much time has passed since the last render cycle and determining by how much scene values should change in that duration.

For example, assume you wish to translate an object in the x direction at a constant speed whenever the user holds down the mouse button. Pressing down the mouse sets a Boolean flag name moving to "true." The onPreRender() method can be used to translate the object the proper amount before each frame renders, if moving==true.


public void onPreRender (Renderer r, Object o) {
 
  if (moving) {     
      float delta = speed/getFramesPerSecond();
      float temp = xform.translation.getValue()[0]  + delta;
      xform.translation.set1Value(0, temp);
  }

}//end of onPreRender()

The expression speed/getFramesPerSecond() computes the amount that the object will have moved since the previous frame. This increment is added to the current x position of the object to get the current position. The translation field of the Transform node is then set to that value. When the scene renders, the object will be in the proper position, creating the impression of movement.

Another important use of the onPreRender() and onPostRender() methods is to change scene values in direct response to user input, such as mouse clicks or drags. If such changes are made in the onDeviceInput() method, they may occur irregularly over multiple render cycles, and may produce incorrect looking results. When changes to scene values are made in onPreRender() or onPostRender(), you can be certain that they occur precisely between two render cycles.

Using JavaScript

With most (but not all) browsers, JavaScript in an HTML page can be used to call methods in the Shout3D API and therefore implement interactivity and procedural animation without writing and compiling extended Shout3DApplet and Shout3DPanel classes. This technique is addressed in the Shout3D Workflow Tutorial.

In order to obtain references to objects in the scene, you can make use of the standard getNodeByName() method of the Shout3DApplet class. This method necessarily requires a reference to the Shout3DApplet object. In JavaScript, this reference is obtained by assigning a name to the applet in the <APPLET> tag. You should also include the MAYSCRIPT attribute when you use JavaScript.


<APPLET MAYSCRIPT NAME="myapplet" CODEBASE="../codebase" 
CODE="shout3d/Shout3DApplet.class" ARCHIVE="shout3dClasses.zip"
WIDTH=320 HEIGHT=240>
<param name="src" value="models/modswing.s3d;
</APPLET>

References to nodes can then be obtained using the DEF name in the scene file. For example:

var xform = document.myapplet.getNodeByName("TRANS");

JavaScript handles arrays differently than does Java, and therefore special methods are provided to get and set node fields that are arrays of values (such as the x,y,z values in the translation field of a Transform node). The idea is to communicate with JavaScript arrays using character strings. For example, you can set the three floating point values in the translation field with a string.

xform.translation.setValueByString(positionString);

This assumes that you have a character string version of the array. Look at the source code in the javascript demo for a JavaScript method named getStringFromFloatArray(). By copying this method into your HTML page, you can write procedures such as:

var posArray = new Array(3);
posArray[0] = 10.5;
posArray[1] =  4.3;
posArray[2] = 6.7
var positionString = getStringFromFloatArray(posArray);
xform.translation.setValueByString(positionString);

Conversely, you can get array values as strings (using Shout3D API methods) and convert such strings to float arrays using the JavaScript method getFloatArrayFromString()in the javascript demo.

var posString = xform.translation.getValueByString();
var posArray = getFloatArrayFromString(posString);

There are a number of javascript examples in the demos directory that show how to do a variety of different things.  Look in Shout3d_runtime/demos for html files whose names begin with "javascript."


Copyright© 1999-2000, Eyematic Interfaces, Inc.