Pioneering efforts to combine Java and VRML have lead the VRML community to embrace the
Java/VRML connection in the 2.0 Specification of VRML.
There are four significant ways for VRML and Java to interact.
- In VRML 2.0, Script nodes allow you to fashion nodes that are described by
Java classes using the VRML 2.0 Java API. (Nodes are the building blocks of
VRML worlds.)
- Alternatively, you can use the VRML 2.0 Java API to link into a VRML world
from outside the VRML file.
- If you are using Netscape's Live3D VRML browser,
you can use Netscape's LiveConnect architecture to link into a VRML world
from a Java applet.
- Or you could just write your whole browser in Java,
as Dimension X has done with their Liquid Reality VRML toolkit.
With LR, you can extend the browser in Java using its special API.
This article takes a brief look at these interactions between
Java and VRML, with an in-depth look at the VRML 2.0 Java API
through an example from Sony's Community Place VRML 2.0 browser.
(For more information on applications of Liquid Reality, see
our article on Liquid
Reality: Physics and Locales by Christopher Blohm.)
Current VRML browsers which support Java are Dimension X's
Liquid Reality, Netscape's Live3D, and Sony's Community Place (formerly
known as CyberPassage). Dimension X and Netscape have their own special Java APIs.
Sony's Community Place uses the VRML 2.0 Java API. The Sony team led
the development of the VRML 2.0 Java API specification. Both Community Place
and Liquid Reality are VRML 2.0 browsers.
Liquid Reality's Core package recently was licensed by Microsoft to
form the underlying technology of Microsoft's VRML 2.0 support. Since Liquid
Reality uses native methods, this means Microsoft essentially licensed
the native methods only for Windows operating systems.
Why would you want to use Java with your VRML?
One excellent
capability of Java is its simple networking classes. You can use this
to write Java that will create VRML based on other data on the network.
So you could create multi-user VRML worlds, visualizations of the Internet
or visualizations of data warehouses.
You can also write corporate applications
with virtual reality interfaces. You could even create CAD or VRML authoring software
using VRML 2.0 and Java. I expect we soon will see a move to put authoring tools
inside the VRML browser, rather than as external applications.
Why would you want to use VRML with your Java?
Anyone who's
ever written or viewed a Java animation applet knows the pain of waiting for all
of the animation frames and segments to download. And if you want to
do 3-D animations with Java, you're really facing a challenge, both from a
programming and graphic design standpoint.
VRML provides a specification
of the entire 3D object and its basic properties. And savvy authoring combined
with compression can give you delightfully small file sizes. Thus, you can achieve huge
bandwidth savings plus cost savings by using a VRML world animated by
a Java applet, rather than 2D GIF or JPEG images animated by a Java world.
As an example, on a project for Goldberg Moser O'Neill Advertising for
Symantec Cafe's online Web advertising, Aereal did both image versions and
VRML versions of a frog in a blender! The Java applet only took up 6K, but
the 2D images took up almost 100K.
However, the VRML version only took 5K compressed! With VRML 2.0 we can use
the VRML version and a similarly sized Java applet and really let loose!
VRML 2.0 Script nodes
Using Script nodes in VRML 2.0, you can use Java classes to affect the
VRML world. With Script nodes, you are essentially creating a new node, with
associated fields and events. For instance, if you are creating a multi-user
Script node, you might have the following properties.
eventIn for movements by the user,
eventOuts for movements of the current avatars,
eventOuts to transition for new avatars entering the world,
eventOuts for garbage collection of avatars who have left the world, and a
field for the number of avatars currently in the world
The Java class referenced by this Script node could do the "plumbing" in
between, transmitting position changes to and from other users or a multi-user
server over the network.
When it comes to programming the actual Java class for this Script node, fields
and eventOuts correspond to properties of the class, while eventIns correspond to class methods.
eventOuts can be referenced using their setValue() method.
In addition there is a constructor method for the class. Plus there is also an
eventsProcessed method, which will be called after a set of events has been received
by the Script node.
Java classes for use with the Script node must import the vrml package. They
also are extensions of the Script class in the vrml package, as shown below.
import vrml;
class MultiUser extends Script {
...
}
VRML 2.0 Java API Example
As an example of how the VRML 2.0 Java API works, we'll look at "drive",
which comes with Sony's Community Place VRML 2.0
browser. (Look in c:\Program Files\Sony\CyberPassage\contents\drive
to find the files we'll be examining.) The world contains a car ("buggy") which
can be started or stopped by touching it (i.e. clicking on it with the mouse).
Once you start it, it moves around a course in a fixed route.
VRML Source
The overall VRML file, main.wrl, just inlines two worlds, buggy.wrl
and world.wrl, which are inside the models directory. The
file buggy.wrl contains the VRML description of the car, including
the Script node which references the Java class, buggy.class, which
is in the classes directory. The important lines are the following.
1 DEF SCRIPT Script{
2 url "classes/buggy.class"
3 scriptType "javabc"
4
5 eventOut SFRotation buggyRt
6 eventOut SFVec3f buggyTr
7
8 eventIn SFTime moveBuggy
9 eventIn SFBool getOnOff
10 }
Let's look at this Script node line by line. In the first line, we define
the Script node to be called by the name SCRIPT. This name
is used at the bottom of the VRML file for the ROUTE statements which link the
Script node outputs to the actual buggy geometry. The ROUTE statements will also
link the TouchSensor on the buggy and a TimeSensor to the Script's input events.
The second and third lines are the url and scriptType fields of the Script
Node. The url field gives the URL which contains the script code. The scriptType
field indicates that the script is given in Java bytecodes at that URL.
The following lines, 5-9, define the interface of this script with the rest
of the VRML world. First come the eventOuts. Line 5 defines an eventOut
called buggyRt -- which can stand for buggy rotation -- of the type SFRotation.
SFRotation is the VRML field type for rotations, which consists of four floats,
the first three giving a vector, and the fourth giving a rotation angle in
radians. The rotation works by rotating the default orientation ( 0 0 -1
or into the negative z axis, which is the direction you are facing in a VRML
world by default when you load it up, assuming no alternate viewpoints have
been set up) around the vector by the rotation angle.
The second eventOut is buggyTr -- which can stand for buggy translation --
of field type SFVec3f. Translation means movement, so this event is related
to moving the buggy, as if we could pick it up and move it to a new position.
The field type SFVec3f is a vector of three float numbers (like decimal numbers).
The eventOuts will be generated by the Java class, buggy.class, giving
the location of the buggy and the direction to point it in.
The eventIns are moveBuggy of field type
time SFTime, and getOnOff of field type SFBool. These are messages
that will be sent to the script from other parts of the VRML world. The eventIns
correspond to methods in the Java class.
Java Source
We look next at the Java source code, buggy.java, which is
included in the classes directory. Here is an outline of
the source file.
// import vs (Sony) and vrml (VRML 2.0 Java API) packages
import vs.*;
import vrml.*;
public class buggy extends Script{
// define variables
// constructor method
public buggy() {}
// invoked every 350ms -- moves the buggy
public void moveBuggy(ConstSFTime time, ConstSFTime ts) {}
// get on/off the car. (opens link to HTML page)
public void getOnOff(ConstSFBool arg, ConstSFTime ts) {}
}
First, the packages for the VRML 2.0 Java API and Sony's browser
are imported. Then the class is defined. Note that it is a public
class and that it extends Script. This is just analogous to
applets extending the java.applet.Applet class. The three methods
include the constructor method, buggy(), which is called
when the class is first loaded, and the methods corresponding
to the eventIns, moveBuggy() and getOnOff().
Java Variable Definitions
Here are the variable definitions from buggy.
float aRad = (float) (Math.PI/180.0) ;
// get the reference to the buggy.
SFRotation buggyRt = (SFRotation)getEventOut("buggyRt");
SFVec3f buggyTr = (SFVec3f)getEventOut("buggyTr");
int count;
boolean ride = false;
float[] positionX;
float[] positionY;
float[] positionZ;
float[] angle;
float[] translation;
float[] rotation;
float rotkeep = 0.0f ;
The properties buggyRt and buggyTr correspond to the eventOuts
in the VRML file. The property ride is a true or false (boolean)
variable which keeps track of whether or not the buggy is currently moving.
The arrays of floats (positionX[], positionY[], etc.)
will be used in the constructor method to store the sequence of positions
and orientations the buggy will move through.
Constructor Method
In the constructor method, buggy(), the method
initializes creates instances of the arrays. Then it fills the
positionX, positionY, positionZ
and angle arrays with preset values of the course
the buggy will drive through.
// initialize
public buggy()
{
positionX = new float[156];
positionY = new float[156];
positionZ = new float[156];
angle = new float[156];
translation = new float[3];
rotation = new float[4];
positionX[0] = 0f;
positionY[0] = 0f;
positionZ[0] = 0f;
angle[0] = 8f;
// 155 more sets of values are
// initialized in the above arrays
// ...
// initialize count.
count = 0;
// initial rotation.
rotation[0] = 0.0f;
rotation[1] = 1.0f;
rotation[2] = 0.0f;
rotkeep = 43.0f ;
rotation[3] = rotkeep * aRad ;
buggyRt.setValue(rotation);
}
At the end of method (we omit the 155 repetitions of the array
value settings), a few more variables are initialized. The variable
count keeps track of what iteration the buggy is at
in its run around the course. Then the initial orientation of the
buggy is set, by passing the value of rotation to
the setValue() method of object buggyRt.
Since buggyRt is associated with the eventOut of the
Script node, this means that an eventOut message is generated and
passed to the VRML world, and then routed (because of the ROUTE
statement in the VRML file) to set the orientation of the buggy.
In this particular case, the buggy is only moving around a flat surface,
which makes calculating the value of rotation rather easy.
Remember, the first three values of an SFRotation are a vector. In this
instance -- at the end of the constructor -- the vector is (0,1,0).
The second number is y the vertical direction. Thus,
the vector is just pointing straight up. The fourth value is the
rotation angle of the car around this vector, which corresponds directly
to the car turning left or right while moving on a flat surface.
The moveBuggy() method
The moveBuggy() method moves the buggy, by updating
both the translation and the rotation of it. Remember that
moveBuggy() corresponds to an eventIn property
of the Script node, and the output translation and rotation
correspond to eventOuts.
The method is called every time it receives an eventIn message.
The eventIn message will contain two pieces of information:
- a value called
time of type SFTime.
- the time stamp
ts -- the time the message was sent at
(A time stamp is a part of every eventIn).
A quick look back at the VRML file, buggy.wrl, reminds us where
the second piece of information comes from.
ROUTE TOUCH_SENSOR.isActive TO SCRIPT.getOnOff
ROUTE TIME_SENSOR.cycleTime TO SCRIPT.moveBuggy
ROUTE SCRIPT.buggyTr TO BUGGY_TRANSFORM.set_translation
ROUTE SCRIPT.buggyRt TO BUGGY_TRANSFORM.set_rotation
It comes from a TimeSensor node in the VRML 2.0 world.
You can think of the TimeSensor node as a clock or stopwatch that
is keeping everything in the world organized and in sync,
no matter what the real time is.
This stopwatch is what controls when to run the moveBuggy
method -- in this case, every 350 milliseconds.
// invoked every 350ms.
public void moveBuggy(ConstSFTime time, ConstSFTime ts)
{
// move.
if (ride) {
translation[0] = positionX[count];
translation[1] = positionY[count];
translation[2] = positionZ[count];
buggyTr.setValue(translation);
// rotate.
rotkeep += angle[count] ;
rotation[3] = rotkeep * aRad;
buggyRt.setValue(rotation);
// count up.
count++;
if(count >= 156){
count = 0;
}
}
}
First, the method checks to see if the buggy is currently
moving. If so, it creates an SFVec3f object containing
the new position the buggy will move to. Then it passes
this object to the setValue method of the
buggyTr object, in effect creating an eventOut
message.
Then it updates the rotation of the buggy, just as it did
at the end of the constructor method. Finally it increments
or resets the count counter to ready the class for the
next movement using the preset array previously initialized.
The getOnOff() method
If you have Netscape started up when you start up
Sony Community Place, the getOnOff() method can communicate
with the browser using Sony's vs package that was imported
at the beginning of the Java source file. This is the loadworld()
method of the Browser object you see in the method source listing below.
// get on/off the car.
public void getOnOff(ConstSFBool arg, ConstSFTime ts)
{
if(true == arg.getValue()){
// when the button is pressed, return immediately.
return;
}
String s[] = {"html/drive.htm"};
Browser.loadWorld(s);
// the button is released.
if(true == ride){
ride = false;
}else{
ride = true;
}
}
Now, the beginning may seem confusing. What's happening is that
the eventIn is coming from a TouchSensor node that has been
routed to the Script node. TouchSensor nodes will transmit
a "true" signal when a user touches an object in the world, and a "false"
signal when the user stops touching the object.
So if the user is starting to touch the buggy (true == arg.getValue()),
the method returns and doesn't do anything else. (However, if you were creating
a car door that could be opened, you might want the method to do something
while they were touching the car door.) When the user releases their touch
(i.e. click and release the mouse), that's when we want the buggy to be affected.
In that case, the buggy will stop moving or start moving. The method adjusts
this by changing the ride flag. Remember that the moveBuggy()
method checks to make sure ride is true before it moves the
buggy.
And that's all there is to buggy.class.
Well -- now you're an expert on VRML 2.0's Java API. Congratulations!
Netscape's LiveConnect
Netscape's Live3D team has developed a plug-in API for Live3D that uses
Netscape's LiveConnect architecture. This lets developers access VRML worlds
through Java or JavaScript. Netscape has developed this as
an intermediary step to their VRML 2.0 Java API. They will support both
the required VRML 2.0 Java API, plus their own API. The current API
will only work on Windows 95/NT.
To use LiveConnect, you create an HTML page with a VRML world embedded in it with the <EMBED>
tag. Then you can also have a Java applet in the HTML page with an <APPLET>
tag which communicates with the Live3D plug-in. Alternatively, you can
use JavaScript to access the plug-in. For instance, you could create a form
with <INPUT> tags set to "TYPE=BUTTON" and have the JavaScript calls in the
ONCLICK field of the <INPUT> tag. When using LiveConnect, you will want
to name the instance of the Live3D embedded world. You can do this using
the NAME field of the <EMBED> tag, as follows.
<EMBED NAME="vrmlworld" SRC="http://www.aereal.com/home.wrl.gz" HEIGHT=100 WIDTH=100>
The API is heavily documented at Netscape's site.
You may have trouble compiling LiveConnect Java source code if you
are using Symantec Café. I ended up having to go back the Sun
Java compiler. Either way, you need to make sure you have the Netscape
Navigator and Live3D LiveConnect files set up in your CLASSPATH (for
Windows 95/NT users).
C:\Program Files\Netscape\Navigator\Program\java\classes\java_30
C:\Program Files\Netscape\Navigator\Program\plugins\npl3d32.zip
Since this API is still bleeding edge (though much more stable
than before!), you will probably also want to monitor the Live3D
Newsgroup on Netscape's server. See the Live3D Web site for a link
to the newsgroup.
Liquid Reality
Dimension X wrote the first combinations of Java and VRML with its
Liquid Reality VRML Toolkit. Liquid Reality is now available in a VRML 2.0
version. You could try writing your own VRML browser in Java, or you could
start with Liquid Reality and write your own VRML/Java with Liquid
Reality. You can easily create your own nodes that are described by Java
classes.
No matter which API you choose to start with, combining Java and VRML
is lots of fun, and hey -- it sure beats double-buffering!