VRML2.0/Moving Worlds: Adding Behaviors and Multiuser to VRML

Mitra <mitra@earth.path.net>
Paragraph International

Yasuaki Honda <honda@csl.sony.co.jp>
Kouichi Matsuda <matsuda@csl.sony.co.jp>
Sony Computer Science Lab

Gavin Bell <gavin@engr.sgi.com>
Chris Marrin <cmarrin@sgi.com>
Chee Yu <chee@sgi.com>
SGI

Summary of moving worlds

VRML 1.0 provided a means of creating and viewing static 3D worlds; VRML 2.0 will provide much more. The overarching goal of the Moving Worlds proposal for VRML 2.0 is to provide a richer, more exciting, more interactive user experience than is possible within the static boundaries of VRML 1.0. The secondary goals of the proposal are to provide a solid foundation that future VRML expansion can grow out of, and to keep things as simple and as fast as possible--for everyone from browser developers to world designers to end users--given the other goals.

The Moving Worlds proposal for VRML grew out of the VRML 1.0 Specification. The initial proposal as it appears today is an integration of the work done by WorldMaker, Sony and SGI. However, it would have been overwhelmingly selected for VRML 2.0 without it having considered and incorporated the input from numerous other organizations, companies, and the VRML community as represented by the www-vrml mailing list. At the time of writing (March 1996) it is in the process of being finalized, incorporating changes recommended during the review process. However several companies have already started implementing, and Beta's of those products should be appearing around the time of the INET'96 conference.

The full specification of Moving Worlds can be found at http://webspace.sgi.com/moving-worlds. For comments, extensions and related papers, see http://earth.path.net/mitra.

Moving Worlds provides these extensions and enhancements to VRML 1.0:

Key concepts

This section describes key concepts related to the definition and use of VRML, including how nodes are combined into scene graphs, how nodes receive and generate events, how to create node types using prototypes, how to add node types to VRML and export them for use by others, and how to incorporate programmatic scripts into a VRML file.

File syntax and structure

For easy identification of VRML files, every VRML 2.0 file must begin with the characters:

#VRML V2.0 utf8

The identifier utf8 allows for international characters to be displayed in VRML using the UTF-8 encoding. Any characters after these on the same line are ignored. The line is terminated by either the ASCII newline or carriage-return characters.

The # character begins a comment; all characters until the next newline or carriage return are ignored. The only exception to this is within double-quoted SFString and MFString fields, where the # character will be part of the string.

Note: Comments and whitespace may not be preserved; in particular, a VRML document server may strip comments and extra whitespace from a VRML file before transmitting it. WorldInfo nodes should be used for persistent information such as copyrights or author information. To extend the set of existing nodes in VRML 2.0, use prototypes or external prototypes rather than named information nodes.

Blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields. One or more whitespace characters separate the syntactical entities in VRML files, where necessary.

After the required header, a VRML file can contain the following:

