Chapter 3:
Node Reference
Intro
Anchor
Appearance
AudioClip
Background
Billboard
Box
Collision
Color
ColorInterpolator
Cone
Coordinate
CoordinateInterpolator
Cylinder
CylinderSensor
DirectionalLight
ElevationGrid
Extrusion
Fog
FontStyle
Group
ImageTexture
IndexedFaceSet
IndexedLineSet
Inline
LOD
Material
MovieTexture
NavigationInfo
Normal
NormalInterpolator
OrientationInterpolator
PixelTexture
PlaneSensor
PointLight
PointSet
PositionInterpolator
ProximitySensor
ScalarInterpolator
Script
Shape
Sound
Sphere
SphereSensor
SpotLight
Switch
Text
TextureCoordinate
TextureTransform
TimeSensor
TouchSensor
Transform
Viewpoint
VisibilitySensor
WorldInfo
|
Script {
exposedField MFString url []
field SFBool directOutput FALSE
field SFBool mustEvaluate FALSE
# And any number of:
eventIn eventType eventName
field fieldType fieldName initialValue
eventOut eventType eventName
}
The Script node is used to program behaviour in a scene. Script nodes
typically
- signify a change or user action;
- receive events from other nodes;
- contain a program module that performs some computation;
- effect change somewhere else in the scene by sending events.
Each Script node has associated programming language code, referenced
by the url field, that is executed to carry out the Script node's
function. That code is referred to as the "script" in the rest of this
description. Details on the url field are described in "2.5 VRML and the World Wide Web."
Browsers are not required to support any specific language. Detailed
information on scripting languages may be found in "2.12 Scripting." Browsers supporting a scripting
language for which a language binding is specified shall adhere to that
language binding.
Sometime before a script receives the first event it shall be initialized
(any language-dependent or user-defined initialize() is
performed). The script is able to receive and process events that are
sent to it. Each event that can be received shall be declared in the
Script node using the same syntax as is used in a prototype definition:
eventIn type name
The type can be any of the standard VRML fields (as defined
in Chapter 4, "Field and Event Reference"). Name
shall be an identifier that is unique for this Script node.
The Script node is able to generate events in response to the incoming
events. Each event that may be generated shall be declared in the Script
node using the following syntax:
eventOut type name
With the exception of the url field, exposedFields are
not allowed in Script nodes.
TECHNICAL
NOTE: Defining exactly what it means
for a Script to have an exposedField gets complicated. It isn't
enough to say that an exposedField is equivalent to an eventIn,
field, and event Out. For example, if the following Script were
legal
DEF ILLEGAL Script {
exposedField SFBool foo FALSE
}
and
considered equivalent to
Script {
field SFBool foo FALSE
eventIn SFBool set_foo
eventOut SFBool foo_changed
}
a variety
of difficult questions would need to be addressed. Is the Script's
code required to generate foo_changed events when a set_foo
event is received, or is that done automatically for the Script
by the browser? If it is done automatically by the browser (which
would certainly be convenient for the person writing the Script),
is the Script's code also allowed to send foo_changed events
or change the foo field? And if it is done automatically
by the browser, then will it be done automatically in the second
previous example (where foo, set_foo, and foo_changed
are declared individually instead of as an exposedField)?
If foo_changed
events are not automatically generated when set_foo events
are received, is the Script required to generate them? If not,
then foo isn't really an exposedField, since the definition
of an exposedField involves both syntax (it is syntactically equivalent
to a field + eventIn + eventOut) and semantics (an exposedField's
semantics are that it generates _changed events and sets
the field whenever a set_ event is received).
ExposedFields
in Script nodes are a design issue that will probably be revisited
at some time in the future. Allowing a Script read-only access
to its exposedFields and allowing only the browser to generate
_changed events would be a good solution, but requires
that the notion of a read-only variable be supported somehow in
each scripting language. For VRML 2.0, the simple and conservative
solution of just not allowing Script nodes to have exposedFields
was chosen.
|
If the Script node's mustEvaluate field is FALSE, the browser
may delay sending input events to the script until its outputs are needed
by the browser. If the mustEvaluate field is TRUE, the browser
shall send input events to the script as soon as possible, regardless
of whether the outputs are needed. The mustEvaluate field shall
be set to TRUE only if the Script node has effects that are not known
to the browser (such as sending information across the network). Otherwise,
poor performance may result.
TECHNICAL
NOTE: Executing
a Script might be a fairly expensive operation, possibly involving
communication with a language interpreter that may be running as
a separate process. Therefore, VRML 2.0 was designed so that browsers
can queue up multiple events and give them to a Script node at the
same time. The mustEvaluate flag is a hint to the browser
that it should execute the Script as soon as possible after it receives
events, which is less efficient than waiting as long as possible
to execute the Script. |
Once the script has access to a VRML node (via an SFNode or MFNode
value either in one of the Script node's fields or passed in as an
eventIn), the script is able to read the contents of that node's exposed
fields. If the Script node's directOutput field is TRUE, the
script may also send events directly to any node to which it has access,
and may dynamically establish or break routes. If directOutput
is FALSE (the default), the script may only affect the rest of the
world via events sent through its eventOuts. If directOutput
is FALSE and the script sends events directly to a node to which it
has access, the results are undefined.
A script is able to communicate directly with the VRML browser to
get information such as the current time and the current world URL.
This is strictly defined by the API for the specific scripting language
being used.
The location of the Script node in the scene graph has no affect on
its operation. For example, if a parent of a Script node is a Switch
node with whichChoice set to "-1" (i.e., ignore its children),
the Script continues to operate as specified (i.e., it receives
and sends events).
TECHNICAL
NOTE: A couple of generalizations for the Script node were
considered but did not make it into the final VRML 2.0 specification.
One was the ability for a Script to add or remove fields, eventIns,
and eventOuts from itself dynamically while it was running. Combined
with the browser addRoute()and deleteRoute()
methods, this would sometimes be useful. However, it might be
difficult to implement and will be easy to add later if necessary.
Another generalization
along the same line is allowing a Script to declare that it can
receive events of any type, with the type determined by the Script
as it runs. This would require additional syntax (perhaps an "SFAny"
field pseudotype) and would affect the design of several other
features (such as PROTO and EXTERNPROTO). Again, this might make
implementation of the VRML specification significantly more difficult
and can be added later if it becomes clear that it is necessary.
|
TIP:
At present, there are two scripting languages supported in the
VRML specification: Java and JavaScript. There has been an endless
and raging debate in the VRML community on which language is
"better." The pro-Java camp believes that Java is a "real" programming
language and has much more power, flexibility, infrastructure,
and industry acceptance. These points are all true. The JavaScript
proponents state that JavaScript is much easier to learn and
use, especially if you are not a hard-core programmer. This
is also a reasonable position (debated strongly by Java programmers,
though). In general, when choosing a programming language, you
should first assess the problem you are trying to solve; second,
consider your own programming skills and experience; and then,
choose the language that best fits these two parameters. A gross
generalization is that Java is more capable of solving the difficult
or serious programming problems, such as network access, database
integration, multiusers, and so forth, while JavaScript is more
suitable for simple behavior scripting, such as "a combination
lock," "turn on the lights when . . . ," and so on.
Another
common generalization is that Java is a better choice for full-time
programmers (due to strong object-oriented architecture and
deep system libraries), while JavaScript is a good choice for
the part-time or amateur programmer (due to forgiving syntax
and lack of types). Also, it is important to note that the VRML
specification does not require either scripting language to
be supported. Therefore, it is important to verify that the
scripting languages you choose to use in your content are supported
by the browsers you intend to use.
|
EXAMPLE
(click to run):
The following example illustrates use of the Script node (see
Figure 3-46). This world defines a toggle button prototype,
Button, and a simple combination lock that composes three
Button nodes together with a Script node that verifies the
combination. Note that the first Script node is defined within
the prototype Button. This example is illustrated in both
Java and JavaScript:
#VRML V2.0 utf8
PROTO Button [
exposedField SFNode geom NULL
eventOut SFInt32 state_changed ]
{
Group { children [
DEF TOS TouchSensor {}
DEF Toggle Script {
eventIn SFTime touch
eventOut SFInt32 which_changed IS state_changed
url [ "javascript:
function initialize() {
// Initialize to 0th child at load time
which_changed = 0;
}
function touch(value, time) {
// Toggle the button value
which_changed = !which_changed;
}"
# Or Java:
"ToggleScript.class" ]
}
DEF SW Switch {
whichChoice 0
choice [
Shape { # child 0 - "off"
geometry IS geom
appearance DEF A2 Appearance {
material Material { diffuseColor .3 0 0 }
}
}
Shape { # choice 1 - "on"
geometry IS geom
appearance DEF A1 Appearance {
material Material { diffuseColor 1 0 0 }
}
}
]
}
]}
ROUTE TOS.touchTime TO Toggle.touch
ROUTE Toggle.which_changed TO SW.set_whichChoice
} # end of Toggle prototype
# Now, create 3 Buttons and wire together with a Script
Transform {
translation -3 0 0
children DEF B1 Button { geom Box {} }
}
DEF B2 Button { geom Sphere {} }
Transform {
translation 3 0 0
children DEF B3 Button { geom Cone {} }
}
DEF ThreeButtons Script {
field SFInt32 b1 0
field SFInt32 b2 0
field SFInt32 b3 0
eventIn SFInt32 set_b1
eventIn SFInt32 set_b2
eventIn SFInt32 set_b3
eventOut SFTime startTime
url [ "javascript:
function set_b1(value, time) {
b1 = value;
if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
}
function set_b2(value, time) {
b2 = value;
if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
}
function set_b3(value, time) {
b3 = value;
if ((b1 == 1) && (b2 == 0) && (b3 == 1)) startTime = time;
}"
# Or Java:
"ScriptLogic.class" ]
}
DEF T Transform { children [ # Explosion effect
Shape { geometry Sphere { radius 0.1 } } # Hidden inside
DEF SI PositionInterpolator {
key [ 0.0 1.0 ]
keyValue [ 0.01 0.01 0.01, 300.0 300.0 300.0 ]
}
DEF TS TimeSensor { }
NavigationInfo { type "EXAMINE" }
] }
ROUTE B1.state_changed TO ThreeButtons.set_b1
ROUTE B2.state_changed TO ThreeButtons.set_b2
ROUTE B3.state_changed TO ThreeButtons.set_b3
ROUTE ThreeButtons.startTime TO TS.startTime
ROUTE TS.fraction_changed TO SI.set_fraction
ROUTE SI.value_changed TO T.set_scale
ToggleScript.java:
/*
* ToggleScript.java
* Toggles an integer between 0 to 1 every time a time event
* is received
*/
import vrml.*;
import vrml.field.*;
import vrml.node.*;
public class ToggleScript extends Script {
SFInt32 which_changed;
public void initialize() {
which_changed = (SFInt32) getEventOut("which_changed");
which_changed.setValue(0);
}
public void processEvent( Event e ) {
String name = e.getName();
if ( name.equals( "touch" )) {
which_changed.setValue(1 - which_changed.getValue());
}
}
}
ScriptLogic.java:
/*
* ScriptLogic.java
* Receives set_b1/2/3 events, when correct combination
* is received outputs a startTime event.
*/
import vrml.*;
import vrml.field.*;
import vrml.node.*;
public class ScriptLogic extends Script {
int b1;
int b2;
int b3;
SFTime startTime;
public void initialize() {
startTime = (SFTime) getEventOut("startTime");
}
public void processEvent( Event e ) {
String name = e.getName();
if ( name.equals( "set_b1" )) {
b1 = ((ConstSFInt32)e.getValue()).getValue();
} else if ( name.equals( "set_b2" )) {
b2 = ((ConstSFInt32)e.getValue()).getValue();
} else if ( name.equals( "set_b3" )) {
b3 = ((ConstSFInt32)e.getValue()).getValue();
}
if ((b1 == 1) && (b2 == 0) && (b3 == 1))
startTime.setValue(e.getTimeStamp());
}
}
|

Figure 3-46: Script Node Example
|