http://tecfa.unige.ch/ico/navi/tex2html/top.gifhttp://tecfa.unige.ch/ico/icons/vrml97-icon.gif next up previous contents index
Next: 6.4 Extracting Information from Up: 6. The External Authoring Previous: 6.2 The very first

Subsections


   
6.3 Essential EAI tricks

Now you already know how to get a reference (handle) to the browser. This is a necessary step in any situation. In the next subsections we will discuss how to do the following:

1.
Getting a reference to a VRML node: Not necessary in any case, we will use this technique in most our introductory examples. Once you have a handle on a VRML node you can inspect and manipulate its Event fields or even do the same for the children nodes. (See section 6.3.1.)

2.
Writing and reading values to the Event fields of a VRML node. (See sections 6.3.1, 6.3.2, 6.3.3).

3.
Generating VRML from strings, replacing scenes, adding and removing nodes. (See sections 6.3.4 and 6.3.5)

4.
Receiving Events from the Scene (call-back from VRML). (see section 6.3.6.)

Those basic operations are illustrated in this figure.

eai-html.gif

   
6.3.1 Getting a reference to a VRML node and writing Event values

For operations such as reading or sending information to a VRML node you first will need a reference to this node. In this subsection we will teach you first how to get a reference to a named node that exists in the VRML scene, a reference to its EventIn field we want to set and some elementary Java widget programming.

Let's look first at the following simple VRML scene. It will display a red ball. Now we want to be able to change its color with a few simple buttons from the Java applet.

DEF Ball Transform {
   children [
      Shape {
         appearance Appearance {
            material DEF MAT Material {
               diffuseColor 0.8 0.2 0.2
            } }
         geometry Sphere {}
      }
     ] }

In order to do so, we must first get a reference to the node which defines the color, i.e. DEF MAT Material {...}. Next, we need a reference to the set_diffuseColor field. Finally, we we must be able to set a value to this EventIn field. The following example just does this:

Example 6.3.1   EAI: RGB Change Applet  

Contents: getNode(), getEventIn()
HTML page: eai/rgb-change/rgb-change.html
JAVA Code: eai/rgb-change/RGBChange.java
Directory: (dir)

Here is the relevant code in the Java applet for getting the node and field references:



  .....
  Node material = null;
  EventInSFColor diffuseColor = null;
  ......

   public void init() {
    ........
    // Get the material node...
    material = browser.getNode("MAT");
    // Get the diffuseColor EventIn
    diffuseColor = (EventInSFColor) material.getEventIn("set_diffuseColor");
    .......
  }


Java programming comments:

Java Newbies need some more explanation (others can skip this): The reference to the VRML browser is hold by a variable we called ``browser''. It has been initialized of type ``Browser''. Browser is a class that defines the ``getNode'' method allowing to retrieve named nodes from the VRML scene. In a similar way we assigned the result of browser.getNode("MAT") to the material variable we declared as type Node. Let's just show you the definition of the ``Node'' class from the EAI Spec (in Java code format):

  public Node          getNode(String name)
       throws InvalidNodeException;
This means that getNode (a) wants as argument a node name (the string of a DEF), (b) returns an instance of a Node class (i.e. the reference to a node) unless it fails in which case it signals an InvalidNodeException. More later about this latter case.

Now the ``Node'' class defines a method called getEvent that will return an instance of the EventIn class. However you must cast (i.e. translate) this instance to the appropriate class you are interested in. The reason is that getEventIn can return different types of values. In our example we cast to (EventInSFColor) in the following line:

    diffuseColor = (EventInSFColor) material.getEventIn("set_diffuseColor");
You have to do this everytime you deal with some event fields. The EAI Spec is pretty self explaining about those types. So far, we only have the handles on VRML things we want to manipulate. In order to obtain what we want we now must proceed with some ordinary Java coding. If you look at the Java code and the applet you can see that we painted three simple buttons:
   // paint 3 simple Java buttons
    add(new Button("Red"));
    add(new Button("Green"));
    add(new Button("Blue"));

