Chapter 41

Adding Behaviors with VRMLScript and Java

by John J. Kottler


CONTENTS

Like the world itself, the World Wide Web is not flat. However, most of the content on the Web today consists of simple, two-dimensional pages that contain two-dimensional graphics. Only recently, a change has begun to sweep over the Internet and the computer industry. More emphasis is being placed on the three-dimensional world. This is obvious by the immense popularity of three-dimensional games that have saturated the market.

The idea of using three-dimensional objects that can be manually manipulated is becoming more a commonality than a futuristic idea. Businesses and scientists can clearly benefit from the use of three-dimensional drawings to analyze mechanical equipment, chemical compounds, or DNA strands. Simulators are developed using three-dimensional technologies to emulate the real world, and the possibility of three-dimensional operating systems for computers is not far away.

But what about the Internet? Anyone who has browsed the Web can easily be inundated with information. In order to organize some of that information, three dimensions may be clearer than two. In the future, you may find more three-dimensional worlds being created to more easily handle large volumes of data. And as Web technologies continue to evolve, three-dimensional games will become a reality, as will chat rooms where you interact with other three-dimensional people.

The technology that has advanced the prospect of 3D on the Web is VRML, the Virtual Reality Modeling Language. As you have learned from other chapters in this book, this simple language instructs a three-dimensional browser how to paint three-dimensional worlds. The language is fairly straightforward, and numerous development tools available today assist in creating three-dimensional objects for VRML. Unfortunately many VRML sites today suffer from the same afflictions as Web sites in the recent past: the lack of interaction. Users can see a VRML world, walk through it, or rotate it, but they can't actually do anything in it.

As the World Turns

Users who are viewing worlds created with VRML 1.0 are very limited in what they can do. Fortunately, just as everything else evolves quickly on the Internet, so has the world of VRML. The newest iteration of VRML 2.0, also referred to as Moving Worlds, is a dynamic virtual reality language. Current information regarding Moving Worlds can be found at the Silicon Graphics Web site: http://vrml.sgi.com/moving-worlds.

In addition to new effects and three-dimensional capabilities, with VRML 2.0 it is possible to create worlds where objects spin or move in space. But more importantly, these objects can be smart as they move about. Moving Worlds allows three-dimensional objects to interact with computer scripting languages to define the unique characteristics or behaviors for those objects. These objects can also interact with each other or with the user to truly make an interactive experience.

In this chapter, you have the opportunity to learn how to make three-dimensional worlds that are dynamic and respond to the actions of the user. You learn about Moving Worlds' newest additions to VRML such as sensors, the script node, and wiring a world with VRMLScript or a Java applet.

Viewing the New World

Moving Worlds still requires the use of a three-dimensional browser. A 3D browser is capable of translating VRML, constructing a three-dimensional scene, and allowing the user to control how the scene is viewed. Most VRML browsers today can be launched by a Web browser such as Netscape's Navigator or Microsoft's Internet Explorer to view a scene related to a Web page. These VRML browsers may either create separate windows for displaying the scene or occupy the same space as the Web page.

In any case, it is important to remember that to see a three-dimensional world created using VRML 2.0, you must use a browser that is compliant with VRML 2.0. A few capable browsers are the following:

These browsers allow you to view VRML 2.0-animated worlds and interact with objects within those worlds. Each of these browsers include sample files that demonstrate the capabilities of VRML 2.0, particularly when used in conjunction with the browser. You will see differences in the way each handles VRML worlds, but they all can display a Moving Worlds three-dimensional world. Figure 41.1 demonstrates a sample of the Sony CyberPassage VRML browser. This browser makes particularly good use of Java applets with the VRML worlds. Several sample files are included with this browser including Drive, a race-track world with an animated car that winds through the curves of the track. In addition, the user can control whether the car moves or is stopped by clicking directly on the car.

Figure 41.1 : Sony's CyberPassage displays Moving Worlds, VRML 2.0 files that feature animation and interaction.

Sensors

In order to accomplish the results shown in Figure 41.1, numerous new interactive capabilities were added to VRML 2.0. For instance, new capabilities that handled the user clicking on the object or touching it were required before the user could click on the car to start it racing around the track. Most of these new interactive capabilities were added to the VRML 2.0 standard in the form of sensors.

You can think of these sensors as everyday sensors in the real world. For instance, a common touch sensor is found in the touch-sensitive computer screens in mall kiosks or on some automated teller machines. When you touch the screen, something happens. Likewise, motion sensors may be attached to the outside of a house or business building. When someone or something comes within a predefined distance of the sensor, exterior lights may be lit.

These same sensors are available in VRML 2.0. You find the touch and proximity sensors as well as many others listed in Table 41.1. These sensors all do different tasks but work on the same principle. They monitor activity within the VRML world and send notifications appropriately to programming scripts or other objects when particular events occur.

Table 41.1. Sensors can be set in a VRML 2.0 world to monitor activity.

