The Annotated VRML 97 Reference

1 Intro     Concepts     3 Nodes     4 Fields/Events    Conformance
A Grammar     B Java     C JavaScript     D Examples     E Related Info    References
Quick Java         Quick JavaScript         Quick Nodes   
 

  About the Book
  
Help
  Copyright © 1997-99
  Purchase the book from Amazon.com

Chapter 3:
Node Reference


Intro
Anchor
Appearance
AudioClip
Background
Billboard
Box
Collision
Color
ColorInterpolator
Cone
Coordinate
CoordinateInterpolator
Cylinder
CylinderSensor
DirectionalLight
ElevationGrid
Extrusion
Fog
FontStyle
Group
ImageTexture
IndexedFaceSet
IndexedLineSet
Inline
LOD
Material
MovieTexture
NavigationInfo
Normal
NormalInterpolator
OrientationInterpolator
PixelTexture
PlaneSensor
PointLight
PointSet
PositionInterpolator
ProximitySensor
ScalarInterpolator
Script
Shape
Sound
Sphere
SphereSensor
SpotLight
Switch
Text
TextureCoordinate
TextureTransform
TimeSensor
TouchSensor
Transform
Viewpoint
VisibilitySensor
WorldInfo

+3.40 Script

Script { 
  exposedField MFString url           [] 
  field        SFBool   directOutput  FALSE
  field        SFBool   mustEvaluate  FALSE
  # And any number of:
  eventIn      eventType eventName
  field        fieldType fieldName initialValue
  eventOut     eventType eventName
}

The Script node is used to program behaviour in a scene. Script nodes typically

  1. signify a change or user action;
  2. receive events from other nodes;
  3. contain a program module that performs some computation;
  4. effect change somewhere else in the scene by sending events.

Each Script node has associated programming language code, referenced by the url field, that is executed to carry out the Script node's function. That code is referred to as the "script" in the rest of this description. Details on the url field are described in "2.5 VRML and the World Wide Web."

Browsers are not required to support any specific language. Detailed information on scripting languages may be found in "2.12 Scripting." Browsers supporting a scripting language for which a language binding is specified shall adhere to that language binding.

Sometime before a script receives the first event it shall be initialized (any language-dependent or user-defined initialize() is performed). The script is able to receive and process events that are sent to it. Each event that can be received shall be declared in the Script node using the same syntax as is used in a prototype definition:

    eventIn type name

The type can be any of the standard VRML fields (as defined in Chapter 4, "Field and Event Reference"). Name shall be an identifier that is unique for this Script node.

The Script node is able to generate events in response to the incoming events. Each event that may be generated shall be declared in the Script node using the following syntax:

    eventOut type name

With the exception of the url field, exposedFields are not allowed in Script nodes.

TECHNICAL NOTE: Defining exactly what it means for a Script to have an exposedField gets complicated. It isn't enough to say that an exposedField is equivalent to an eventIn, field, and event Out. For example, if the following Script were legal
     DEF ILLEGAL Script {
       exposedField SFBool foo FALSE
     }
and considered equivalent to
     Script { 
       field SFBool foo FALSE
       eventIn SFBool set_foo
       eventOut SFBool foo_changed
     }

a variety of difficult questions would need to be addressed. Is the Script's code required to generate foo_changed events when a set_foo event is received, or is that done automatically for the Script by the browser? If it is done automatically by the browser (which would certainly be convenient for the person writing the Script), is the Script's code also allowed to send foo_changed events or change the foo field? And if it is done automatically by the browser, then will it be done automatically in the second previous example (where foo, set_foo, and foo_changed are declared individually instead of as an exposedField)?

If foo_changed events are not automatically generated when set_foo events are received, is the Script required to generate them? If not, then foo isn't really an exposedField, since the definition of an exposedField involves both syntax (it is syntactically equivalent to a field + eventIn + eventOut) and semantics (an exposedField's semantics are that it generates _changed events and sets the field whenever a set_ event is received).

ExposedFields in Script nodes are a design issue that will probably be revisited at some time in the future. Allowing a Script read-only access to its exposedFields and allowing only the browser to generate _changed events would be a good solution, but requires that the notion of a read-only variable be supported somehow in each scripting language. For VRML 2.0, the simple and conservative solution of just not allowing Script nodes to have exposedFields was chosen.


