CS184 Lecture 11 summary

Scripting

Scripting is a powerful way to control the location and appearance of objects. VRML's scripting mechanism is different that most graphics languages, but is arguably a better and more modular design. Here is the interpolation script from last time, written in Javascript:

DEF SnakeWiggle Script {
    url "javascript:
              function set_fraction (frac, eventTime) {
                   for ( i = 0; i < 19; i++) {
                        value_changed[i][0] = 0.47 * i - 4.47;
                        value_changed[i][1] = 0.0;
                        value_changed[i][2] = Math.sin((i/4.5 - frac*2) * 3.14);
                        }
                  value_changed[0][0] = -4.1;  // Fix the snake's head
                  value_changed[0][2] = value_changed[1][2];
             }"
    eventIn SFFloat set_fraction
    eventOut MFVec3f value_changed
}

Notice a few things about the script:

Javascript is a particularly easy language to prototype scripts in. It is ideal for small scripts that are included in VRML files.

Java scripting

The java language is a full-scale object-oriented programming language that is available for VRML scripting. The linkage between VRML and Java is not quite as simple as for Javascript, but is still easy to manage.

Here is another motion interpretation script, using Java. This time, the Java code must be in a different file from the VRML file containing the Script node definition:

DEF SnakeWiggle Script {
	 url "interp.class"
	 eventIn SFFloat set_fraction
	 eventOut MFVec3f value_changed
      }

Now here is the Java code for the file interp.java. First we import the VRML classes and a Math class for using a sine function:

import vrml.*;
import vrml.node.*;
import vrml.field.*;
import java.lang.Math;

Then we define the interpolator class itself with local variables corresponding to events:

public class interp extends Script {
   private ConstSFFloat set_fraction;  // set_fraction as a ConstSFFloat,
   private float frac;                 // same thing as a Java float.
   private MFVec3f value_changed;      // value_changed eventOut as MFVec3f,
   private float vals[][];             // same thing as a 2D Java float array.

Then there is a method to initialize some variables, called the first time the script is invoked:

   public void initialize() {
      value_changed = (MFVec3f) getEventOut("value_changed");
      vals = new float[37][3];
   }

This is the main script called to process an event. Note the conversion of the event type:

   public void processEvent(Event e){
      set_fraction = (ConstSFFloat) e.getValue(); // Get set_fraction eventIn 
      frac = set_fraction.getValue(); // Convert it from SFFloat to java float
      for (int i = 0; i < 37; i++) {
	 vals[i][0] = 0.23f * i - 4.23f;
	 vals[i][1] = 0.0f;
	 vals[i][2] = (float)Math.sin((i/9.0f - frac*2.0f) * 3.14f);
      }
      vals[0][0] = -4.1f;
      vals[4][2] = (vals[5][2] + vals[4][2])/2.0f;
      vals[3][2] = vals[4][2];
      vals[2][2] = vals[3][2];
      vals[1][2] = vals[2][2];
      vals[0][2] = vals[1][2];
      value_changed.setValue(vals);  // Save the vals array in the eventOut
   }
}

Handling multiple eventIns is a little more complicated. You must explicitly recognize the name of the eventIn, and invoke the appropriate method.

Here is a VRML environment that simulates a stopwatch with start/stop and reset buttons. This version uses Javascript to implement the control script.

Here is the same environment using a script written in Java. For direct comparison, here is the Javascript version of the script, and here is the Java code.

The Java program uses a recommended style for programming a VRML script. Local variables are used for eventOuts and fields, and private methods are created for each eventIn. Note that you dont have to use such private methods. All VRML actually does is call the processEvents method no matter what the event is. But you should write processEvent to decode the event type and then call a private method, along the lines of the the Javascript version.

You can also access fields and events of other nodes than the script node. To do this, you pass the other node as an argument to the script. See the VRML node reference for examples of how to do this.