Sensor NameSensor Action
CylinderSensor This sensor allows the developer to specify objects of 3D space that are to be rotated around their Y axis, independent of the rest of the scene. This sensor monitors when a user drags the mouse in this region with the button down.
PlaneSensor A developer can specify which objects may be moved along their X and Y axis' independent of the scene. This sensor outputs events when the user drags the mouse with the button down within this region.
ProximitySensor As a user moves closer to a region in 3D space using the VRML browser, specific events are triggered. The developer can use this sensor to make objects run away from the user when the user gets too close.
SphereSensor Similar to CylinderSensor and PlaneSensor, SphereSensor allows the developer to choose which objects in 3D space may be rotated around any axis, independently of the rest of the scene.
TimeSensor Some events occur regularly at a scheduled interval or once at a specific time. The TimeSensor monitors an internal clock and triggers events either regularly on a given interval or once when a specific time has occurred. To create objects that update every second, a developer sets the interval for a TimeSensor.
TouchSensor Truly interactive worlds allow the user to control them. One method of implementing this control is to note when a user clicks on or "touches" an object. A developer can use the TouchSensor to create a switch for turning animation on or off, for example.
VisibilitySensor This sensor determines whether a particular region of the three-dimensional world can be seen by the viewer. If a user can see a particular region monitored by the sensor, an event is triggered. Developers can use this sensor to determine when users can see parts of their world.

Most of the sensors listed in Table 41.1 are associated with particular objects in a VRML scene. For instance, the TouchSensor affects all objects within its parent group. Therefore, if a TouchSensor is created within a cone transform, as shown in Listing 41.1, you are able to click on the cone and not any other object in the scene. Of course, each object in the scene may have its own sensors with unique functionality.


Listing 41.1. Sensors can be applied to individual objects in VRML 2.0.

Transform {

  children [

    DEF TOUch_SENSOR TouchSensor {}

    Shape {

      appearance Appearance {

        material Material {

          diffuseColor    0 0 1

        }

      }



    geometry Cone{}

    }

  ]

}


These sensors open the possibility to create interactive worlds by noting when particular events occur. However sensors alone do not make the world interactive, they need to be connected to other objects or programming logic. In the next section, you learn how to add intelligent behaviors to objects using the Script node.

The Script Node

As mentioned numerous times already, objects themselves are static but can behave differently when coupled with programming scripts. These scripts can add simple or complex natures to objects in a scene. Anything from simply moving or rotating objects to creating interactive games is possible with scripts.

The Script node allows you to add these complex routines and behaviors to your VRML scene. Currently, the Script node supports numerous scripting languages such as VRMLScript, JavaScript, and more robust languages such as Java. Each of these languages, as we will see shortly, allows you to give more control over the flow of a world.

Tip
Each scripting language that can be used in Moving Worlds can perform the same basic functions. The difference between these languages becomes more apparent when constructing complex worlds. More complex scenes require faster and more robust languages like Java.

The following outlines the basic scripting node for VRML 2.0:


DEF toggle Script {

    field       fieldType   fieldName defaultValue

    eventIn     eventType   eventName

    eventOut    eventType   eventName

    url         "Script/Applet URL or embedded Script"

    scriptType  "ScriptLanguage"

}



field-There may be any number of fields within a Script node. These fields are similar to variables in other computer programming languages. Like variables in most languages, the type of data each field holds must be specified in VRML by way of an appropriate field type. Valid VRML field types can be found on the Web at http://vrml.sgi.com/moving-worlds/spec/part1/fieldsRef.html. A sample type is SFBool to indicate Boolean values.

eventIn-VRML works with objects in three-dimensional space. Therefore, it is not surprising that it is also somewhat object-oriented in nature. eventIn specifies the names of functions within the Script node that may be invoked by objects outside of the Script node. For instance, if there is an object named MY_CONE and a function named MoveCone within a Script node of a VRML scene, that MoveCone function needs to be made available to other objects like the cone using eventIn. eventIn is comparable to methods in object-oriented technology, and there can be as many eventIn's as needed within a Script node.

eventOut-Just as a script can expect information to be sent into its functions, a Script node may export data to other objects. The data to be made available to other objects is specified using the eventOut parameters. eventOut's are equivalent to variables that are defined within a Script node that are made available to other objects as well. eventOut is similar to properties in object-oriented technology, and there may be numerous eventOut's in a Script node.

url-After the functions for receiving input and the variables for sending output are defined in the Script node, the actual functions and programming logic must be specified. The url parameter of the Script node identifies the functions to be associated with that node. The information within the url parameter may indicate another location to retrieve scripting commands from or may be used to embed the language directly into the node. It may also be used with an optional scriptType parameter to indicate executable languages such as Java.

scriptType-When choosing other languages, such as Java, to handle the script's events, the scriptType parameter is essential for specifying what that language is. It is also useful when the url parameter points to a file that contains the script, not the actual script itself. In the case of using Java applets, the scriptType parameter must be set to javabc, for Java byte-codes, when the url contains the name of the Java class to use.