If the Script node's mustEvaluate field is FALSE, the browser may delay sending input events to the script until its outputs are needed by the browser. If the mustEvaluate field is TRUE, the browser shall send input events to the script as soon as possible, regardless of whether the outputs are needed. The mustEvaluate field shall be set to TRUE only if the Script node has effects that are not known to the browser (such as sending information across the network). Otherwise, poor performance may result.

TECHNICAL NOTE: Executing a Script might be a fairly expensive operation, possibly involving communication with a language interpreter that may be running as a separate process. Therefore, VRML 2.0 was designed so that browsers can queue up multiple events and give them to a Script node at the same time. The mustEvaluate flag is a hint to the browser that it should execute the Script as soon as possible after it receives events, which is less efficient than waiting as long as possible to execute the Script.

Once the script has access to a VRML node (via an SFNode or MFNode value either in one of the Script node's fields or passed in as an eventIn), the script is able to read the contents of that node's exposed fields. If the Script node's directOutput field is TRUE, the script may also send events directly to any node to which it has access, and may dynamically establish or break routes. If directOutput is FALSE (the default), the script may only affect the rest of the world via events sent through its eventOuts. If directOutput is FALSE and the script sends events directly to a node to which it has access, the results are undefined.

A script is able to communicate directly with the VRML browser to get information such as the current time and the current world URL. This is strictly defined by the API for the specific scripting language being used.

The location of the Script node in the scene graph has no affect on its operation. For example, if a parent of a Script node is a Switch node with whichChoice set to "-1" (i.e., ignore its children), the Script continues to operate as specified (i.e., it receives and sends events).

TECHNICAL NOTE: A couple of generalizations for the Script node were considered but did not make it into the final VRML 2.0 specification. One was the ability for a Script to add or remove fields, eventIns, and eventOuts from itself dynamically while it was running. Combined with the browser addRoute()and deleteRoute() methods, this would sometimes be useful. However, it might be difficult to implement and will be easy to add later if necessary.

Another generalization along the same line is allowing a Script to declare that it can receive events of any type, with the type determined by the Script as it runs. This would require additional syntax (perhaps an "SFAny" field pseudotype) and would affect the design of several other features (such as PROTO and EXTERNPROTO). Again, this might make implementation of the VRML specification significantly more difficult and can be added later if it becomes clear that it is necessary.


TIP: At present, there are two scripting languages supported in the VRML specification: Java and JavaScript. There has been an endless and raging debate in the VRML community on which language is "better." The pro-Java camp believes that Java is a "real" programming language and has much more power, flexibility, infrastructure, and industry acceptance. These points are all true. The JavaScript proponents state that JavaScript is much easier to learn and use, especially if you are not a hard-core programmer. This is also a reasonable position (debated strongly by Java programmers, though). In general, when choosing a programming language, you should first assess the problem you are trying to solve; second, consider your own programming skills and experience; and then, choose the language that best fits these two parameters. A gross generalization is that Java is more capable of solving the difficult or serious programming problems, such as network access, database integration, multiusers, and so forth, while JavaScript is more suitable for simple behavior scripting, such as "a combination lock," "turn on the lights when . . . ," and so on.

Another common generalization is that Java is a better choice for full-time programmers (due to strong object-oriented architecture and deep system libraries), while JavaScript is a good choice for the part-time or amateur programmer (due to forgiving syntax and lack of types). Also, it is important to note that the VRML specification does not require either scripting language to be supported. Therefore, it is important to verify that the scripting languages you choose to use in your content are supported by the browsers you intend to use.


EXAMPLE (click to run): The following example illustrates use of the Script node (see Figure 3-46). This world defines a toggle button prototype, Button, and a simple combination lock that composes three Button nodes together with a Script node that verifies the combination. Note that the first Script node is defined within the prototype Button. This example is illustrated in both Java and JavaScript:

#VRML V2.0 utf8
PROTO Button [
    exposedField SFNode geom NULL 
    eventOut SFInt32 state_changed ]
{
  Group { children [
    DEF TOS TouchSensor {}
    DEF Toggle Script {
      eventIn SFTime touch
      eventOut SFInt32 which_changed IS state_changed
      url [ "javascript:
        function initialize() {
          // Initialize to 0th child at load time
          which_changed = 0;
        }
        function touch(value, time) {
          // Toggle the button value
          which_changed = !which_changed;
        }"
        # Or Java:
        "ToggleScript.class" ]
    }
    DEF SW Switch {
      whichChoice 0
      choice [
        Shape {     # child 0 - "off"
          geometry IS geom
          appearance DEF A2 Appearance {
            material Material { diffuseColor .3 0 0 }
          }
        }
        Shape {     # choice 1 - "on"
          geometry IS geom
          appearance DEF A1 Appearance {
            material Material { diffuseColor 1 0 0 }
          }
        }
      ]
    }
  ]}
  ROUTE TOS.touchTime TO Toggle.touch
  ROUTE Toggle.which_changed TO SW.set_whichChoice
} # end of Toggle prototype

# Now, create 3 Buttons and wire together with a Script
Transform {
  translation -3 0 0
  children DEF B1 Button { geom Box {} }
}
DEF B2 Button { geom Sphere {} }
Transform {
  translation 3 0 0
  children DEF B3 Button { geom Cone {} }
}
DEF ThreeButtons Script {
  field SFInt32 b1 0
  field SFInt32 b2 0
  field SFInt32 b3 0
  eventIn SFInt32 set_b1
  eventIn SFInt32 set_b2
  eventIn SFInt32 set_b3
  eventOut SFTime startTime
  url [ "javascript:
    function set_b1(value, time) {
      b1 = value;
      if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
    }
    function set_b2(value, time) {
      b2 = value;
      if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
    }
    function set_b3(value, time) {
      b3 = value;
      if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
    }"
    # Or Java:
    "ScriptLogic.class" ]
}
DEF T Transform { children [                 # Explosion effect
  Shape { geometry Sphere {  radius 0.1 } }  # Hidden inside
  DEF SI PositionInterpolator {
    key [ 0.0 1.0 ]
    keyValue [ 0.01 0.01 0.01, 300.0 300.0 300.0 ]
  }
  DEF TS TimeSensor { }
  NavigationInfo { type "EXAMINE" }
] }
ROUTE B1.state_changed TO ThreeButtons.set_b1
ROUTE B2.state_changed TO ThreeButtons.set_b2
ROUTE B3.state_changed TO ThreeButtons.set_b3
ROUTE ThreeButtons.startTime TO TS.startTime
ROUTE TS.fraction_changed TO SI.set_fraction
ROUTE SI.value_changed TO T.set_scale

ToggleScript.java:

/*
 * ToggleScript.java
 * Toggles an integer between 0 to 1 every time a time event
 *   is received
 */
import vrml.*;
import vrml.field.*;
import vrml.node.*;
public class ToggleScript extends Script {
  SFInt32 which_changed;
  public void initialize() {
    which_changed  = (SFInt32) getEventOut("which_changed");
    which_changed.setValue(0);
  }
  public void processEvent( Event e ) {
    String name = e.getName();
    if ( name.equals( "touch" )) {
      which_changed.setValue(1 - which_changed.getValue());
    }
  }
}

ScriptLogic.java:

/*
 * ScriptLogic.java
 * Receives set_b1/2/3 events, when correct combination
 *   is received outputs a startTime event.
 */
import vrml.*;
import vrml.field.*;
import vrml.node.*;
public class ScriptLogic extends Script {
  int b1;
  int b2;
  int b3;
  SFTime startTime;
  public void initialize() {
    startTime  = (SFTime) getEventOut("startTime");
  }
  public void processEvent( Event e ) {
    String name = e.getName();
    if ( name.equals( "set_b1" )) {
      b1 = ((ConstSFInt32)e.getValue()).getValue();
    } else if ( name.equals( "set_b2" )) {
      b2 = ((ConstSFInt32)e.getValue()).getValue();
    } else if ( name.equals( "set_b3" )) {
      b3 = ((ConstSFInt32)e.getValue()).getValue();
    }
    if ((b1 == 1) && (b2 == 0) && (b3 == 1))
      startTime.setValue(e.getTimeStamp());
  }
}

 

 

Script node example

Figure 3-46: Script Node Example