http://tecfa.unige.ch/ico/navi/tex2html/top.gifhttp://tecfa.unige.ch/ico/icons/vrml97-icon.gif next up previous contents index
Next: 6. The External Authoring Up: 5. Introduction to moving, Previous: 5.2 Introduction to Events,

Subsections


   
5.3 Introduction to Scripting with Javascript

Many things you want to do are too complex for the built-in sensors and interpolators. In this case you can write nodes that are program scripts, that will:

Here is the formal definition of a =>Script node:

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

As you will learn in the examples in the next sections, the ``url'' fields contains either a pointer to a script file or a full script.

The VRML standard leaves it open to the browser manufactures what language they will support for scripting. However, if they implement scripting with either JAVA or JavaScript, conformance to the spectifcations is required!

In this tutorial we don't teach you how to program with JavaScript or even to program in the abstract. See our JavaScript Page for the most important pointers that will help you to get started. There are 2-3 good on-line tutorials and several good books. Scripting with JavaScript will be based on the ECMAScript scripting language with extensions that are required for the VRML plug-in. I.e. it does not contain typical Netscape objects. This means for example that you can't open popup windows and such!

WARNING: If you use the Cosmo 1.02 on Win95 for any reason (you should not) or Cosmo 1.02 on Irix (you have to), you have either to replace:

     url "javascript: ...
           by
     url "vrmlscript: ...