Tip
The position for Script nodes within a VRML file is irrelevant. Scripts are only invoked as instructed by ROUTE statements and therefore can exist at any location in a VRML file. It is recommended that you keep all of the scripts near each other to improve readability of the source VRML file. Typically, scripts are placed near the end, after the scene is described.

The Best Route

Script nodes, like most other nodes in VRML, can be placed at any location in a VRML source file. This is because the VRML browser typically reads the entire VRML source file before attempting to display the scene. However, if this is the case, how can a script be instructed to execute when particular events occur? The answer to controlling the flow of events in a VRML world is the ROUTE command.

ROUTE is a command in VRML that specifies the actions that objects in a world should follow. It specifies what actions in one object can occur to affect other objects. It is not a node, and it can be written on a single line of code. Its sole purpose is to map eventOut's from objects to eventIn's of other objects. This mapping creates a link between objects so that they can communicate with each other and update each other accordingly.

The ROUTE command is a fairly straightforward command to understand:

ROUTE eventOut_of_Object TO eventIn_of_Object

Note
A ROUTE command must exist for every mapping between two objects. If you wish to map three eventOut's of one object to three eventIn's of a second object, you must include three distinct ROUTE statements in your VRML source code for each connection.

For example, it's possible to route the output of a sensor to a function in a custom script. The TouchSensor, for instance, contains an isActive property (eventOut). This property can then be routed into a function (eventIn) in your own custom script. That way, whenever a user clicks on an object and triggers the TouchSensor, a custom function defined in a Script node can be executed. With this route, it is possible to monitor when a user clicks on objects in the VRML scene and act appropriately.

Each node in VRML contains its own set of eventIn's and eventOut's. Covering each node with its respective events would require much more than a single chapter. To find out more about specific events for each node, visit the VRML specification site: http://vrml.sgi.com/moving-worlds/spec/part1/nodesRef.html.

Tip
Like the Script node, ROUTE statements may be placed anywhere within the VRML file. However, it is recommended that you either group all routing commands at the end of the file or at least have related commands near their respective Script or object nodes.

Introduction to VRMLScript

Now that you have learned about the Script node and the ROUTE statement in VRML 2.0, you need to begin creating scripts that determine the behavior of objects in a three-dimensional world. VRMLScript is a language that is supported by VRML 2.0 and therefore browsers that are compliant with Moving Worlds. This language, which is a derivative of JavaScript, is exceedingly similar to Java or C programming languages. If you have experience with these languages already, then VRMLScript appears very easy.

VRMLScript is actually a subset of JavaScript. Therefore, it supports much of the same language semantics of JavaScript. The primary difference between VRMLScript and JavaScript is that VRMLScript disregards many of the document and browser class methods and properties necessary for Web pages. VRMLScript does contain a math library and common functions found in JavaScript.

This chapter presents a quick overview of VRMLScript. For more information on the JavaScript scripting language, which is again identical to VRMLScript in many ways, consult Chapters 34-38 of this book or Teach Yourself JavaScript in 21 Days or Netscape 3 Unleashed by Sams Publishing.

Functions in VRMLScript

As mentioned earlier, functions are essential in scripts. A function is simply a collection of VRMLScript commands that are executed. The important thing to note about functions is that the same group of VRMLScript commands can be executed repeatedly simply by calling that function's name.

The Script node maps particular eventIn parameters to functions within a Script node. In addition, values from other objects can be passed into a function. The function can use those values to determine what other events are happening in the three-dimensional scene you create.

Functions typically take the following format:


function functionName(eventIn_Value) {

    VRMLScript Code

}



Note
More than one function may be inserted within a Script node. However, each function must be declared by the eventIn parameters of a Script node in order for those functions to be publicly available to other objects in the scene.

Assigning Values in VRMLScript

The code between curly braces ({}) in a script's function may be any combination of VRMLScript commands or functions. Often calculations are performed and stored in variables within VRMLScript code. These variables can be assigned using a single = character. Likewise, property values for particular objects may be assigned using the = character. For example, assigning the on property of a DirectionalLight node named MY_LIGHT may be performed with the following statement:


MY_LIGHT.on = 1

Traditional mathematical functions are accepted within VRMLScript as well, such as multiplication (*), division (/), addition (+), and subtraction (-). Many other mathematical functions are defined within VRML/JavaScript's math object. For instance, using Math.PI in a line of VRMLScript code returns the value for pi.

Tip
There are numerous shortcuts in C, Java, JavaScript, and VRMLScript for updating variables. By using a variable name followed by two mathematical symbols, it is possible to update the value of that variable. For instance, to update the value of the variable var, the following statements can be used:
var++;     // Updates the variable by adding "1" to it, same as "var=var+1"
var-=5;   // Updates the variable by subtracting "5" from it, same as "var=var-5"

Conditional Logic

