Subscribe for FREE - Win a FREE T-shirt

Discussion Area

VRML by the Pound

People Linking to Us
Tell a Friend

We'll Link to You!!

Aereal, Inc.
   Instant VRML World
   Proteinman's Top 10 Worlds

view me!

Java and VRML 2.0 Part 2: Putting theory into practice
by Rodger Lea

Ok, so grace of last month's epistle you've got the basics of Java and VRML 2.0 sorted, you sense the power, you see the vistas but you need that little extra help to get you up and authoring. Fear not, this article will show a few choice little snippets of code that will burn off any lingering fog and open up those vistas. What's more, after we've given you all the details, we'll point you towards an authoring tool that will take care of the details, leaving you to fill in the creative parts.

In last month's article we introduced you to the basic theory and mechanisms for connecting a Java script to a VRML scene and communicating between the two. In this article we'll put some of that theory into practice by showing a few examples of what you can do with these two powerful tools.

Java Tutorial

Like the previous article, this one is based on our book, Java for 3D and VRML worlds, (Lea, Matsuda and Miyashita, New Riders Publishing, ISBN 1-56205-689-1) which covers in detail how to use Java to build active VRML scenes. The book goes into more detail and includes examples showing use of AWT, threads and multi-user VRML servers using Java networking. The book is accompanied by a CD with all source code, Sony's VRML 2.0 browser, a Java compiler and all instructions you need to get started with Java and VRML.

A brief recap
In the previous article we showed you how the internal execution model of VRML 2.0, based on events, could be used to communicate with a piece of Java code we referred to as the event handler class. The bridge between the VRML and Java worlds was provided by the special Script node which passed events from the VRML world across to the event handler class. Events generated in the VRML scene, when routed to the eventIn fields of the Script node are delivered into the event handler class of the associated Java program as specified in the Script node. Conversely, within the Java code, an event can be written into the Script node in the VRML scene which can then be routed to other nodes in the scene.

Once you have mastered this basic mechanism then you are free to use Java to manipulate VRML in anyway your imagination can dream up. To help you on your way, here are a couple of examples designed to let you see some of the possibilities.

Random movement of a scene object
The example VRML file below is taken from a larger example in the book which shows how to create an intelligent agent that tracks your location and follows you. This agent will also speak and gesture. In this article we've restricted ourselves to a simple subset of that functionality. Firstly we'll show how to create a randomly moving object (representing the agent), then we'll show how to have that object track your movement through the VRML scene and follow you.

Looking at the VRML 2.0 code below, at line 4 there is a TimeSensor which invokes a Script node named "AGENT_SCRIPT" every 0.1 seconds. This is an example of a repeating timer. Because the start and stop time have a default value of 0, and the loop field is set to TRUE, the timer runs continuously from the point when the world is loaded. Every 0.1 seconds it generates a cycleTime value, containing the current time, which is routed to the Script Node. The associated Java program uses this event to randomly move the Transform node which includes the geometry representing the scene object to be moved. As a result, the object appears to be floating back and forth in a random fashion. Because the geometry for the object is relatively complex, it is not included directly in the file, but indirectly by using the Inline node (line 10) which points to another VRML file, in this case ManDetailed.wrl. (you should insert your own file here) The browser will automatically read in this description when needed.

1   #VRML V2.0 utf8
2   # an object is floating randomly.
3   #
4   DEF AGENT_TIME TimeSensor{
5      loop TRUE
6      cycleInterval 0.1
7   }
8   DEF AGENT Transform{   # floating agent
9      children[
10         Inline{url "ManDetailed.wrl"}  # a model of agent.
11      ]
12   }
14      url "FloatingAgent.class"
15      eventIn SFTime interval
16      eventOut SFVec3f setAgentPosition
17   }
18   ROUTE AGENT_TIME.cycleTime TO AGENT_SCRIPT.interval
19   ROUTE AGENT_SCRIPT.setAgentPosition TO AGENT.set_translation

Line 18 routes the event from the Timesensor to the Script node's eventIn field, interval. This is then passed across to the event handler class "FloatingAgent.class".