The Applet class which is imported from java.applet.* is a child of the java.awt.Component class which defines a simple action event method. This method is activated (i.e. called) with the Event and the Object triggered when the user clicks on a button. The programmer now has to deal with the event, i.e. implement an ``action'' method for this applet. Since the ``action'' method is depreciated in Java 1.1. we will not explain much here, but revise the code once most Netscape and VRML plug-in users will have versions that fully support Java 1.1.

Java Event handling and writing to the VRML scene:

Let's just look at the VRML related part for dealing with the Blue button:

  float[] color = new float[3];
    .......
  public boolean action(Event event, Object what) {
    // handle Java user actions
    if (event.target instanceof Button)
      {
        Button b = (Button) event.target;
        ..........
        color[0] = 0.2f; color[1] = 0.2f; color[2] = 0.8f;
        diffuseColor.setValue(color);
        ......
      }
    return true;
   }
In the prologue code of the class (see just above) we did declare a ``color'' variable of type array that can take take three floating point elements. All we have to do after identifying which button has been clicked on is the following:
1.
assign values to the color array:
2.
write the value to VRML scene with the setValue() method. This method is defined by the EAI's EventInSFColor class.
The ``setValue'' method of the ``EventInSFColor'' class wants an array argument i.e. the Java equivalent of the VRML SFColor triple. The action() method is called whenever the user does something and the first lines of codes will identify events generated by clicking on a button.

Let's summarize what you have learned:

1.
Getting a node reference with Browser.getNode()

2.
Getting a reference to an EventIN field with Node.getEventIn()

3.
Setting a value of an event field with setValue()

   
6.3.2 Getting a reference to a VRML node and writing Event values II

Now let's make our Applet a bit more bullet proof. The getNode and getEventIn method will throw exceptions that we could handle, e.g. by giving some feedback to the user and disabling Java code that depends on references not found:

Example 6.3.2   EAI: RGB Change Test Applet  

Contents: getNode(), getEventIn(), Java: try/catch
HTML page: eai/rgb-change/rgb-change-test.html
JAVA Code: eai/rgb-change/RGBChangeTest.java
Directory: (dir)

The following code will implement what we do when references are not found. Java's ``try'' statement allows us to define ``catch'' clauses that are called by the Java interpreter when something is wrong. (Recall the definition of the the getNode discussed in the previous section 6.3.1).



    try {
      // Get the material node...
      material = browser.getNode("MAT");
      // Get the diffuseColor EventIn
      diffuseColor = (EventInSFColor) material.getEventIn("set_diffuseColor");
    }
    catch (InvalidNodeException ne) {
      add(new TextField("Failed to get node:" + ne));
      error = true;
    }
    catch (InvalidEventInException ee) {
      add(new TextField("Failed to get EventIn:" + ee));
      error = true;
    }


Lines like:
      add(new TextField("Failed to get node:" + ne));