Another common action to perform in programming languages is conditional logic. Conditional logic is an elegant name for simply testing for certain values. Conditional logic typically consists of an if/then/else set. The VRMLScript application checks to see if some variable is equivalent to a particular value. If it is, then the VRMLScript can perform a particular task. If it is not equivalent, then the VRMLScript may have an option to do something else.

Conditional statements typically appear as follows:


if (variable == value) {

    do some VRMLScript if result is true

}

else {

    do some VRMLScript if result is false

}



Conditional statements may be as complex as necessary and can even be nested within each other. In some cases, you may need to test multiple criteria in order to establish a result. VRMLScript uses typical C/Java/JavaScript notation for testing equivalence (==), non-equivalence (!=), greater than (>), less than (<), greater than or equal to (>=), less than or equal to (<=), logically "ANDing" values together (&&), logically "ORing" values (||), or inverting TRUE/FALSE Boolean values (!).

For example, to test whether an object is a sphere and is either red or blue, the following condition can be used:


if (shape == "sphere" && (color == "Red" || color == "Blue")) {

    … the object is a red or blue sphere.

}


Note
Curly braces ({}) are used to surround a group of lines in VRMLScript source code. These braces indicate which lines of code are to be executed together as a group. For example, if an if statement is used, it is essential to indicate in the VRMLScript source code exactly which lines are to be executed when the result is true versus those lines that are executed when the result is false.

Looping

Some regions of a VRMLScript application may need to be executed more than once. To make a portion of code repeat numerous times, loops may be used in VRMLScript. There are typically two types of loops available in VRMLScript: the for loop and the while loop.

For Loop

The for loop indicates that a section of source code should execute for a particular number of iterations. This loop requires the following syntax:


for (loopIndex = startingValue; loopIndex_Condition; loopIndex_Update) {

    VRMLScript code

}



The loopIndex is a variable that keeps track of how many times the loop has executed. It may be set to a starting value. The loopIndex_Condition is similar to an if statement that instructs what a loop should look for in order to terminate. If the loopIndex_Condition is true, then the code within the loop is executed. When it is false, the loop terminates. Typically, it tests the loopIndex's value to determine whether it has reached the end of the loop's range. Commonly, the loopIndex is incremented by one step at a time. However, it is possible to specify that this index update its value differently using the loopIndex_Update parameter.

The following are two examples of for loops that execute VRMLScript code for a total of 10 iterations:


for (n=1; n<=10; n++) {

    some VRMLScript code

}



or



for (n=20; n>0;n-=2){

    some VRMLScript code

}



while Loop

In some cases, you may want to execute a section of code several times without necessarily knowing in advance the exact number of times that loop should occur. The while loop allows you to execute a section of VRMLScript as long as the condition within the while loop is true. while loops use the following syntax:


while (condition) {

    do some VRMLScript

}



In this case, the condition parameter of the while loop acts like an if statement. When the result of the conditional test is true, the VRMLScript code is executed. Once the result is false, the loop is terminated.

VRML Browser Commands

You will remember that VRMLScript is very similar to JavaScript with the exception of the typical properties and methods found in JavaScript for documents and Web page control. However, VRMLScript also adds several specific commands that can be used with VRML browsers. Table 41.2 lists these specific commands and their functionality within the VRML browser.

Table 41.2. VRMLScript can access numerous methods for the Browser object.

Browser MethodAction Performed
getName() Returns the name of the VRML browser being used to view a scene as a string.
GetVersion() Returns the version number of the VRML browser being used as a string.
getCurrentSpeed() Returns the current speed at which a user is traveling through the VRML world. The value returned is a floating-point number.
getCurrentFrameRate() Returns the speed at which scenes can be generated, in frames per second, as a floating-point number returns a VRML scene can be updated as quickly as a browser on a particular computer platform allows. Constraints may include the speed of the hardware, software, or graphics controller.
getWorldURL() Returns a string value containing the current URL used to access the VRML scene displayed in the browser window.
loadWorld(url) Instructs the browser to load another VRML world into the browser window. The url parameter specifies the file and location of the VRML file to load.
replaceWorld(nodes) At times it may become necessary to update or replace particular nodes in a world currently viewed in the browser without actually loading a brand new world. The nodes parameter allows you to specify a list of nodes to use when replacing the currently viewed scene.
createVRMLFromURL It is possible to create a 3D scene using another
(url, node, event) URL for the VRML source. However, retrieving other URLs from the Internet may be a time consuming process. Therefore this method allows you to specify an eventIn that is notified when the new node list is successfully read.
createVRMLFormString(string) Three-dimensional scenes may be created on the fly by passing strings that contain VRML commands to the browser. This method sends a string to the browser to be rendered and returns a list of root nodes when completed.
addRoute(fromNode, fromEventOut, toNode, toEventIn) Because objects can be created by way of browser methods at any time, there must be a provision to add routing between new nodes. The addRoute method instructs the VRML browser to connect the eventOut from one node to the eventIn of another node, provided that the events and nodes are named.
deleteRoute(fromNode, fromEventOut, toNode, toEventIn) Just as it is possible to add routes spontaneously to newly created nodes, it is possible to remove them. This method disconnects an eventOut from one node and the eventIn of a second node.