The better solution is just to duplicate the code within the url field as in the following example. Your browser will pick whatever he likes best.

    url [
      "javascript: ...... 

           ",
      "vrmlscript: ......

           "]

From the little I have looked at the documentation, vrmlscript looks more or less like javascript, i.e. it implements a subset that contains most functionalities you need to get started. My examples all work the same (except for the name of the language). Compare yourself:

    
5.3.1 Touchsensors, ROUTE and Javascript

Example 5.3.1   The Light-on problem again  

VRML: ../examples/anim/anim-touch-2.wrl
Source: ../examples/anim/anim-touch-2.text
Contents: JavaScript, TouchSensor

The ``simple touchsensor effect'' example discussed in section 5.2 directly wired the state of a TouchSensor to the state of a PointLight. This meant that the light went on when you were holding down the mouse button, but it went off as soon as you released the button. Now let's see how to make a switch that that will keep the object lit with JavaScript. (Note that I don't know yet if I could do without, but that's another problem).

The trick is to build a JavaScript node in between that will receive the input from the TouchSensor and then simply switch the PointLight on. When the user releases the mouse button, the Script allready propagated the information and the light will stay lit.

The definition of the TouchSensor exactly looks the same:

# The touchsensor for putting the Light ON
#
Transform {
   translation -0.5 1 5
   children [
      DEF LightONSwitch TouchSensor {
      }
      Inline { url ["light-switch.wrl"] }
     ]
}

Now if you look at the routing statements below you can see that the event LightONSwitch.isActive (the user points on the LightONSwitch node) is routed to eventIn of a node called ``lightONScript''. This node is an ``engine'' written in JavaScript that will compute this ``In'' event and produce an ``Out'' event that in turn is wired to the LIGHT node (i.e. the PointLight).

# link activation of lightON to eventIn of lightONScript
#
ROUTE LightONSwitch.isActive TO lightONScript.lightONIsActive
#
# link "output" of lightONScript to LIGHT
#
ROUTE lightONScript.lightIsON TO LIGHT.set_on

Let's look at the JavaScript node called lightONScript now: We define a =>Script node with two mandatory slots: ``eventIn'' and ``eventOut''. For each of those slots we must define its type (in our case we use SFBool since both the TouchSensor's and the light's fields we are interested in are either on or off, i.e. TRUE or FALSE). The second argument are the event names that must be unique identifiers within the scope of the node.

DEF lightONScript Script {
   eventIn SFBool lightONIsActive
   eventOut SFBool lightIsON
   
   url "javascript:
                function lightONIsActive(active) {
                        lightIsON = TRUE;
                }"
}
In order to compute the eventOUT from the eventIn we must assign a function to the ``url'' field. That function has the same name as eventIn and assigns a value to the variable of eventOut [hmmmm this does not sound very clean]. Now where do we put the function ? Either we insert the function as string in the url field or we use an URL that points to a script. (Note, that you can write several functions if needed).

As you can see, the script is fairly simple. It takes one argument (the value of eventIn, which we don't use here) and sets the value of the eventOUT.

In order to change a value of a node, you don't actually need to ROUTE the eventOUT. Instead you can directly set a value within the Scripting node. Here is how you do it:

# Clicking on the green Light Switch (ON)
#
DEF lightONScript Script {
   eventIn SFBool lightONIsActive
   field SFNode node USE LIGHT
   directOutput TRUE
   url "javascript:
                function lightONIsActive(active) {
                        node.set_on = TRUE;
                }"
}

# link activation of lightON to evenIn of lightONScript
ROUTE LightONSwitch.isActive TO lightONScript.lightONIsActive
In the line
   field SFNode node USE LIGHT
we bind the LIGHT node to ``node'' and in the function we can simply send an event to the ``set_on'' eventIn of the LIGHT on with
        node.set_on = TRUE;

Example 5.3.2   The Light-on problem without eventOUT  

VRML: ../examples/anim/anim-touch-3.wrl
Source: ../examples/anim/anim-touch-3.text

The script can access any exposedField, eventIn or eventOUT of any node to which it has a pointer in a ``field''. Note that eventIN of the passed node (in our example that is LIGHT) can be only to the left side (meaning you can set a value) and eventOUT of the passed node can only be on the right side (meaning you can read it). This needs some example .... hold on.

Here is another example that activates something. Instead of turning on/off a light, we change the ViewPoint. It has been strongly inspired from Markus Roskothen's Touch Me example. You can see an other example of using a TouchSensor with JavaScript.

Example 5.3.3   A viewpoint changing TouchSensor Script  

VRML: ../examples/anim/anim-move-1.wrl
Source: ../examples/anim/anim-move-1.text

   
5.3.2 Dealing with state

Let's assume that you that feel that two switches (ON and OFF) for turning on and off the light is too much.

Within a Script Node you can define fields that will serve as state variables like you would encounter in object-oriented programming. In other words you can remember things, e.g. you can store objects, colors, user activities and more. The only restriction is that those ``variables'' must be VRML data (field) types.

Example 5.3.4   The Light-on problem with state  

VRML: ../examples/anim/anim-touch-state.wrl
Source: ../examples/anim/anim-touch-state.text
Contents: JavaScript state, TouchSensor,

Here is the code of the Script Node that solves the light switch problem:

DEF lightScript Script {
   eventIn SFBool lightIsActive
   eventOut SFBool fixLightState
   field SFBool state FALSE
   
   # Only do something in the function
   # if lightIsActive = TRUE (the active param)
   # because as soon as the user releases the mouse
   # button a FALSE is generated again.
   url ["javascript:
        function lightIsActive(active) {
           if (active) {
              if (state == FALSE) {
                 state = TRUE;
                 fixLightState = TRUE;
              }
              else {
                 state = FALSE;
                 fixLightState = FALSE;
              }
           }
        }"
       ]
}

Our state variable that will remember if the light is on or off is declared as a boolean (note that name ``state'' is our choice):

   field SFBool state FALSE

Now if you read the specifications of the =>Touch Sensor node again you will learn that when the user clicks on it, it generates an isActive Event==TRUE. However when the user releases the mouse button, an isActive Event==FALSE is generated.

We are not interested in the fact that the user releases a button, so we ignore it. When we pass the value of the eventIn Field lightIsActive to the function we only do something if it is TRUE (as we said before):

              if (active) { .... }

Within the ``big'' if clause we then look at the value of our state variable. If state = FALSE we know that the light is off and we will turn it on and remember it by toggling the state variable. On the contrary we will turn the light on and remember that too.

Now of course we also should change the color of the light button. There are many ways to do it, e.g. change the color of the little cube or use a switch node to insert another one. We leave that exercice to the reader.

Well, now did we really had to make use of state variable ? The answer is NO. The PointLight node itself perfectly knows if it is on or off and we could have asked it each time the script node receives a ``lightIsActive'' Event. The next example exactly does this. However to show the usefulness of state variables we now count the number of times a user has switched on the light. After 10 trials we will turn on another light that will be lit permanently with the hope that this will discourage the person from playing too much with light switches.

Example 5.3.5   The Light-on problem with VRML state)  

VRML: ../examples/anim/anim-touch-state2.wrl
Source: ../examples/anim/anim-touch-state2.text
Contents: JavaScript, TouchSensor, VRML state

In order to do this we ``USE'' again handlers to VRML nodes somewhere in the scene as shown in the two following lines of code:

   field SFNode node USE LIGHT
   field SFNode node2 USE LIGHT2

Instead of asking a state variable if the light is on we then go and ask the light itself:

     if (node.on == FALSE) {....}

Finally we light the second light with the following instruction:

        if (n > 10) {
           node2.set_on = TRUE;
           }
Note that you can't turn it off again. Below you can study the whole function:

DEF lightScript Script {
   eventIn SFBool lightIsActive
   eventOut SFBool fixLightState
   field SFNode node USE LIGHT
   field SFNode node2 USE LIGHT2
   field SFInt32 n 0
   directOutput TRUE
   
   # Only do something in the function
   # if lightIsActive = TRUE (the active param)
   # because as soon as the user releases the mouse
   # button a FALSE is generated again.
   url ["javascript:

        }",
        "vrmlscript:
        function lightIsActive(active) {
           if (active) {
              if (node.on == FALSE) {
                 n = n+1;
                 node.set_on = TRUE;
                 if (n > 10) {
                   node2.set_on = TRUE;
                   }
              }
              else {
                 node.set_on = FALSE;
              }
           }
        }",
       ]
}