Field, event, prototype, and node names must not begin with a digit (0x30-0x39) but may otherwise contain any characters except for nonprintable ASCII characters (0x0-0x20), double or single quotes (0x22, 0x27: "'), sharp (0x23: #), comma (0x2c: ,), period (0x2e: .), square brackets (0x5b, 0x5d: []), backslash (0x5c: \) or curly braces (0x7b, 0x7d: {}). Characters in names are as specified in ISO 10646 and are encoded using UTF-8.

VRML is case-sensitive. "Sphere" is different from "sphere", and "BEGIN" is different from "begin".

URLs and URNs

A URL (Universal Resource Locator) specifies a file located on a particular server and accessed through a specified protocol. A URN (Universal Resource Name) provides a more persistent way to refer to data than is provided by a URL. The exact definition of a URN is currently under debate. See the discussion at http://www.w3.org/hypertext/WWW/Addressing/Addressing.html for further details.

All fields in VRML 2.0 that have URLs are of type MFString. The strings in such a field indicate multiple places to look for files, in decreasing order of preference. If the browser can't locate the first file or doesn't know how to deal with the URL or URN given as the first file, it can try the second location, and so on.

VRML 2.0 browsers are not required to support URNs. If they do not support URNs, they should ignore any URNs that appear in MFString fields along with URLs. URN support is specified in a separate document at http://earth.path.net/mitra/papers/vrml-urn.html, which may undergo minor revisions to keep it in line with parallel work happening at the IETF.

Relative URLs are handled as described in IETF RFC 1808, "Relative Uniform Resource Locators."

MIME type

The MIME type for VRML files is currently "x-world/x-vrml." It is anticipated that the official type will change to "model/vrml." At this time, servers should present files as being of type x-world/x-vrml. Browsers should recognize both x-world/x-vrml and model/vrml.

IETF work-in-progress on this subject can be found in "The Model Primary Content Type for Multipurpose Internet Mail Extensions."

The file extension for VMRL files is .wrl (for world).

Nodes, fields, and events

At the highest level of abstraction, VRML is just a file format for describing objects. Theoretically, the objects can contain anything--3D geometry, MIDI data, JPEG images, and so on. VRML defines a set of objects useful for doing 3D graphics. These objects are called nodes. Nodes contain data, which are stored in fields.

VRML defines several different classes of nodes. Most of the nodes can be classified into one of two categories: grouping nodes or leaf nodes. Grouping nodes gather other nodes together, allowing collections of nodes (specified in a grouping-node field called "children") to be treated as a single object. Some grouping nodes also control which of their children are drawn.

Leaf nodes may not have children. Nodes that are considered leaf nodes include shapes, lights, viewpoints, sounds, scripts, sensors, interpolators, and nodes that provide information to the browser.

Shape nodes contain two kinds of additional information: geometry and appearance. For purposes of discussion, this specification uses a third node category, subsidiary nodes, for nodes that are always used within fields of other nodes and cannot be used alone. These nodes include geometry (for example, Cone and Cube), geometric property (for example, Coordinate3 and Normal), appearance (Appearance) and appearance property nodes (for example, Material and Texture2).

General node characteristics

A node has the following characteristics:

exposedField foo

is equivalent to the declaration:

The syntax for representing these pieces of information is as follows:

nodetype { fields }

Only the node type and braces are required; nodes may or may not have fields.

Sample file format

For example, this file contains a simple scene defining a view of a red cone and a blue sphere, lit by a directional light:

#VRML V2.0 utf8
Transform {
  children [

    DirectionalLight {
        direction 0 0 -1  # Light shining into scene
    },

    Transform {   # The red sphere
      translation 3 0 1
      children [
        Shape {
          geometry Sphere {radius 2.3}
          appearance Appearance [ 
             material Material {diffuseColor 1 0 0} ] # Red
        }
      ]
    },

    Transform {  # The blue cube
      translation -2.4 .2 1
      rotation     0 1 1  .9
      children [
        Shape {
          geometry Cube {}
          appearance Appearance [ 
             material Material {diffuseColor 0 0 1} ] # Blue
        }
      ]
    }

  ]
}

The structure of the scene graph

This section describes the general scene graph hierarchy, how to reuse nodes within a file, coordinate systems and transformations in VRML files, and the general model for viewing and interaction within a VRML world.

Grouping nodes and leaves

A scene graph consists of grouping nodes and leaf nodes. Grouping nodes, such as Transform, LOD, and Switch, can have child nodes. These children can be other grouping nodes or leaf nodes, such as shapes, browser information nodes, lights, cameras, and sounds. Appearance, appearance properties, geometry, and geometric properties are contained within Shape nodes.

Transformations are stored within Transform nodes. Each Transform node defines a coordinate space for its children. This coordinate space is relative to the parent (Transform) node's coordinate space--that is, transformations accumulate down the scene graph hierarchy.

Instancing

A node may be referenced in a VRML file multiple times. This is called "instancing" (using the same instance of a node multiple times; called "aliasing" or "multiple references" by other systems) and is accomplished by using the DEF and USE keywords.

The DEF keyword gives a node a name and creates an instance of the node. The USE keyword indicates that a previously named node should be used again. If several nodes were given the same name, then the last DEF encountered during parsing "wins." DEF/USE is limited to a single file; EXTERNPROTO/PROTO must be used to refer to a node type that is defined in another file. For example, if a node is defined inside a file referenced by a WWWInline node, the file containing the WWWInline node cannot USE that node.

Rendering the following scene results in three spheres being drawn. Both of the spheres are named "Joe"; the second (smaller) sphere is drawn twice, on either side of the first (larger) sphere:

#VRML V2.0 utf8
Transform {
  children [
    DEF Joe Sphere { },
    Transform {
      translation 2 0 0
      children [
        DEF Joe Sphere { radius .2 }
      ]
    },
    Transform {
      translation -2 0 0
      children [
        USE Joe  # radius .2 sphere will be used here; most recent one defined
      ]
    }
  ]
}

Tools that create VRML files may need to modify the user-defined node names to ensure that a multiply instanced node with the same name as some other node will be read correctly. The recommended way of doing this is to append an underscore followed by an integer to the user-defined name. Such tools should automatically remove these automatically generated suffixes when VRML files are read back into the tool (leaving only the user-defined names).

Similarly, if an unnamed node is multiply instanced, tools will have to automatically generate a name to correctly write the VRML file. The recommended form for such names is just an underscore followed by an integer.

Coordinate systems and transformations

VRML uses a Cartesian, right-handed, 3-dimensional coordinate system. By default, objects are projected onto a 2-dimensional display device by projecting them in the direction of the positive Z axis, with the positive X axis to the right and the positive Y axis up. A modeling transformation can be used to alter this default projection.

The standard unit for lengths and distances is meters. The standard unit for angles is radians.

VRML scenes may contain an arbitrary number of local (or object-space) coordinate systems, defined by the transformation fields of the Transform node. These fields are translation, rotation, scale, scaleOrientation, and center.

Given a vertex V and a series of transformations such as:

Transform {
  translation T
  rotation    R
  scale       S
  children [
    Shape {
      geometry[ PointSet { ... }]
    }
  ]
}

the vertex is transformed into vertex V' in world-space by first scaling, then rotating, and finally translating. In matrix-transformation notation, thinking of T, R, and S as the equivalent transformation matrices,

V' = T·R·S·V

(if you think of vertices as column vectors)

or

V' = V·S·R·T

(if you think of vertices as row vectors).

Conceptually, VRML also has a world coordinate system. The various local coordinate transformations map objects into the world coordinate system, which is where the scene is assembled. Transformations accumulate downward through the scene graph hierarchy, with each Transform inheriting the transformations of its parents. (Note, however, that this series of transformations takes effect from the leaf nodes up through the hierarchy. The local transformations closest to the Shape object take effect first, followed in turn by each successive transformation upward in the hierarchy.)

Viewing model

This specification assumes that there is a user viewing and interacting with the VRML world. It is expected that a future extension to this specification will provide mechanisms for creating multiparticipant worlds. The viewing and interaction model that should be used for the single-participant case is described here.

The world creator may place any number of viewpoints in the world--interesting places from which the user might wish to view the world. Each viewpoint is described by a Viewpoint node. Viewpoints exist in a particular coordinate system, and either the viewpoint or the coordinate system may be animated.

It is expected that browsers will support user-interface mechanisms by which users may "teleport" themselves from one viewpoint to another, and scripting-language mechanisms by which a viewer can be bound to a viewpoint that can then be animated. If a user teleports to a viewpoint that is moving (one of its parent coordinate systems is being animated), then the user should move along with that viewpoint.

The browser may provide a user interface that allows the user to change his or her viewing position or orientation, which will also change the currently bound viewpoint.

Time

The browser controls the passage of time in a world by causing TimeSensors to generate events as time passes. Specialized browsers or authoring applications may cause time to pass more quickly or slowly than in the real world, but typically the times generated by TimeSensors will roughly correspond to "real" time.

A world's creator must make no assumptions about how often a TimeSensor will generate events but can safely assume that each time event generated will be greater than any previous time event.

Typically, a TimeSensor affecting a visible (or otherwise perceptible) portion of the world will generate events once per "frame," where a "frame" is a single rendering of the world or one time-step in a simulation.

Events

Most nodes can receive events, which have names and types corresponding to their fields, with the effect that the corresponding field is changed to the value of the event received. For example, the Transform node can receive set_translation events (of type SFVec3f) that change the Transform's translation field (it may also receive set_rotation events, set_scale events, etc.).

Nodes can also generate events that have names and types corresponding to their fields when those fields are changed. For example, the Transform node generates a translation_changed event when its translation field changes.

Routes

The connection between the node generating the event and the node receiving the event is called a "route." A node that produces events of a given name (and a given type) can be routed to a node that receives events of the same type using the following syntax:

ROUTE NodeName.eventOutName TO NodeName.eventInName

Routes are not nodes; ROUTE is merely a syntactic construct for establishing event paths between nodes. ROUTE statements may appear at either the top-level of a .wrl file or prototype implementation, or may appear inside a node wherever fields may appear.

The types of the eventIn and the eventOut must match exactly; it is illegal to ROUTE from an SFFloat to an SFInt32 or from an SFFloat to an MFFloat.

Routes may be established only from eventOuts to eventIns. Given two exposedFields of a node "field1" and "field2," the following is illegal:

ROUTE node.field1 TO node.field2 # ILLEGAL

Instead, the corresponding eventIns/eventOuts must be used:

ROUTE node.field1_changed TO node.set_field2 # OR:
ROUTE node.field2_changed TO node.set_field1

Sensors

Sensor nodes generate events. Geometric sensor nodes (BoxProximitySensor, ClickSensor, CylinderSensor, DiskSensor, PlaneSensor, and SphereSensor) generate events based on user actions, such as a mouse click or navigating close to a particular object. TimeSensor nodes generate events at regular intervals, as time passes.

Prototypes

Prototyping is a mechanism that allows the set of node types to be extended from within a VRML file. It allows the encapsulation and parameterization of geometry, behaviors, or both.

A prototype definition consists of the following:

Square brackets enclose the list of events and fields, and braces enclose the definition itself:

PROTO prototypename [ eventIn      eventtypename name
                      eventOut     eventtypename name
                      exposedField fieldtypename name defaultValue
                      field        fieldtypename name defaultValue
                      ... ] {
  Scene graph
  (nodes, prototypes, and routes, containing IS statements)
}

A prototype is not a node; it merely defines a prototype (named prototypename) that can and/or ROUTE declarations, as necessary to implement the prototype.

PROTO and EXTERNPROTO statements may appear anywhere ROUTE statements may appear-- at either the top-level of a .wrl file or prototype implementation, or inside a node wherever fields may appear.

The eventIn and eventOut declarations export events from the scene graph rooted by node. Specifying the type of each event in the prototype is intended to prevent errors when the implementation of prototypes is changed and to provide consistency with external prototypes.

Events generated or received by nodes in the prototype's implementation are associated with the prototype using the keyword IS. For example, the following statement exposes a Transform node's built-in set_translation event by giving it a new name (set_position) in the prototype interface:

Transform {
  set_translation IS set_position
}

Fields hold the persistent state of VRML objects. Allowing a prototype to export fields allows the initial state of a prototyped object to be specified when an instance of the prototype is created. The fields of the prototype are associated with fields in the implementation using the IS keyword. For example:

Transform {
  translation IS position
}

IS statements may appear inside nodes wherever fields may appear. Specifying an IS statement for a node that is not part of a prototype's implementation is an error. It is an error for an IS statement to refer to something that is not part of the prototype's interface declaration. It is an error if the type of the field or event being exposed does not match the type declared in the prototype's interface declaration.

A prototype is instantiated as if typename were a built-in node. For example, a simple chair with variable colors for the leg and seat might be prototyped as:

PROTO TwoColorChair [ field MFColor legColor  .8 .4 .7
                      field MFColor seatColor .6 .6 .1 ] {
  Transform {
    children [

      Transform { # chair seat
        children [
          Shape {
            appearance Appearance {
              material Material { diffuseColor IS seatColor }
            }
            geometry Cube { ... }
          }
        ]
      },

      Transform { # chair leg
        translation ...
        children [
          Shape {
            appearance Appearance {
              material Material { diffuseColor IS legColor }
            }
            geometry Cylinder { ... }
          }
        ]
      }

    ] # End of root Transform's children
  } # End of root Transform
} # End of prototype

The prototype is now defined. Although it contains a number of nodes, only the legColor and seatColor fields are public. Instead of using the default legColor and seatColor, this instance of the chair has red legs and a green seat:

TwoColorChair {
  legColor 1 0 0
  seatColor 0 1 0
}

Prototype instances may be named using DEF and may be multiply instanced using USE.

A prototype instance can be used in the scene graph wherever its root node can be used. For example, a prototype defined as:

PROTO MyObject [ ... ] {
  Transform { ... }
}

can be instantiated wherever a Transform can be used, since the root node of this prototype's implementation is a Transform node.

A prototype's implementation defines a DEF/USE name scope separate from the rest of the scene; nodes DEF'ed inside the prototype implementation may not be USE'ed outside the prototype implementation, and nodes DEF'ed outside the prototype implementation may not be USE'ed inside the prototype implementation.

Prototype definitions appearing inside a prototype implementation are local to the enclosing prototype. For example, given the following:

PROTO one [ ] {
    PROTO two [ ] { ... }
    ...
    two { } #Instantiation inside "one":  OK
}
two { } # ERROR: "two" may only be instantiated inside "one".

the second instantiation of "two" is illegal. IS statements inside such a nested prototype's implementation may not refer to the enclosing prototype's interface.

Defining prototypes in external files

The syntax for defining prototypes in external files is as follows:

EXTERNPROTO prototypename [ eventIn eventtypename name
                            eventOut eventtypename name
                            field fieldtypename
                            ... ]
  "URL" or [ "URL", "URL", ... ]

The external prototype is then given the name prototypename in this file's scope. It is an error if the eventIn/eventOut declaration in the EXTERNPROTO is not a subset of the eventIn/eventOut declarations specified in the PROTO referred to by the URL. If multiple URLs are specified, the first one found should be used.

Unlike a prototype, an external prototype does not contain an inline implementation of the node type. Instead, the prototype implementation is fetched from a URL or URN. The other difference between a prototype and an external prototype is that external prototypes do not contain default values for fields. The external prototype points to a file that contains the prototype implementation, and this file contains the default values.

To allow the creation of libraries of small, reusable PROTO definitions, browsers should recognize EXTERNPROTO URLs that end with "#name" to mean the prototype definition of "name" in the given file. For example, a library of standard materials might be stored in a file called "materials.wrl" that looks like:

#VRML V2.0 utf8
PROTO Gold [] { Material { ... appropriate fields ... } }
PROTO Silver [] { Material { ... } }
   etc.

A material from this library could be used as follows:

#VRML V2.0 utf8
EXTERNPROTO Gold [] "http://.../materials.wrl#Gold"
...
    Shape { appearance Appearance { material Gold {}}
            geometry ...
    }

The advantage is that only one http fetch needs to be done if several things are used from the library; the disadvantage is that the entire library will be transmitted across the network even if only one thing is used from it.

Extensibility

The set of built-in VRML nodes can be extended using either prototypes or external prototypes. External prototypes provide a way to extend VRML in a manner that all browsers will understand. If a new node type is defined as an external prototype, other browsers can parse it and understand what it looks like, or they can ignore it. An external prototype uses the URL syntax to refer to an internal or built-in implementation of a node. For example, suppose your system has a Torus geometry node. This node can be exported to other systems using an external prototype:

EXTERNPROTO Torus [ field SFFloat bigRadius
                    field SFFloat smallRadius ]
  ["urn:yourdomain:Torus", "http://machine/directory/protofile" ]

The browser can recognize the URN and look for its own internal implementation of the Torus node. If it does not recognize the URN, it goes to the next URL and searches for the specified prototype file. In this case, if the file is not found, it ignores the Torus. If more URLs are listed, the browser tries each one until it succeeds in locating an implementation for the node or it reaches the end of the list.

Naming conventions for prototypes

Check the "File Syntax and Structure" section of this standard for the rules on valid characters in names.

To avoid namespace collisions with nodes defined by other people, any of the following conventions should be followed:

  1. Anyone can pick names that include a suffix of an underscore followed by a domain name that you own with the periods changed into underscores. For example, a company owning foo.com could create an extension node "Cube_foo_com".
  2. If you are building a product--for example, an authoring tool or a browser--or defining a lot of new nodes, then you can apply for a short prefix. E-mail type_registry@vrml.org to register for the prefix. This will normally be accepted if it is the most significant part of a .com, .org, or .net address. In the above example, foo.com could register the extension "_foo" and create nodes of the form "Cube_foo".
  3. Extensions supported by several companies should be registered and use the "_X" extension.

Scripting

Logic is often necessary to decide what effect an event should have on the scene--"if the vault is currently closed AND the correct combination is entered, THEN open the vault." These kinds of decisions are expressed as Script nodes that take in events, process them, and generate other events. A Script node can also keep track of information between invocations, "remembering" what its internal state is over time.

The event processing is done by a program contained in (or referenced by) the Script node's behavior field. This program can be written in any programming language that the browser supports.

A Script node is activated when it receives an event. At that point the browser executes the program in the Script node's behavior field (passing the program to an external interpreter if necessary). The program can perform a wide variety of actions: sending out events (and thereby changing the scene), performing calculations, communicating with servers elsewhere on the Internet, etc.

Two of the most common uses for scripts will probably be animation (using interpolators to smoothly move objects from one position to another) and network operations (connecting to servers to allow multiuser interaction).

Script languages

Scripts can be written in a variety of languages, including Java, C, and Perl. Moving Worlds does not require browsers to support any particular language. See appendices to this specification for bindings to Java and C.

Execution model

Every time a Script node receives one or more eventIn's, they are delivered to the associated script as a queue of events. In its simplest implementation, the script consists of one function or method for each eventIn in the Script node. For each eventIn in the queue, the appropriate method is executed, ordered by the time of receipt of the eventIn. These methods are passed the data from the eventIn and an SFTime of its time stamp, both of which are const parameters.

The author can also define an eventsProcessed() method, executed after all eventIn methods are called, to perform any post-processing necessary. For instance, the eventIn methods can simply collect data, allowing eventsProcessed() to process all the data at once, preventing duplication of work.

Actual event processing is handled by the processEvents() method or function. This is passed an array of event structures, each containing the name of the eventIn, its data, and the time stamp when it was received. A default implementation of this method is provided with the functionality described in the previous paragraphs. This may be overridden however, giving the author complete control over event processing.

After all events in the queue are handled, either on return from eventsProcessed() or processEvents(), values stored during script execution as eventOuts are sent, one for each eventOut that was set at least once during script execution. At most one message is sent for each eventOut value.

In languages that allow multiple threads, such as Java, you can use the standard language mechanisms to start new threads. When the browser disposes of the Script node (as, for instance, when the current world is unloaded), the script's shutdown() method will be called to allow the script to gracefully terminate any threads it may have created.

If you want to keep static data in a script (that is, to retain values from one invocation of the script to the next), you can use instance variables--local variables within the script, declared private. However, the value of such variables can't be relied on if the script is unloaded from the browser's memory; to guarantee that values will be retained, they must be kept in fields of the Script node.

Nodes and fields

The API provides a data type in the scripting language for every field type in VRML. For instance, the Java bindings contain a class called SFFloat, which defines methods for getting and setting the value of variables of type SFFloat. A script can get and set the value of its own fields using these data types and methods.

The API also provides a way to access other nodes in the scene. It allows getting the value of any exposed field of any node that the Script has access to.

Browser interface

The API provides ways for scripts to get and set global information associated with the VRML browser, such as the URL of the current world. Here are descriptions of the functions/methods that the browser API supports. The syntax given is the Java syntax.

  public static String getName();
  public static String getVersion();

The getName() and getVersion() methods get the "name" and "version" of the browser currently in use. These values are defined by the browser writer and identify the browser in some (unspecified) way. They are not guaranteed to be unique or to adhere to any particular format, and are for information only. If the information is unavailable, these methods return empty strings.

   public static float getCurrentSpeed();

The getCurrentSpeed() method returns the speed at which the viewpoint is currently moving, in meters per second. If speed of motion is not meaningful in the current navigation type, or if the speed cannot be determined for some other reason, 0.0 is returned.

  public static float getCurrentFrameRate();

The getCurrentFrameRate() method returns the current frame rate in frames per second. The way in which this is measured and whether or not it is supported at all is browser-dependent. If frame rate is not supported, or can't be determined, 100.0 is returned.

  public static String getWorldURL();
  public static void loadWorld(String [] url);

The getWorldURL() method returns the URL for the root of the currently loaded world. loadWorld() loads one of the URLs in the passed string and replaces the current scene root with the VRML file loaded. The browser first attempts to load the first URL in the list; if that fails, it tries the next one, and so on until a valid URL is found or the end of list is reached. If a URL cannot be loaded, some browser-specific mechanism is used to notify the user. Implementations may either block on a loadWorld() until the new URL finishes loading, or may return immediately and at some later time (when the load operation has finished) replace the current scene with the new one.

  public static Node createVrmlFromURL( String[] url );
  public static Node createVrmlFromString( String vrmlSyntax );

The createVrmlFromString() method takes a string consisting of a VRML scene description and returns the root node of the corresponding VRML scene. The createVRMFromURL() asks the browser to load a VRML scene description from the given URL or URLs, returning the root node of the corresponding VRML scene.

  public void addRoute(Node fromNode, String fromEventOut,
    Node toNode, String toEventIn);
  public void deleteRoute(Node fromNode, String fromEventOut,
    Node toNode, String toEventIn);

These methods respectively add and delete a route between the given event names for the given nodes.

  public void bindBackground(Node background);
  public void unbindBackground();
  public boolean isBackgroundBound(Node background);

bindBackground() allows a script to specify which Background node should be used to provide a backdrop for the scene. Once a Background node has been bound, isBackgroundBound() indicates whether a given Background node is the currently bound one, and unbindBackground() restores the Background node in use before the previous bind. If unbindBackground() is called when nothing is bound, nothing happens. Changing the fields of a currently bound Background node changes the currently displayed background.

  public void bindNavigationInfo(Node navigationInfo);
  public void unbindNavigationInfo();
  public boolean isNavigationInfoBound(Node navigationInfo);

bindNavigationInfo() allows a script to specify which NavigationInfo node should be used to provide hints to the browser about how to navigate through a scene. Once a NavigationInfo node has been bound, isNavigationInfoBound() indicates whether a given node is the currently bound one, and unbindNavigationInfo() restores the NavigationInfo node in use before the previous bind. If unbindNavigationInfo() is called when nothing is bound, nothing happens. A script can change the fields of a NavigationInfo node using events and routes. Changing the fields of a currently bound NavigationInfo node changes the associated parameters used by the browser.

  public void bindViewpoint(Node viewpoint);
  public void unbindViewpoint();
  public boolean isViewpointBound(Node viewpoint);

In some cases, a script may need to manipulate the user's current view of the scene. For instance, if the user enters a vehicle (such as a roller coaster or elevator), the vehicle's motion should also be applied to the viewer. bindViewpoint() provides a way to bind the viewer to a given Viewpoint node. This binding doesn't itself change the viewer location or orientation; instead, it changes the fields of the given Viewpoint node to correspond to the current viewer location and orientation. (It also places the viewer in the coordinate space of the given Viewpoint node.) Once a Viewpoint is bound, the script can animate the transformation fields of the Transform that the Viewpoint is in (probably using an interpolator to generate values) and move the viewer through the scene.

Note that scripts should animate the Viewpoint's frame of reference (the transformation of the enclosing Transform) rather than the Viewpoint itself, in order to allow the user to move the viewer a little during transit (for instance, to let the user walk around inside the elevator while it's between floors). Fighting with the user for control of the viewer is a bad idea.