Seeing the Light

Now that you have had a quick overview of VRMLScript and a familiarity with it, we can examine how it is used in a VRML scene to create an interactive world. A simple example in VRMLScript is to create a world where there is a single white cone in the middle of the world.

Ordinarily this cone appears very dark, therefore a default directional light can be added to give the cone an additional glow of light. Figure 41.2 demonstrates this simple three-dimensional object with the light source applied. In this figure, the Silicon Graphics CosmoPlayer was used to display the VRML world.

Figure 41.2 : A directional light adds a glow to the right half of this 3D cone. The light can be turned on or off by clicking on the cone.

When the light is on and you click on the cone, the light is turned off, and the cone is scarcely visible against the black background. Likewise, if you click on the cone while the light is off, it is turned back on again to reveal the cone.

This simple task of turning a directional light on or off is accomplished using a simple VRML script embedded within a Script node and a TouchSensor. There are also additional ROUTE statements used to direct eventOut's of some nodes into eventIn's of others. To understand how this simple world is constructed, examine its source code in Listing 41.2.


Listing 41.2. The light is controlled using a TouchSensor and a simple VRMLScript with routing statements.

#VRML V2.0 utf8



DEF MY_LIGHT DirectionalLight {

    on TRUE

}



Transform {

  children [

    DEF TOUch_SENSOR TouchSensor {}

    Shape {

      appearance Appearance {

        material Material {

          diffuseColor    1 1 1

        }

      }



      geometry Cone{}

    }

  ]

}



DEF toggle Script {

    field    SFBool lightOn FALSE

    eventIn  SFBool isConeClicked

    eventOut SFBool toggle_changed

    url  "vrmlscript:

          function isConeClicked(button_state) {

            if (button_state == 0) {

              if (lightOn == 0)

                lightOn = 1;

              else

                lightOn = 0;

              toggle_changed = lightOn;

            }

          }

         "

}



ROUTE TOUch_SENSOR.isActive TO toggle.isConeClicked

ROUTE toggle.toggle_changed TO MY_LIGHT.on


As you look at the code in Listing 41.2, you will not find much out of the ordinary for creating a simple cone shape in a VRML world. The following sections review what code elements make this simple world interactive.

Touch-Sensitive Cone

The first thing you may notice as you scan down from the top of the source code in Listing 41.2 is the presence of a TouchSensor. This TouchSensor is named TOUch_SENSOR for reference later on in the source code.

You'll recall that a TouchSensor gives an object in VRML 2.0 the ability to have a user click on it in order to perform some action. The TouchSensor node can contain numerous events and fields, but the only thing we are concerned with is setting up the TouchSensor and triggering a VRMLScript when someone clicks on the cone.

TouchSensors are applied to all geometry that falls within the parent node containing the TouchSensor node. In our example, the TouchSensor is related to its parent node Transform, which was not given a proper name. Therefore, any geometry that appears within this Transform is treated as "hot-spots" for the TouchSensor. That is, if you can click on any objects within that transform and a TouchSensor is connected to that transform, an action occurs. In our example in Listing 41.2, the only geometric object within the transform is a cone, therefore you are only able to click on that cone to initiate an action.

The sensor simply monitors mouse input while the world is viewed. Whenever the mouse passes over objects that contain the TouchSensor and the mouse button is clicked, an eventOut is triggered from the TouchSensor. In this case, the isActive eventOut is sent, indicating that a user is actively clicking on the object.

So now that the sensor has been created and an appropriate event is being triggered, what's next? The action of clicking on the cone must be recognized by a script and handled appropriately. This requires the use of a routing statement that connects the isActive eventOut from the TouchSensor to an appropriate function within a Script node. We will review all of the routes that are defined in this scene shortly.

Reading the Script

The next obvious difference in the source code is the addition of a Script node. This node allows the VRML browser to control other objects and actions within the three-dimensional world using instructions presented in the node.