All this (I hope) did give you an idea about the ``dynamics'' potential of VRML. It wouldn't take much more to program a little bug that would crawl into the scene and remove the light switch for example after somebody played too much with it.

   
5.3.3 Touchsensors, ROUTE, Switch and Javascript

Let's assume again that you that feel that two switches (ON and OFF) for turning on and off the light is too much. You also want one switch that shows it's ``state'' and does both. Additionally you did not grasp how to deal with state (like I did when I built this example in spring 97).

 

In order to so you need to learn the =>Switch node. It allows to define several definitions of the same conceptual object (a bit like the LOD node), one of which is displayed at a given time.

Switch {
  exposedField    SFInt32 whichChoice -1
  exposedField    MFNode  choice      []
}

The WhichChoice field specifies the index of the list of nodes in choice that will be displayed. Note that the first element in the list is 0. If the index is less than 0 or bigger than than number of nodes, nothing is chosen.

The only tricky stuff (assumed that you understood the previous examples) is how to deal with the fact that all nodes under a Switch continue to receive and send events (i.e.routes) regardless of the value of whichChoice. So maybe what I did here is more or less illegal. I let you study the examples by yourself. In any case this example shows a particularly dumb way of programming a on/off switch but it also introduced the important switch node.

Example 5.3.6   The Light-on problem switch the dumb way(a)  

VRML: ../examples/anim/anim-touch-4.wrl
Source: ../examples/anim/anim-touch-4.text
Contents: JavaScript, TouchSensor, Switch Node

Example 5.3.7   The Light-on problem switch the dumb way (b)  

VRML: ../examples/anim/anim-touch-5.wrl
Source: ../examples/anim/anim-touch-5.text
Contents: JavaScript, TouchSensor, Switch Node

   
5.3.4 Scripting with TimeSensors

If you don't know the basics about =>TimeSensors, go and read section 5.2.1 on [*] again.

You can manipulate the following eventIns (among others):

In the next simple example we will show how to start and stop some animation. You can see something sliding forth and back once you click on the little green switch and you stop it with the red switch.

Example 5.3.8   Activating and Stopping a Shuttle  

VRML: ../examples/anim/anim-shuttle-2.wrl
Source: ../examples/anim/anim-shuttle-2.text

This example used pretty much the same interface mechanism as the light examples. However it represents an additional element in the interaction, i.e. an animation element.


next up previous contents index http://tecfa.unige.ch/ico/navi/tex2html/top.gifhttp://tecfa.unige.ch/ico/icons/vrml97-icon.gif
Next: 6. The External Authoring Up: 5. Introduction to moving, Previous: 5.2 Introduction to Events,
D.K.S. - 1998-03-18