will paint a text field on the Java Applet with the error message we defined. (Note, that we could just have written something to the Java Console instead.) In addition, the catch statements will set the ``error'' variable to true. If true the action method will not try to handle any events, i.e. it will just exit. Below is the the code (in old Java 1.0.x style) that whill handle the event.
public boolean action(Event event, Object what) {
    if (error)
      {
        showStatus("Problems! Had an error during initialization");
        return true;  // Uh oh...
      }
     .......

Finally note the usage of the ``showStatus'' method which will print a message on the status bar (the bottom) of your WWW client window. If you now want to see how this works play with the following example which shows a Java Applet where ``MAT'' it misspelled as ``MATS'' in the code:

Example 6.3.3   EAI: RGB Change Test Error Applet with an error  

Contents: getNode(), getEventIn(), Java: try/catch
HTML page: eai/rgb-change/rgb-change-err.html
JAVA Code: eai/rgb-change/RGBChangeErr.java
Directory: (dir)

   
6.3.3 Reading Events from the Scene

The next example deals with reading information from a VMRL scene. Note that this is not the same as receiving information (the equivalent of VRML's ROUTEing) which we shall introduce later.

The scenario of our next examples deals with a (very) simple ``where I am'' problem. The user can click on a button an it will tell him some information about the EntryView Point.

Example 6.3.4   EAI: Get Node Info Demo  

Contents: getEventOut(),
HTML page: eai/get-node-info/get-node-info-s.html
JAVA code: eai/get-node-info/GetNodeInfoS.java
Directory: (dir)

Retrieving Information from a VRML scene is very much the same as writing information, something we learned in the previous section 6.3.1. We also need a handle to an Event field. In this case we will read from EventOut fields. Remember that: EventOut and not just the field. Therefore ordinary VRML exposed fields can be accessed by something like <handle>.getEventOut("<field_name>_changed") as in the lines below:

  EventOutSFVec3f positionVP = null;
  EventOutSFRotation orientationVP = null;
  ......
  positionVP = (EventOutSFVec3f) entryVP.getEventOut("position_changed");
  orientationVP = (EventOutSFRotation) entryVP.getEventOut("orientation_changed");

Once we got the handle we can read. In the action method of our example we get the position and the orientation of a Viewpoint with the following lines of code:

  currentPosition = positionVP.getValue();
  currentOrientation = orientationVP.getValue();

Writing into a Java text field:

Again some comments for Java newbies: The following defines a text widget to which we can print text:

  TextArea output = null;
  .....
  public void init() {
    ......
    output = new TextArea(5, 40);
    add(output);
    ......
   }

   public boolean action(Event event, Object what) {
    .......
        output.appendText("Entry ViewPoint is at" +
                          " x=" + currentPosition [0] +
                          " y=" + currentPosition [1] +
                          " z=" + currentPosition [2] + "\n");
     .....
  }
Such a thing is definitively better than telling the user to open up the Java Console.

Error handling again:

The next example does exactly the same, but includes error handling. Of new interest is only the ``InvalidEventOutException'':

  public void initScene() {
    try {
    .......
      positionVP = (EventOutSFVec3f) entryVP.getEventOut("position_changed");
      orientationVP = (EventOutSFRotation) entryVP.getEventOut("orientation_changed");
    }
    .........
    catch (InvalidEventOutException ee) {
      System.out.println("InvalidEventOutException: " + ee);
      die("initScene: EventOut Field not found!");
      return;
    }   }
Also note that that a ``die'' method has been defined that is called when encountering an error. This adds some better modularization to the code.

Finally you can look at a minimal implementation of the ``start'', ``stop'' and ``destroy'' methods that are called by the Java interpreter.

Example 6.3.5   EAI: Get Node Info Demo with Error Handling  

Contents: getNode(), getEventOut(), Java: try/catch
HTML page: eai/get-node-info/get-node-info.html
JAVA code: eai/get-node-info/GetNodeInfo.java
Directory: (dir)

   
6.3.4 Create Vrml from String

In this subsection you will learn how to create a VRML scene from a string and to replace the existing scene in the browser. Let's start with an empty world, i.e. all there is in the *.wrl file is a line that will get the plug-in going:


#VRML V2.0 utf8


The following example will paint a blue ball as soon as the applet starts up:

Example 6.3.6   EAI: Creating and inserting a simple VRML tree  

Contents: createVrmlFromString()
HTML page: eai/create/create-ex0.html
JAVA code: eai/create/CreateEx0.java
Directory: (dir)

A Vrml scene (or tree) is represented in the Java Applet as as an array of Nodes which we will initialize as follows:

  Node[] scene = null;

The Browser class' createVrmlFromString method will take a simple string containing ordinary VRML statements and then create an array of VRML nodes from it. In this example we just add simple strings together, but of course you also could produce strings from variables and methods to program some more useful applet. The code below will just create a few VRML objects.

     scene = browser.createVrmlFromString(
     "DEF Camera Viewpoint {\n" +
          "  position 0 0 5}\n" +
     "DEF MySphere Transform {\n" +
          "   children [ \n" +
      ......
          "     ] \n" +
          " } \n"
          );

Once the scene array contains VRML nodes (represented as Java instances) the rest is very simple. We can replace the current (in this case empty) VRML scene by the nodes in the new scene array with the replaceWorld method.

      browser.replaceWorld(scene);
Note that we also could have added these nodes to a children field of an existing scene without replacing everything else. With what you already learned you ought to be able to set a value to a ``addChildren'' field of a grouping node, but we will demonstrate it anyhow in section 6.3.5.

The next example is more complex, because it will separately create a series of VRML nodes and then assemble them together by setting respective EventIn fields. If you do not understand what it does you should read again the previous sections or just move on and come back later.

Example 6.3.7   EAI: Creating and assembling a VRML tree  

Contents: createVrmlFromString()
HTML page: eai/create/create-ex.html
JAVA code: eai/create/CreateEx.java
Directory: (dir)

A more sophisticated example from which the above has been inspired can be found in the EAI specification. A local copy is below. You can study it yourself :)

Example 6.3.8   EAI: Creating and assembling a VRML tree  

Contents: createVrmlFromString()
HTML page: eai/create-test/create-test.html
JAVA code: eai/create-test/CreateTest.java
Directory: (dir)

   
6.3.5 Create, Put and Remove together

In this subsection we pull together most of what we have learned so far. This applet will allow the user to add a (VrmlFromString created) ball to an exiting Group node and also to remove it. The Vrml we originally start with is very simple:


#VRML V2.0 utf8

DEF Camera Viewpoint {
    position 0 0 7
}
DEF ROOT Group {}


As you might remember from section 4.4 on page [*], nodes like =>Group and Transform define the EventIn addChildren and the EventOut removeChildren allowing you to add and remove objects from children. We will make use of this feature in the next example.

Example 6.3.9   EAI: Add and remove an object  

Contents:  
HTML page: eai/add-remove/add-remove1.html
JAVA code: eai/add-remove/AddRemove.java
Directory: (dir)

If you understood the previous subsections there is not much explaining needed. Look first at these lines in the init() method:

   // Get root node of the scene
    root = browser.getNode("ROOT");

    // Instantiate (get handle to) the EventIn objects
    addChildren = (EventInMFNode) root.getEventIn("addChildren");
    removeChildren = (EventInMFNode) root.getEventIn("removeChildren");
Note that root.getEventIn("addChildren") and root.getEventIn("removeChildren") produce EventInMFNodes.

In the action() method we then simply setValue() the node we want to remove or to add. Note that shape is an array that holds the result of the createVrmlFromString() operation.

    if (b == addButton) {
        addChildren.setValue(shape);
      }
    else if (b == removeButton) {
        removeChildren.setValue(shape);
      }

This example is just a simplified version of the one found in the EAI spec. The original adds some error checking and feedback and you can look at it in the example below. Also, if the example above won't work on your browser, the one below will print out the reasons for you :)

Example 6.3.10   EAI: Add and remove test  

Contents:  
HTML page: eai/add-remove-test/AddRemoveTest.html
JAVA code: eai/add-remove-test/AddRemoveTest.java
Directory: (dir)

   
6.3.6 Receiving Events from the Scene

The last element we consider basic and essential in the EAI Spec is receiving Events from the Scene which as bit analogous to the ROUTE statement used with animated VRML and scripting. It is not the same as reading something from a scene which was discussed for example in subsection 6.3.3.

In the example we will discuss a simple TouchSensor example. When the user clicks on the cube it will change color (something that usually would not need the EAI).

Example 6.3.11   EAI: Monitor and act upon a touch  

Contents: EventOutObserver
HTML page: eai/monitor-test/monitor-touch.html
JAVA code: eai/monitor-test/MonitorTouch.java
Directory: (dir)

So what's new ? Our applet in order to receive call-backs from the VRML scene needs to implement an EventOutObserver Interface. It can be done like this:

public class MonitorTouch extends Applet implements EventOutObserver
Of course you also could define a subclass implementing EventOutObserver but it would be slightly more complicated. In any case, you must write a callback method for the EventOutObserver class in order to process events. This method takes three arguments: the EventOut, the time and some user definable data. Let's look at the code:

  public void callback(EventOut who, double when, Object which) {
    
    // retrieve the state of isActive (see above the advise function)
    EventOutSFBool state = (EventOutSFBool) which;
    
    // only deal with the event if isActive was true, else the ball would flip back
    if (state.getValue() == true)  {
      System.out.println("callback(): EventOut=" + who
                         + " state =" + state.getValue());
      // Change the color and remember it
      if (colorState == 1) {
        diffuseColor.setValue(redColor);
        colorState = 0;
      }
      else {
        diffuseColor.setValue(greenColor);
        colorState = 1;
      }
    }
  }


In our case we do not need to deal with the EventOut object nor with the time stamp. The which variable contains any kind of Java Object that we decided to pass over from the VRML scene. In this example we passed isActive, i.e. an EventOutSFBool object related to the TouchSensor. To set up the call-back we had to add the following lines to the init() method:
   ....
    // Get the Touch Sensor
    Node sensor = browser.getNode("TOUCH");
    // Get its touchTime EventOut
    isActive = (EventOutSFBool) sensor.getEventOut("isActive");
    // Set up the callback
    isActive.advise(this, isActive);
    ....
As you can see, EventOuts do have an advise method that is used to tell (advise) the class, that this EventOut will be used for a call-back. Let's go back to the callback() method listed above. If you remember how to deal with state and isActive Events from a TouchSensor (see section 5.3.2 on page [*]) you know that isActive will both generate true and false events depending on whether the user clicks or releases a mouse button. Here again, we are just interesting in the ``down'' click:
    EventOutSFBool state = (EventOutSFBool) which;
    // only deal with the event if isActive was true ...
    if (state.getValue() == true)  { .... }
The rest is fairly easy. If the ball is green (remembered within our Java code as colorState) we paint it red, else green. The following line should be meaningful to you, else go back to subsection 6.3.1.
     diffuseColor.setValue(redColor);
Voila, that's it. We have written an invisible applet that does something we have been doing before with Javascript (or that could be done with the Java Script node).

Other examples:

The following example will monitor the output of a TimeSensor Node and the rotation field of a spinning cube.

Example 6.3.12   EAI: Monitor a Rotation  

Contents: EventOutObserver
HTML page: eai/monitor-test/monitor-test2.html
JAVA code: eai/monitor-test/MonitorTest2.java
Directory: (dir)

The VRML scene we are dealing with is slightly more complex than the previous ones. In case you forgot all your basic animated VRML you can go back reading section 5.2.1 on [*]. It just links the pulse of a TimeSensor to an OrientationInterpolator which sets the rotation field of a Transform node containing a cube.

On the Java Side there is not much more than in the previous example. Note that the advise methods simply passes an Integer as a way to identify the Event we have to handle. There are also 2 buttons to stop and start the animation, some error handling code, and a long loop that makes sure that we really get the browser. [I have a feeling that one should put the EAI/Java inits into the start() method to be sure that the VRML scene has enough time to fully load ... to be tested].

Finally you can now look at the RGB Test Applet from the EAI Spec. It does about everything you have learned so far.

Example 6.3.13   EAI: RGB Test Applet  

Contents: getEventIn, getEventOut, EventOutObserver
HTML page: eai/rgb/RGB-demo.html
JAVA code: eai/rgb/RGBTest.java
Directory: (dir)

In the next sections we will discuss some slightly more complex examples and introduce some more EAI stuff.


next up previous contents index http://tecfa.unige.ch/ico/navi/tex2html/top.gifhttp://tecfa.unige.ch/ico/icons/vrml97-icon.gif
Next: 6.4 Extracting Information from Up: 6. The External Authoring Previous: 6.2 The very first
D.K.S. - 1998-03-18