The first three lines of the Script node declare a variable to be used within the script (lightOn), the function isConeClicked (which is the node's eventIn ), and a property named toggle_changed, which is an eventOut. As you will recall, a Script node's eventIn declarations match the functions that are available to other objects as methods. Similarly the script node's eventOut declarations match variables used in the script that can be read by other objects afterward.

This simple script reads the output of the TOUch_SENSOR output event as input to its own isConeClicked function. The isActive event from the touch sensor is TRUE or 1 when the mouse button is held down and FALSE or 0 otherwise. This event is passed to the isConeClicked function as the variable button_state. When the button is pressed, the isActive event of the TouchSensor changes from a 0 to a 1, and that result is assigned to the variable button_state in the isConeClicked function.

Within that function, the script determines whether the light is currently on or off using a variable that toggles its state simultaneously with the directional light MY_LIGHT. After the script establishes that the light is either on or off, it changes the state of the light to be the opposite. This is accomplished by first setting the variable within the Script node that represents the light to the appropriate value (1 = on, 0 = off). Another routing statement later takes this variable's result and routes it to the actual light source to complete the action.

Note
When writing scripts that respond to TouchSensors, it is important to notice that the TouchSensor triggers the events and actions continuously. For example, when you hold the mouse button down for a long period of time over the object with the sensor, the sensor sends the isActive event to a respective object as long as the mouse button is held down. Therefore, you may consider checking the status of the mouse button (which is indicated by the isActive event from the TouchSensor). You may wish to only execute the script commands when the mouse button has been released, indicating a complete button click. Listing 41.2 does just this using the condition:
if (button_state == 0).

Tip
Although it does not matter to the VRML browser that the events and fields appear before the URL, it is a good practice to place them before the actual embedded script code. This makes the code more legible since common programming practices have developers declare variables before the script. Also when reading the script from top to bottom, you will read the variables first and remember them when they are referenced in the code.

Wiring the Light

Now that the script has been designed to check the status of the light and set the new state appropriately, it is time to see how the TouchSensor, script, and light are all connected together.

You learned earlier about the ROUTE statement in VRML 2.0. This command allows you to control the flow of control for a three-dimensional environment created with Moving Worlds. Routing commands basically inform the browser of the sequence of actions that are to be taken when particular events arise.

In the example shown in Listing 41.2, the TouchSensor triggers a script, which in turn triggers a light source in the scene. Therefore, two routing statements are required: one to connect the TOUch_SENSOR sensor to the toggle script, and a second to connect the toggle script to the light MY_LIGHT.

More specifically, the output event's value of the sensor is passed to the input event and ultimately a function of the script. The resulting property value of the script is then sent onto the input event for the light that determines whether the light is on or off. The two lines that accomplish this are:


ROUTE TOUch_SENSOR.isActive TO toggle.isConeClicked

ROUTE toggle.toggle_changed TO MY_LIGHT.on



Using Java for More Kick

You can create some fairly complex VRML worlds using the VRMLScript capabilities that have been introduced so far. However, for very complex worlds that require a more robust, object-oriented language, you may consider using something like Java. With Java, truly interactive applications that deal with user's input, network information, and a host of other capabilities are possible.

Fortunately, it is quite simple to tap the capabilities of Java and integrate them with your VRML 2.0 worlds. In the previous section, you learned many of the fundamentals of integrating programmable scripts with Moving Worlds objects. These same techniques will be applied to integrating Java applets with your three-dimensional worlds.

To illustrate connectivity between Java and VRML, consider another example. In this example, we will make a simple cone (we love cones) rotate towards the viewer and appear closer. It will then take several steps to rotate that cone away from the viewer at a further distance away. The animation will continue to swing back and forth as long as the scene is viewed in the VRML browser. In this case, Sony's CyberPassage makes viewing Java-controlled VRML files easy. Sony CyberPassage also includes Java classes that can be incorporated into your Java applets to create these complex VRML worlds. These classes follow standards documented at the Moving Worlds site at Silicon Graphics (http://vrml.sgi.com/moving-worlds).

This is a fairly simple animation applet that does not require complicated coding algorithms. However, it is ample for demonstrating the connectivity between VRML and Java. Figures 41.3 through 41.5 show three of the stages of the cone's rotation in order for you to gain a feel for how it will look animated.

Figure 41.3 : In the Java-controlled VRML animation, the cone begins by pointing directly at the user.

Figure 41.4 : In the middle of the same Java-controlled VRML animation, the cone points straight up.

Figure 41.5 : The Java-controlled VRML animation ends by pointing the cone away from the user.

Now that you can picture how this animation appears, consider the source code for this animation. Listing 41.3 demonstrates the VRML file that renders the cone. Listing 41.4 displays the Java source code that controls the animation of the VRML cone. The VRML file also connects a TimeSensor to a Script node that invokes the Java applet described in Listing 41.4.


Listing 41.3. The VRML file for the animated cone consists of the cone, a TimeSensor, and a simple script to connect the Java applet.

#VRML V2.0 utf8



DEF MY_LIGHT DirectionalLight {

    on TRUE

}



DEF MYCONE_TRANSFORM Transform {

  children [

    Shape {

      appearance Appearance {

        material Material {

          diffuseColor    0 0 1

        }

      }



    geometry Cone{}

    }

  ]

}



DEF TIME_SENSOR TimeSensor{

    loop TRUE

    cycleInterval .250

}



DEF SCRIPT Script{

    url        "MyCone.class"

    scriptType "javabc"



    eventOut   SFRotation MyConeRt

    eventOut   SFVec3f    MyConeTr



    eventIn    SFTime     moveMyCone

}





ROUTE TIME_SENSOR.cycleTime TO SCRIPT.moveMyCone



ROUTE SCRIPT.MyConeTr TO MYCONE_TRANSFORM.set_translation

ROUTE SCRIPT.MyConeRt TO MYCONE_TRANSFORM.set_rotation



Listing 41.4. This Java applet controls the animation of the cone displayed in Figures 41.3-41.5.

import vs.*;

import vrml.*;



public class MyCone extends Script{

    // aRad is defined to make degrees to radian conversion easier

    float aRad = (float) (Math.PI/180.0) ;



    // Get the eventOuts from the VRML file

    // These are the properties that get sent to the cone

    // to update its rotation and position

    SFRotation MyConeRt = (SFRotation)getEventOut("MyConeRt");

    SFVec3f MyConeTr = (SFVec3f)getEventOut("MyConeTr");



    // We're going to setup an array of coordinates, so we'll

    // need an index for the array

    int count;

    // Since the animation will bounce back and forth, we'll

    // need to increment or decrement "count" accordingly

    int countDirection = 1;



    float[] positionX;

    float[] positionY;

    float[] positionZ;

    float[] angle;

    float[] translation;

    float[] rotation;



    // Constructor my "MyCone" class.  Initialization happens here.

    public MyCone()

    {



        positionX = new float[9];

        positionY = new float[9];

        positionZ = new float[9];

        angle = new float[9];



        // Translations of objects requires a 3D vector

        // "translation" will hold the X, Y, and Z

        translation = new float[3];



        // Rotations require four parameters:

        // Which axis to rotate around: X, Y, or Z, and the

        // amount of radians to rotate

        rotation = new float[4];



        // Setup all of the animation points

        positionX[0] = 0f;

        positionY[0] = 0f;

        positionZ[0] = 4f;

        angle[0] = 60f;

        positionX[1] = 0f;

        positionY[1] = 0f;

        positionZ[1] = 3f;

        angle[1] = 45f;

        positionX[2] = 0f;

        positionY[2] = 0f;

        positionZ[2] = 2f;

        angle[2] = 30f;

        positionX[3] = 0f;

        positionY[3] = 0f;

        positionZ[3] = 1f;

        angle[3] = 15f;

        positionX[4] = 0f;

        positionY[4] = 0f;

        positionZ[4] = 0f;

        angle[4] = 0f;

        positionX[5] = 0f;

        positionY[5] = 0f;

        positionZ[5] = -1f;

        angle[5] = -15f;

        positionX[6] = 0f;

        positionY[6] = 0f;

        positionZ[6] = -2f;

        angle[6] = -30f;

        positionX[7] = 0f;

        positionY[7] = 0f;

        positionZ[7] = -3f;

        angle[7] = -40f;

        positionX[8] = 0f;

        positionY[8] = 0f;

        positionZ[8] = -4f;

        angle[8] = -60f;







        // Initialize the count index

        count = 0;

}



    // moveMyCone is invoked every 250ms as defined in

    // the host VRML file.  This method is invoked each

    // time the TimeSensor cycleTime event occurs.

    public void moveMyCone(ConstSFTime time, ConstSFTime ts)

    {

        // Move cone to new X, Y, Z position

        translation[0] = positionX[count];

        translation[1] = positionY[count];

        translation[2] = positionZ[count];

        MyConeTr.setValue(translation);



        // Rotate the cone around its X access

        rotation[0] = 1.0f;

        rotation[1] = 0.0f;

        rotation[2] = 0.0f;

        rotation[3] = angle[count] * aRad;

        MyConeRt.setValue(rotation);



        // Increment/Decrement array index through animation

        count = count + (1*countDirection);



        // When we hit one end of animation, change direction

        if(count >= 9){

            countDirection = -1;

        count = 7;

        }

        if(count < 0){

            countDirection = 1;

            count = 1;

        }

    }

}


It's About Time

To make sense of all of this code, first look at the VRML file shown in Listing 41.3. It should be fairly straightforward by now what this VRML file is doing. It renders a scene with a cone, lit by a directional light source.

It also defines a TimeSensor. A TimeSensor in Moving Worlds is similar to other sensors, including the TouchSensor we have reviewed already. It contains eventOut's that can be used to trigger actions to occur in other objects and eventIn's for specifying the sensor's own characteristics. The TimeSensor node allows a VRML browser to create an object that checks an internal clock and sends notifications when a particular time has transpired. This TimeSensor can be used as an alarm for events that should occur only after a specific amount of time has past. It may also be used as a metronome, sending events on a regular basis. In our example, the TimeSensor triggers actions in the script object on a regular interval (every 250ms).

Time to Read the Script

Every 250ms, the TimeSensor sends a cycleTime eventOut to a Script node as specified by a ROUTE command near the end of the VRML code listing. This Script node is similar to the one we examined earlier with our lighted cone example. The sole purpose of the Script node is to determine which methods within the program Script are to be used by other objects and what values the program script can post for other objects to use.

In our previous example in this chapter, the Script node also embedded the actual scripting commands within the node. In this case, the Java applet that controls the animation cannot be embedded since it is compiled into Java bytecodes. Therefore, the URL parameter of this node points to the actual Java class file. Since the URL parameter does not contain the actual script language, the VRML browser must know what type of program code it can expect at the location specified. The scriptType parameter in Listing 41.3 indicates that the program pointed to by the URL is a byte code-compiled Java applet (javabc implies Java Bytecode).

Route It Out

The final routing statements should come as little surprise. Once the Java applet has determined what the next rotational and translation points should be for the cone during its course of animation, it must update the cone accordingly. The last ROUTE commands map the respective properties of the Java applet that contain this information to the appropriate eventIn methods for the Transform node that contains the cone.

Imported Java

Once you have an understanding of the VRML file for this simple animation, you can begin to examine its accompanying Java applet's source code. As mentioned earlier, Sony CyberPassage provides VRML classes that can be imported into your Java applet. These classes provide numerous functions, but most importantly in this example, they provide the linkage between the Script node in the VRML file and the respective methods and properties in the Java applet.

Tip
To make compilation of Java/VRML applets easier, copy the VRML and VS subdirectories into the Java Developer's Kit (JDK) directory. The best place to move them to make your life easier is within the classes subdirectory within your JDK directory. Your JDK directory should then contain a classes folder that contains at least four directories: java, sun, vrml, and vs.

The first two lines found in Listing 41.4 import the necessary classes for creating Moving Worlds Java applets. One of these classes defines the Script class that the applet inherits from, as shown by the first line of code past the import statements. The Script class contains all of the Application Programming Interface (API) commands necessary to connect your Java applet to the Script node in your VRML file. These classes also contain the different field types found in VRML and map them as appropriate variable declarations in Java.

A few lines further into the Java application, you will find the following two lines:


SFRotation MyConeRt = (SFRotation)getEventOut("MyConeRt");

SFVec3f MyConeTr = (SFVec3f)getEventOut("MyConeTr");

These two lines fetch the eventOut information from the Script node in the VRML file and map the information to Java variables respectively. The getEventOut function finds a particular eventOut in a VRML Script node to which the Java applet is related. As you can see from these two lines, the rotational values and translation positions are declared using appropriate vectors for VRML.

Initializing the Applet

When the applet is constructed for the first time, its constructor method is invoked. The MyCone method is the constructor for this applet, which initializes default values and fills the rotational/translation arrays used for the animation with numbers. The number of four basic arrays that are used: positionX, positionY, positionZ, and angle. These arrays are as large as the number of steps in the animation. This particular animation uses nine steps, where each step stores a different rotational and translation value in each of the arrays.

Therefore the first step of the animation, denoted by an index of 0 for the arrays, contains the following settings for the cone's location in 3D space as well as how far it should be rotated:


positionX[0] = 0f;

positionY[0] = 0f;

positionZ[0] = 4f;

angle[0] = 60f;

The Main Task

The Script node found in Listing 41.3 specifically states that the primary method (eventIn) for the Java applet is the moveMyCone method. Therefore, whenever the script is invoked in the Script node, this method is invoked in the Java applet. In this example, this happens when the actual animation is performed.

The method simply finds the position and rotation information for the current step in the animation and assigns the appropriate position vector and rotational properties for export out of the Java applet. The ROUTE statements in the VRML file found in Listing 41.3 take the data exported from the Java applet and update the correct object nodes appropriately.

Note
Notice that the exported properties (myConeTr and myConeRt) for the Java applet in Listing 41.4 are set using the setValue method. This is essential since the rotational and translation values are treated as arrays of floating-point numbers in the Java applet, but must be converted to appropriate vectors for VRML.

The moveMyCone method also updates the pointer to the current frame in the animation and verifies that it has not gone past the beginning or the end of the animation. If it has crossed the bounds at either end, the direction of the animation is reversed by changing the steps in the sequence to increment or decrement. The end result is an animation that appears to swing back and forth.

Note
Because a number of steps are played for a consecutive number of times to create the animation, it is tempting to think that a loop would be appropriate to use in the moveMyCone method. Although loops can be used, this particular animation is supposed to constantly run in the background. The TimeSensor found in the VRML source for this example indicates that an action should be taken every 250 milliseconds. Therefore, every 250 ms the cone's position and rotation can be updated. The TimeSensor keeps the VRML world in sync, particularly if other VRML objects are animated with the cone.
In addition, some machines may execute Java code faster than others. If the animation were accomplished completely with a loop, the speed of the animation would vary depending on the machine. Not to mention that a continuous loop may occupy all of the applet's running time and not allow other actions to occur.

Summary

This chapter introduced you to the fundamentals of VRMLScript and the techniques required to attach VRML objects to programmable scripts. You learned the basics of controlling a VRML world with a Java applet and can hopefully expand upon that knowledge to create some truly unique, entertaining, and interactive worlds of your own.