|
|
VRML Interactive Tutorial
Full list
Script Tutorial
Introduction and Sintax
Defining Fields and Events
Accessing Fields and Events
When are scripts executed
Script Example: Highlight
Browser Script Interface
Appendix: Data types
|
|
|
|
Script Examples: Highlight
In this section it is shown how to highlight a shape when the cursor is
over it or when the mouse is pressed. There are many ways of accomplishing
this task, in here three different methods will be presented.
Using a Switch node
Changing the materials of a shape
Accessing a field directly
1.Using a Switch node
One way of changing the attributes of a shape is using a switch node in
which each choice contains the shape with certain attributes. When the
mouse is over the shape the whichChoice field of the switch is changed,
and when the mouse is no longer over the whichChoice field is changed
back to its initial value.
Putting it in an algorithmic form would result in something like:
Example:
-
-
if (mouse is over)
-
else
-
whichChoice = 0; // initial value
This is probably the simplest class of examples of scripts. As you've noticed
before you can route events from
one node to another, however the values associated with the events must
be of the same type, i.e. you can route booleans to booleans but not booleans
to integers. In this example we want to route a boolean value to an integer
event. You can't do this directly in VRML, you can only route events if
they have the same data type. Using a script is a simple way to overcome
this limitation.
First lets take a look at our world description dealing with the script
details afterwards.
Example:
-
#VRML V2.0 utf8
Group {
children [
DEF sw Switch {
whichChoice 0
choice [
Shape { appearance Appearance {
material Material {
emissiveColor 0.2 0.20.2
}
}
geometry Sphere {}
}
Shape { appearance Appearance {
material Material {
emissiveColor 0.5 0.50.5
}
}
geometry Sphere {}
}
]
}
DEF ts TouchSensor {}
....]
}
In the above described world there is a group containing a Switch
and a TouchSensor. The Switch
node contains two shapes which differ in the
emissive color, one is brighter
than the other as you can see by the values in the source code. By default
the darker shape is displayed because whichChoice is zero indicating that
it is the first children the one which is active.
When the mouse is over the shape selected in the Switch node the TouchSensor
will generate the boolean event isOver with a value TRUE, otherwise,
i.e. if the mouse isn't over the shape, the event isOver has a value
FALSE.
The highlight script must set the value of whichChoice to 1 when
it receives a boolean event with the value TRUE, and set whichChoice
to 0 when it receives a boolean event with the value FALSE.
To define the script node one must define the fields, events it receives
and sends. From the description above one knows that the script must receive
a boolean event, and generate a scalar, or integer, event. Therefore the
first lines of the script can be:
Example:
-
DEF highlight Script {
-
eventIn SFBool in
-
eventOut SFInt32 out
-
....
-
}
So far so good. Now lets define the script function. As mentioned before,
this function has the same name as the eventIn which is supposed to handle.
We also know that the functions to handle events have two parameters: the
value associated with the event, and its timestamp. Having this in mind
we can now write the header of the function:
Example:
-
function in(value, timestamp) { ... }
The body of the function is also simple in this case. When the value
TRUE is received an eventOut with a value of 1 is generated, and when
the value received is FALSE the eventOut generated is 0. As mentioned
above in order to generate events one needs only to make an attribution
to the respective eventOut as defined in the header of the script. The
complete function is:
Example:
-
-
function in2(value, timestamp) {
-
}
The only thing remaining is to include the function in the script. In this
case, because the script is so small, we are going to inline the function
so that it will appear in the same file as the rest of the VRML world.
The complete Script is as follows:
Example:
-
-
DEF switchScript Script {
-
eventIn SFBool in2
-
eventOut SFInt32 out
-
url "javascript:
-
function in(value, timestamp) {
-
}"
-
}
When inlining a function or set of functions in a Script node, the url
field value must be within quotes. Also note that the language in which
the script is written must be present.
Syntax:
url :"language_name: list_of_functions"
where language_name is the name of a supported language by your
browser/plugin (check the manual!).
Finally we just have to establish the routes between the nodes. We want
to send the event isOver from the TouchSensor to the Script node
eventIn in, and to send the eventOut out to the Switch node
whichChoice eventIn. The required routes are as follows:
Example:
-
- ROUTE ts.isOver TO switchScript.in2
-
ROUTE switchScript.out TO sw.set_whichChoice
This is it!
Conclusion
You can use the switch node to change parts of your world in runtime. The
use of a Switch node to change a single attribute of a shape is not the
simplest way of doing things. This particular example only requires you
to change an attribute in the material
of a shape, namely the emissiveColor. Because the emissiveColor is an exposedField,
i.e. it can receive events (set_emissiveColor) there is a more direct way
of doing it as we shall see in the next section. In order to justify
the use of a Switch node you should have at least several attributes different
not to mention completely different geometries.
2.Changing the material of a shape
In this example one is going to change the emissiveColor field of a material
using routes. Because one is not using a Switch node the static world is
much simpler. Let's take a look at our world description dealing with the
script details afterwards.
Example:
-
#VRML V2.0 utf8
Group {
children [
Shape { appearance Appearance {
material DEF mat Material {
emissiveColor 0.2 0.2 0.2
}
}
geometry Sphere {}
}
DEF ts TouchSensor {}
]
}
Note that the material of the shape is DEF'ed, this is because we need
to route the new emissive color to it.
The script to change the emissive color of the shape upon receiving
the event isOver from the TouchSensor needs to receive a boolean event
and generate a SFColor event (the data
type of emissiveColor). Therefore
the first lines of the script can be something like:
Example:
-
-
DEF highlight Script {
-
eventIn SFBool in2
-
eventOut SFColor out
-
....
-
}
The function to process the eventIn in2 can be defined in rough terms as:
Example:
-
function in2(value, timestamp) {
-
if (value)
-
generate event with light color;
-
else
-
generate event with dark color;
-
}
In order to generate the event, and as seen before, one has only to set
a value to out. A SFColor value is the equivalent of a three element
array in JavaScript, see data types.
In order to set a value to the eventOut out you have to set each
component of the array in a separate statement. The complete function is:
Example:
-
-
function in2(value, timestamp) {
-
if (value) {
-
out[0] = 0.5;
-
out[1] = 0.5;
-
out[2] = 0.5
-
}
-
else {
-
out[0] = 0.2;
-
out[1] = 0.2;
-
out[2] = 0.2
-
}
-
}
Note: Recall that in JavaScript indexing arrays starts from zero, not from
one.
The necessary routes to complete this world are:
Example:
-
ROUTE ts.isOver TO switchScript.in2
ROUTE switchScript.out TO mat.set_emissiveColor
Only the second route differs from the first example. In the former example one
was trying to change the whichChoice field of a Switch node, whereas
now one is changing the emissiveColor of a Material node (previously
defined as mat).
Conclusion
This is a far more direct way of doing things than in the previous
example. Although the script is slightly longer, the static world is far
more simpler, and the reasoning more direct.
However there is yet an easier way of doing this, by accessing the field
emissiveColor directly. The details are presented in the next section.
3. Accessing a field directly
In this section it is shown how to change the value of a field without
using routes, i.e. directly accessing the fields value.
The static world is the same as in the previous example. The only differences
are in the script itself and in the number of routes required.
The script for this example still receives an event when the mouse is
over the shape, however no event is generated because the value of the
emissiveColor field is changed directly from within the script.
We still need a field SFColor, but this time an eventOut is not required.
In order to change a field of a node directly we also need to have access
to the node, i.e. to have a field SFNode with the desired node as its initial
value. There is one more requirement to access a field directly, to have
the scripts field directOutput set to TRUE. The first lines of the
Script look like:
Example:
-
-
DEF highlight Script {
-
eventIn SFBool in2
-
field SFColor out 0 0 0
-
field SFNode m USE mat
-
directOutput TRUE
-
....
-
}
The function in2 can be defined as:
Example:
-
-
function in2(value, timestamp) {
-
if (value) {
-
out[0] = 0.5;
-
out[1] = 0.5;
-
out[2] = 0.5;
-
}
-
else {
-
out[0] = 0.2;
-
out[1] = 0.2;
-
out[2] = 0.2;
-
}
-
m.emissiveColor = out;
-
}
There is only one route required:
Example:
-
ROUTE ts.isOver TO switchScript.in2
Note that, when comparing with the previous examples, there is a route
missing. In this last example we do not need to create a route to change
the emissiveColor field of the Material since this is done in the
Scritp directly.
Conclusion
Using direct access to fields in other nodes is an alternative to
routing. As far as I know there are no disadvantages or advantages to either
in terms of performance. In terms of design both have their pros and cons
as you can see from the code.
|