| Java and VRML 2.0 Part 1: Basic theory
|
| by Rodger Lea
|
|
So, you've written a PROTO node, played around with touchsensors,
and maybe even written a spinning cube. What next? 'Cos frankly,
VRML is starting to look a little boring. The problem is that it's
easy to get locked into a mindset that views VRML as a self
contained system. Instead, if you start to think of VRML as a
display engine for applications then you're starting to break out of
the closed mindset. VRML applications should allow you to query
databases and display results, interact with users via pop up menus,
display your disk contents, access the web, work with others. To do
all of this you need to combine VRML with a fully fledged
programming language, such as Java, and these articles will show you
how.
|
|
|
In our first article we'll give you a techie's overview of the VRML 2.0
execution model and how Java can interact with VRML. The second
article will build on this to show you how to start writing serious
applications that use VRML to display results.
|
|
|
Both these articles are based on our book, Java for 3D and VRML
worlds, Lea, Matsuda, Miyashita, New Riders (ISBN 1-56205-689-1) and
are taken from early chapters. Later chapters discuss the use of
Java in more detail with full examples of Java windowing, a VRML
directory browser and a multiuser VRML server using Java networking
code.
|
Community Place
|
NOTE: You'll need a VRML browser that supports Java. We recommend
Community Place.
|
the VRML execution model isn't sophisticated
|
The execution model
Unlike VRML 1.0, VRML 2.0 supports behaviors, doing so through an internal
execution model; an execution model is a way to manipulate the basic data
structures that VRML offers: its internal nodes or new nodes that
you define with PROTOs. The VRML execution model isn't
sophisticated; it's simply an event mechanism that allows you to
propagate data values, as events, between fields of nodes. The
source and target for these events are specified using the ROUTE
construct. Although this model is simple, it is quite powerful
when used in conjunction with
existing nodes and allows you to build simple
animation and behaviors completely within VRML. However, one of the
reasons that VRML 2.0 architects kept the execution model simple was
because they wanted it to work in conjunction with full-fledged programming
languages, which would provide it with the more sophisticated
facilities such as comparison, looping and I/O.
|
|
|
A quick recap
We assume that people reading this article have a basic knowledge of
the way that VRML 2.0 can be used to create dynamic scenes, but for
those of you who may be sketchy on the details, here is a quick
recap.
|
|
|
Nodes, fields and routes
VRML 2.0's built-in nodes consist of a name and a set of
fields. Each field has a both category and a type. The type of the field
defines the data that it can hold, e.g. integers or strings. The
category of the field defines whether you can set, read or write the
field.
|
|
|
Fields of category Field are set at definition time by the
scene author and only change again when written; they can be read
but don't generate events.
|
|
|
written to. When this happens, the field is set to a new value, and
an event that contains the new value is generated.
|
|
|
eventIn fields
receive events. The value of the event is used to set the value of
the eventIn field. A special type of field, an exposedField, is
actually a shorthand method of saying that a field is both an
eventIn and an eventOut; i.e, it can be both source and target for
events.
Once we have a mechanism to allow fields to change, then we need a
way to send the events between these fields. The ROUTE mechanism is
designed to do that. It specifies a source field and a target field
for an event. By using a ROUTE, a scene author can specify an
eventOut field as the source of an event and "route" that event to
an eventIn field. By chaining eventIns to eventOuts, the author can
build chains of events, or event cascades, that propagate data from
node to node throughout the scene. Since these fields are also used
by the browser to display the scene; any changes in their values are
shown by the browser next time it renders the scene.
|
Obviously, you must have a source event to start an event cascade
|
Sourcing events, sensors and interpolators
Obviously, you must have a source event to start an event cascade.
VRML
2.0 provides two ways to create source events: Sensor nodes and
Interpolator nodes. Sensors track some external entity, e.g. the
mouse or time, and generate events as that external entity changes value.
Interpolators (which are usually used in conjunction with sensors)
generate values between defined starting and ending points, which can then
be used as events. These initial events can then be routed through
to the rest of the scene in the normal way.
|
|
|
Here is a simple example that allows you to turn on a light when the
mouse button is held down and turn it off when the button is
released. Line 9 defines a PointLight and line 14, a light
shade. Line 26 defines a TouchSensor which is attached to the
light because it is defined within the same Transform
node. Line 38 sets up the ROUTE between the TouchSensor named
LIGHT_ON_SWITCH and the PointLight named LIGHT. When the mouse clicks on it,
the
TouchSensor generates an
isActive event with a value of TRUE, which is then routed to the
PointLight setting its "on" file to TRUE also. When the mouse
button is released, the TouchSensor generates a FALSE event
which causes the light to be switched off.
|
1 #VRML V2.0 utf8
2
3 #
4 # turn on/off the light by pressing/releasing the mouse button.
5 #
6
7 Transform{
8 children[
9 DEF LIGHT PointLight{
10 on FALSE # initially the light is turned off.
11 },
12 Transform{
13 translation 0 1 0
14 children[
15 # lamp shade
16 DEF LAMP_SHADE Shape{
17 geometry Cone{
18 height 2
19 bottomRadius 4
20 bottom FALSE
21 }
22 }
23 ]
24 },
25 # sensor to turn the light on / off
26 DEF LIGHT_ON_SWITCH TouchSensor{}
27 ]
28 }
29
30 # dummy object to reflect the light.
31 Transform{
32 translation 0 -5 0
33 children[
34 Shape{geometry Box{size 10 0.3 10}}
35 ]
36 }
37
38 ROUTE LIGHT_ON_SWITCH.isActive TO LIGHT.on
|
To be able to perform these more complicated logic operations, you must use a programming language
|
Using Java
The basic method to building dynamic scenes is to route events between
fields in an arbitrary fashion and combineg that facility with sensor
and interpolator nodes, as just described. However, because
you are restricted to only being able to route a data item from one
node to another, you won't be able to handle a whole class of behaviors.
For example, you can't use this method to determine if the value of
position interpolator is greater than a certain value, and if so,
negate the value. To be able to perform these more complicated logic
operations, you must use a programming language.
|
|
|
Bridging from VRML to programs - the Script node
The Script node allows you bridge from VRML to access a programming
language while allowing your world to be independent of the language
you are trying to access.
The Script node is similar to any other VRML node in that it has
a name and a set of pre-defined fields. However, it is different in
that it can also have a set of user defined fields. These fields are
special because events sent to them will be automatically passed, by
the VRML browser from the Script node, to an associated piece
of programming code.
|
|
|
Here is the specification of the 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
}
|
|
|
The key field is the url field. This defines the name and location
of the program that 'manages' this Script node. Since this
article is about Java, we'll assume for the rest of the examples
that the program specified is a Java program. However, VRML 2.0 is
open; the Script node can be managed by any language that the
VRML browser supports.
|
|
|
The user defined fields of a Script node are no different
from the fields of any other nodes; they can be of category field,
eventIn or eventOut and can be routed to and from in the usual
way. They can't be of category exposedField because of the
complications that would impose on the execution model.
|
|
|
The event handler class - Java code to handle Script Nodes
The Java side of the picture is a piece of Java code that we refer
to as the 'event handler class' - this is a Java class that is
designed to handle events sent to the Script node. You should
note that there are two ways to access Java from VRML; One, referred
to as internal scripting, communicates directly with Java and the other
, referred to as external scripting, accesses Java running as
a seperate application using the External Application Interface
(EAI). The following discussion concentrates on internal scripting.
|
|
|
To make the following discussion more concrete, here is an example
of a simple VRML Script node and the associated event handler
class. The goal of this example is to use a TouchSensor to
switch the color of a Sphere from red to blue. If it's blue,
change it to red, and if red, change to blue. This is an example
that requires the use of a comparison, and so access to a
programming language. First the VRML file:
|
|
|
The VRML example for changing a Sphere color:
|
1 #VRML V2.0 utf8
2 Transform {
3 children [
4 DEF TS TouchSensor {} # TouchSensor
5 Shape {
6 appearance Appearance {
7 material DEF SphereColor Material { diffuseColor 1 0 0 } # red
8 }
9 geometry Sphere {}
10 }
11 ]
12 }
13
14 DEF ColorScript Script {
15 url "ChangeColor.class"
16 eventIn SFBool clicked
17 eventOut SFColor newColor
18 field SFBool on FALSE
19 }
20 # Routing
21 ROUTE TS.isActive TO ColorScript.clicked
22 ROUTE ColorScript.newColor TO SphereColor.set_diffuseColor
|
|
|
Note the TouchSensor, "TS" (line 4), which is routed to the
Script node field clicked using the ROUTE at line 21. This
ensures that whenever the the mouse is clicked over the light shade,
the TouchSensor is triggered, and an isActive event is routed
to the Script field "clicked". The route from the
Script node "newColor" to the Shape node's diffuseColor field
ensures that whenever the "newColor" field is written to, an event is
generated and passed along the route to the diffuseColor field of
the Shape node. Essentialy, the field "clicked" checks for click
events from the mouse, the field "on" holds the current status of
the Sphere color (TRUE equals blue, FALSE equals red) and the
field "newColor" holds the value that the Sphere should now be
set to.
|
|
|
The Java code which actually reads the current color from the field "on"
and sets the new color via the eventOut field "newColor" is shown
below:
|
1 import vrml.*;
2 import vrml.field.*;
3 import vrml.node.*;
4
5 public class ChangeColor extends Script {
6
7 private SFBool on; // status of on-off
8 float red[] = { 1, 0, 0 }; // RGB(Red)
9 float blue[] = { 0, 0, 1 }; // RGB(Blue)
10 private SFColor newColor ;
11
12 public void initialize() {
13
14 newColor = (SFColor) getEventOut("newColor");
15 on = (SFBool) getField("on");
16 }
17
18 public void processEvent(Event e) {
19 ConstSFBool v = (ConstSFBool)e.getValue();
20
21 if(v.getValue()){
22 if (on.getValue()) {
23 newColor.setValue(red); // set red to 'newColor'
24 } else {
25 newColor.setValue(blue); // set blue to 'newColor'
26 }
27 on.setValue(!on.getValue()); // = !on.getValue();
28 }
29 }
30 }
|
|
|
This example may seem a little confusing at first because it
contains quite a lot of information. We will use it to show you
three things: how to pass information from the Script node to
the event handler class using events, how to read a field in the
Script node from Java, and how to write back a result from
the event handler class to the Script node.
|
|
|
You should note that the Java file includes three packages, vrml.*,
vrml.field.* and vrml.node.* which contain the java classes that
support the Java API. You must import these packages into each event
handler class. You must also define you event handler class as an
extension of the Script class.
|
|
|
Receiving events and reading their values
The Script node contains three user defined fields, "clicked"
of type SFBool and category eventIn; "newColor" of type SFColor and
category eventOut; and "on" of type SFBool --initially set to FALSE--
and of category field. The Script node also defines an event
handler class, ChangeColor.class, which is responsible for dealing
with events sent to the Script node.
The name of the file which implements the event handler class (in
our case, ChangeColor.Class) as specified in the url field of the
Script node is important. The name of the event handler
class must equal the stem of the file name. In our example, the file
is ChangeColor.class, and the event handler class (shown in
ChangeColor.java) must be ChangeColor. This is necessary to allow
the browser to make the link between the Script node and the
event handler class.
|
|
|
Within the event handler class (ChangeColor) there is a special
method, processEvent(), which must always be present and is called by
the browser when an event is sent to the Script node. The
actual mechanism that ensures that an event sent to a field of a
Script node is forwarded to the Java event handler is
automatic, as it is the responsibility of the browser manufacturer to
make sure this happens. What is
important is that whenever an event arrives at an eventIn field of a
Script node, that event will always be delivered to the Java event
handler class and will cause execution of the special method,
processEvent().
|
|
|
Looking back at our example, you can see that that the method
processEvent takes a parameter "e" of type Event. Event is a
pre-defined Java class in the vrml.* package which holds the
actual event sent to the Script node.
|
|
|
In our example, we know that the only type of event that can arrive
at the Script node is of type SFBool since this is the only
eventIn field in the Script node. Therefore, the first thing
we do in the method processEvent() is convert the event into a
ConstSFBool value "v". All VRML data types have a corresponding Java
class, such as ConstSFBool. This new class "v" is initialised by
calling getValue on the "e" event. You should note here that we are
not using getValue on "e" to actually get the value, but to
initialize
an instance of a ConstSFBool class. We call this instance the
handle. To actually find out the value of the event, we need to call
getValue() on the handle, "v" - this returns the actual value of
"v"
the event. Hence, getting a value of an event is a two step process:
first, create an instance of whatever type the event is (e.g. a
boolean, ConstSFBool) and then, read the value of that instance using the
getValue() method.
we can also access other fields of the node that haven't received events
|
we can also access other fields of the node that haven't received events
|
Accessing another field of the Script node
We are not restricted to only receiving events from a Script
node; we can also access other fields of the node that haven't
received events. The next line in our example (line 22) checks the
value of the field "on" in the Script node.
if (on.getValue()) {
|
|
|
However, we have just stated that accessing a value is a two stage
process; first, get a handle to the value's class and then, get the
value itself. In the above, we only get the value. The first stage is
actually performed in another special method,
initialize(). initialize() is used to set up internal variables and
is guaranteed to be called by the browser after the event handler
class is instanciated but before it recives any events. Within
initialize() we create handles to the variable "newColor" and the
variable "on" by calling getEventOut() and getField() respectively.
|
|
|
Writing data back to the VRML scene
So far, we have checked the incoming event sent to the eventIn field
"clicked" to see if it equals TRUE. If so, we have accessed the
field "on" to see the current status of the Script node.
Now,
depending on the status of the Script node field "on", we will
decide to set the Script node field "newColor" to red or blue.
|
|
|
Lines 23 or 25 write a new color back to the scene graph by setting
the "newColor" field of the Script node. To do that, they use
the method setValue(). When this method is called, it actually
writes the new value to the field of the Script node. Since
the field newColor is of category eventOut, writing a new value to
it from the Java event handler causes it to generate an event
within the VRML scene. This event is routed to the diffuseColor of
the Sphere node (line 23 of the VRML file) causing the new
color to be sent to the Sphere node and so changing the color
of the Sphere in the VRML scene.
|
|
|
The net result: the TouchSensor monitors the mouse over the
Sphere. When clicked, the "click" event causes the event handler
class to read the current color of the Sphere from the
Script node, and if red set it to blue, or if blue, to
red.
|
|
|
Conclusion
This basic model of sending events to Script nodes, handling the
events in the event handler class, and returning results to the VRML
forms the basis of the link between VRML and Java. Once you've
understood this, you can write arbitrarily complicated scripts. In
the next article, we will build on this explanation and show you a
couple of examples that use this mechanism to enhance built-in VRML
nodes.
|
|
|
If your browser is already set up to handle Java, then you can copy
these examples, edit them, compile the java and test them out. Note,
your browser must support Java internally - accessing Java via the
EAI is not sufficient. If this is not the case, then you should download Community
Place. See the URL at the beginning of this article.
|
Community Place tutorials
|
In addition, there is a set of online tutorials for Community Place
which includes a page on setup for the Java environment.
|
|
|
The examples in this article are taken from the our book: "Java for
3D and VRML worlds" Lea, Matsuda and Miyashita. New Riders
Publishing, ISBN 1-56205-689-1 and are provided courtesy of New
Riders Publishing.
|
|
Rodger Lea is a senior research scientist at Sony's Computer Science
lab in Tokyo . He holds a PhD in computer science, usually works on
distributed operating systems but has, over the last year, moved up
in the world and developed the distributed architecture for Sony's
multiuser VRML system. He can be reached at
rodger@csl.sony.co.jp
|