Events and Routing
The fields of many nodes can be altered on-the-fly by sending ''events'' to the node. Events tell the node that the value of one of its field needs to be changed. Each node has one or more ''eventIn'' fields, which act rather like mail boxes for recieving incoming data.
Most nodes that have ''eventIn'' fields also have corresponding ''eventOut'', which can generate an event that is passed to the ''eventIn'' via a ''route''.
This is very powerful. So long as the data types you are routing are the same you can pass information between fields. To give a simple example, you could use this to turn the wheels on a car all at the same rate.

DEF wheel01 Transform { }
DEF wheel02 Transform { }
DEF wheel03 Transform { }
DEF wheel04 Transform { }
ROUTE wheel01.rotation_changed TO wheel02.set_rotation
ROUTE wheel02.rotation_changed TO wheel03.set_rotation
ROUTE wheel03.rotation_changed TO wheel04.set_rotation
In this way, when the rotation field of wheel01 is changed it gets passed to the rotation field of wheel02, and so on to wheel03 and wheel04.
You could also write this like:
ROUTE wheel01.rotation TO wheel02.rotation
without the ''set_'' prefix and ''_changed'' suffix, which ExitRealty can work out by itself. These do help to make your code more readable however.
This is all fine, except that nothing is going to happen at all unless you can make wheel01 turn in the first place. Lets have a quick look at how we can enable the user to interact with objects in the world.
The TouchSensor
''Sensor'' nodes in X3D send events out based on the actions of the user within the virtual 3D world. There are a number of different types, but for now we will look at the TouchSensor node. Have a quick read of the description of the node via that link.
The TouchSensor is quite easy to implement once you know how. You basically just drop it into a list of children of a grouping node, such as a Transform node. Unless it is disabled, it will affect each of the other children in that list; that is to say, its siblings. Here we define a TouchSensor called touchMe:
DEF parentNode Transform {
children [
DEF touchMe TouchSensor { }
DEF brotherNode Transform { ... }
DEF sisterNode Transform { ... }
]
}
Now, elsewhere within the file we can use the TouchSensor's eventOut fields to control an animation, or indeed lighting or pretty much anything else in defined our world file.
If we want the animation to only occur when the user hovers their mouse-pointer over the sibling objects we use the ''isOver'' field. If we only want it on the user clicking and holding we use ''isActive''. If we want to click and release to start an animation we can use the ''touchTime'' field. Lets do that.
Time Sensor
To start with we'll set up a TimeSensor.
DEF TS TimeSensor {
stopTime 1
loop TRUE
}
This will serve as the clock, or motor, that drives our animation. As always, see the Node Reference for more details on TimeSensor.
Elsewhere in the file we can associate the touchTime eventOut of the TouchSensor with the startTime exposedField of the TimeSensor:
ROUTE touchMe.touchTime TO TS.startTime
This will start the TimeSensor. Now, lets actually do something with it.
Interpolation
Interpolation, in mathematics at least, is the calculation of the value of a function between the values already known. This is really useful for animation, because you are going to want to render your object at every position along a path, but who wants to work out what all of those positions are going to be? Not me, and probably not you. A computer, using the PositionInterpolator node, will do it gladly.
DEF bounce PositionInterpolator {
key [0, 0.5, 1]
keyValue [0 -2 0, 0 10 0, 0 -2 0]
}
In the code above you can see that we have defined the ''bounce'' PositionInterpolator as having three "keys" and three corresponding key values. You will notice that the key values only change position along the y axis, starting at -2, moving to +10 and then returning to -2. Finishing where you start is a good idea if you are building a looping animation that will repeat itself.
Now we are ready to get animating.
We want to take the ''fraction_changed" eventOut field from the TimeSensor and route it to the ''bounce'' PositionInterpolator and then get the position values out of ''bounce'' and route them to the actual object that we want to make bounce up and down. We do this with the following code:
ROUTE TS.fraction_changed TO bounce.set_fraction
ROUTE bounce.value_changed TO objectGroup.translation

''bounce'' gets input from the TimeSensor and then objectGroup gets its new position data from ''bounce''.
objectGroup doesn't need to be a group, of course. You can make a simple bounce animation using the code that we have just discussed.
Color Interpolation
We won't go through all of the interpolators here, as they work by the same principles, but lets just visit the ColorInterpolator to see how to animate colors. This is a good example, because there is a bit of a trick involved. Have a look:
DEF colorWarp ColorInterpolator {
key [0, 0.3, 0.6, 1]
keyValue [1 0 0, 0 1 0, 0 0 1, 1 0 0]
}
ROUTE TS.fraction_changed TO colorWarp.set_fraction
ROUTE colorWarp.value_changed TO mat.set_diffuseColor
As usual, the interpolator node sets up the keys and their values, and then the TimeSensor is used to drive the interpolation. The trick comes in the last line, where we are routing the values from the interpolator to the object. Trying to route them to myObject.set_diffuseColor is NOT going to work. The field that you are referring to '''must be a child of the node that contains the field'''. In the Animation Example we had a Transform node called ''original'', but the diffuseColor field does not belong to it directly. It belongs to the Material node, which is undefined. In order to reference the diffuseColor field, we have to define the Material node with the expression:
material DEF mat Material {
which is nested within the Transform, Shape and Appearance nodes. (The name of the node can, of course, be any name that does not yet exist). We can then route the values from colorWarp to mat.set_diffuseColor and get a result.
Please refer to the VRML Node Reference in the Developer Center Wiki for further information on the other available interpolation nodes, including the ColorInterpolator, CoordinateInterpolator, NormalInterpolator, OrientationInterpolator, and the ScalarInterpolator.
ExitReality 3D Browser has also extended the VRML specification by implementing the CoordinateInterpolator2D. This and many other extensions to the VRML spec can be found at the ExitReality VRML Node Extensions page.