Note also that results are undefined for vehicle travel if the user is allowed to move out of the vehicle while the animation is running. This problem is best resolved by using collision detection to prevent the user from leaving the vehicle while it's in motion. Another option is to turn off the browser's user interface during animation by setting the current navigation type to "none."

When the script has finished transporting the user, unbindViewpoint() releases the viewer from the influence of the currently bound Viewpoint, returning the viewer to the coordinate space of the previous viewpoint binding (or the base coordinate system of the scene if there's no previous binding). The fields of the now-unbound Viewpoint node return to the values they had before the binding.

And of course isViewpointBound() returns TRUE if the specified Viewpoint node is currently bound to the viewer (which implies that the fields of that Viewpoint node indicate the current position and orientation of the viewer). The method returns FALSE if the specified Viewpoint is not bound.

System and networking libraries

Scripts that need to use system and networking calls should use the scripting language's system and networking libraries. The VRML API doesn't provide such calls.

Scripting example

A Script node that decided whether or not to open a bank vault might receive vaultClosed and combinationEntered messages, produce openVault messages, and remember the correct combination and whether or not the vault is currently open. The VRML for this Script node might look like this:

DEF OpenVault Script {
  # Declarations of what's in this Script node:
  eventIn SFBool vaultClosed
  eventIn SFString combinationEntered
  eventOut SFBool openVault
  field SFString correctCombination "43-22-9"
  field SFBool currentlyOpen FALSE

  # Implementation of the logic:
  scriptType "java"
  behavior "java.class"
}

The "java.class" file will contain a compiled version of the following Java source code:

import vrml;

class VaultScript extends Script {

  // Declare fields
  private SFBool currentlyOpen = (SFBool) getField("currentlyOpen");
  private SFString correctCombination = (SFString) getField("correctCombination");

  // Declare eventOuts
  private SFBool openVault = (SFBool) getEventOut("openVault");

  // Handle eventIns
  public void vaultClosed(ConstSFBool value, SFTime ts) {
    currentlyOpen.setValue(FALSE);
  }

  public void combinationEntered(ConstSFString combo, SFTime ts) {
    if (currentlyOpen.getValue() == FALSE &&
        combo.getValue() == correctCombination) {
      currentlyOpen.setValue(TRUE);
      openVault.setValue(TRUE);
    }
  }
}