Looking at the event handler class in the Java listing in detail shows that the initialize() method is used to create a reference to the Script node(IU(Bs setAgentPosition field. This field is used to receive the agent's new position when it is calculated. In addition, the agent's initial position is set up at line 18. In the processEvent() method, you can see at line 32, that receipt of the TimeSensor event causes the script to generate a random number ranging from -0.1 to 0.1. The random number is generated by using the randomNumberGenerator class which is part of the java.util package.

1   // an agent is floating randomly.
2   //
3   import java.util.*;
4   import vrml.*;
5   import vrml.node.*;
6   import vrml.field.*;
8   public class FloatingAgent extends Script{
9      SFVec3f setAgentPosition;
10      float agentPosition[] = new float[3];
11      Random randomNumGenerator = new Random();
13       public void initialize(){
14         // get the reference of the event-out 'setAgentPosition'.
15         setAgentPosition = 
16            (SFVec3f)getEventOut("setAgentPosition");
17         // initialize the agent position.
18         agentPosition[0] = 0.0f;
20         agentPosition[1] = 0.0f;
21         agentPosition[2] = 0.0f;
22      }
23      public void processEvent(Event e){
24         if(e.getName().equals("interval") == true){
25            moveAgent();
28         }
29      }
30      // generate random float value ranging between -0.1 to 0.1.
31      float generateRandomFloat(){
32         return(randomNumGenerator.nextFloat() * 0.2f - 0.1f);
33      }
34      // move the agent randomly.
35      void moveAgent(){
36         agentPosition[0] += generateRandomFloat();
37         agentPosition[1] += generateRandomFloat();
38         agentPosition[2] += generateRandomFloat();
40         // move the agent to the new position.
41         setAgentPosition.setValue(agentPosition);
42      }
43   }

Using this random number, the script moves the Transform that represents the agent by simply setting its position co-ordinates to a random position within a small distance of its current position. This value is written back to the Script node at line 41 using the setValue method on the setAgentPosition variable. Since this is a handle holding a value retrieved from the VRML scene, setting a new value also causes the browser to propagate the value across to the VRML scene and into the setAgentPosition field of the Script node.

In turn, because the setAgentPosition of the Script node is an event out, the value is then routed through to the agent's Transform node's translation field. This kind of random movement, or a more complex behavior (for example, changing the direction according to a state variable), is impossible to realize with only a time sensor and an interpolator.

While this example is relatively simple, it shows you the basic mechanism needed to move any object in your VRML scene in any arbitrary manner. Just by substituting lines 36-38 with your own algorithm you can animate objects in any way you wish.

Following the viewer
A very simple extension to the random agent example is to have the agent follow the user as they navigate through the scene. To achieve this, all that needs to be added to the VRML file is a means to track the user's position and the mechanism to send this across to the event handler class.

To track a user in the scene, a proximity sensor can be used. This is initialized with a bounding box sufficiently large to encompass the whole scene.

Then a route is set up that sends the user's position, as recorded by the proximity sensor, to the script node.

Here is a code fragment:

3   # initial viewpoint
4   Viewpoint{
5      position 0 2 0
6   }
7   DEF PROX_SENSOR ProximitySensor{
8      size 100 100 100  #always in the sensor field.
9   }
24   DEF PROX_SCRIPT Script{
25      url "FollowingAgent.class"
26      eventIn SFVec3f currentPosition
27      eventOut SFVec3f setAgentPosition
29   }
30   ROUTE PROX_SENSOR.position_changed TO 
31        PROX_SCRIPT.currentPosition
32   ROUTE PROX_SCRIPT.setAgentPosition TO AGENT.set_translation

This value is then used by the event handler class to calculate a new position for the agent that allows the agent to appear to follow or track the user as s/he moves through the scene. Here is another code fragment which calculates the agents new position. The calculation simply reads the user's current X, Y and Z positions and adds a fixed offset held in agentDefaultPosition.

35     void moveAgent(){
36         // translate the agent according to your current position.
37         agentPosition[0] = 
38            yourPosition[0] + agentDefaultPositionX;
39         agentPosition[1] = 
40            yourPosition[1]+ agentDefaultPositionY;
41         agentPosition[2] = 
42            yourPosition[2]+ agentDefaultPositionZ;
43         // move the agent to the new position.
44         setAgentPosition.setValue(agentPosition);
45      }

Dynamically adding to the scene
Being able to construct scenes and manipulate them is great fun, but you are limited to the original scene. You may want to go further, to dynamically add to existing scenes or even construct them on the fly. The following example does just that. It automatically generates a new piece of VRML code, in this case, a sphere node, which is stored in a string. This string is then used with the browser method createVRMLFromString() to dynamically add the Sphere node to the current scene. For the sake of brevity we will not show the VRML scene. Assume it contains a Script node with an eventIn field, touched, that is set by a TouchSensor.

1   //
2   // dynamic generation of geometry nodes
3   //
4   import java.util.*;
5   import vrml.*;
6   import vrml.node.*;
7   import vrml.field.*;
9   public class DynamicGen extends Script{
10       MFNode addSphere;
11       Random randomNumGenerator = new Random();
12       float posX = 0.0f;
14      public void initialize(){
15           // get the reference of the event-out 'addSphere'.
16           addSphere = (MFNode)getEventOut("addSphere");
17       }
19       public void processEvent(Event e){
20           if(e.getName().equals("touched") == true){
21               String sphereDesc1 =
22                   "Transform {" +
23                   "    translation ";
24               String sphereDesc2 =
25                   "        0.0 0.0 " +
26                   "    children[" +
27                   "    Shape{geometry Sphere{}" +
28                   "          appearance Appearance{" +
29                   "              material Material{diffuseColor ";
30               String sphereDesc3 =
31                   "              }" +
32                   "          }" +
33                   "    }" +
34                   "    ]" +
35                   "}";
37               float red = randomNumGenerator.nextFloat();
38               float green = randomNumGenerator.nextFloat();
39               float blue = randomNumGenerator.nextFloat();
41               Browser browser = getBrowser();
42               BaseNode baseNodes[];
44               // next position.
45               posX += 3.0f;
47               try{
48                baseNodes = browser.createVrmlFromString(
49                         sphereDesc1 + posX + sphereDesc2 + red + " " +
50                         green + " " +
51                         blue + sphereDesc3);
52                   if(null != baseNodes) {
53                       addSphere.setValue(baseNodes);
54                   }
55               } catch (Exception ex) {
56                   ex.printStackTrace() ;
57               }
58           }
59       }
60   }

The key to this example is the dynamic creation of the VRML code at lines 21 through 35. These simply create a string describing the Transform and geometry that we wish to add. Lines 37 through 39 then generate a random RGB value for the sphere. Lastly lines 48 through 54 concatenate all of the these strings together and add it to the scene using the createVrmlFromString() call at line 48.

You should note how lines 48 through 54 are enclosed in a try/catch call. This ensures that if the call to the browser fails in any way, you will catch the exception and exit gracefully. You should always enclose any java API calls with try/catch. You will save yourself a lot of hard work tracking down bugs if you follow this simple advice.

Obviously this code could be extended to read VRML from a file, or to create arbitrary VRML based on data read from a database, or event read from over the network. In fact in the book we have an interesting example that uses this technique to create a 3D representation of the local disk that can be interactively browsed. Don't do it yourself If your reaction to all of this is "Wow, looks really interesting, but complicated" then help is at hand. We have just released a free version of our authoring tool, Community Place Conductor, which takes a lot of the grunt work out of authoring scenes and adding Java. It even automatically generates Java templates for you based on your VRML scene.

Java allow VRML scenes to become open ended in their interactive capabilities. The limit is your imagination. In this article we have only touched on the possibilities and shown you a few small examples that show how Java can manipulate the VRML scene either directly, or by dynamically adding to the scene. However, we hope we have shown you enough concrete code examples to allow you to feel comfortable with the idea of going off and writing your own Java. The authoring tool mentioned above will help you do that, but our message to you all is - experiment!

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
Back Contents Next
Have you tried out our new Discussion Area yet?

LinkExchange Member

Need sponsorship or advertiser information? E-mail adrian@aereal.com.

Have an idea for an article? Thought of a way we can improve VRMLSite? E-mail suggest@vrmlsite.com.

Please Tell a Friend about VRMLSite!

© 1997 Aereal, Inc. All rights reserved. VRMLSite is a trademark of Aereal Inc.