Contributors

Using Java with VRML Script Nodes and the EAI


When it comes to 3D graphics on the Internet, using VRML is a good idea. VRML 2.0 (Virtual Reality Modeling Language) is a portable human-readable extensible scriptable procedural 3D graphics language. Being portable means that VRML content you create will run on any VRML 2.0 compliant browser. VRML is extensible in that you can create your own node types through prototyping for reuse. It is procedural in that one doesn't have to explicitly state each 3D pixel (voxel) that is to be viewed, but rather higher level constructs define the image data. Being scriptable means that VRML applications can have logic embedded with a scripting or other programming language.

In regards to Java, VRML has several defined ways to use Java to interact with and drive VRML events. One method is it's use as a scripting language for VRML, and we shall see that Java can drive scripts, animations, and behavior logic in the VRML applications. The other use of Java with VRML is the External Authoring Interface, which allows one to control the VRML browser and it's content from a Java applet on the same page as the embedded VRML model. In this article I will discuss the use of Java in these different ways, and how Java can help bring your 3D worlds to life.

There are three basic tools you will need to get started programming with VRML and Java. A text editor is necessary to edit the VRML files. A Java compiler is necessary to make the Java classes. A VRML browser is necessary to get an implementation of the VRML/Java classes and view your worlds.

The Standard

VRML was created in 1995 as the result of a few idealists and a mailing list hosted at Wired magazine. This group rapidly developed a prototype for a 3D graphics language, and VRML was born. Within months, as people grew to the limits of VRML 1.0 (no animations or scripts) the need for a full featured portable 3D graphics language was found. The www-vrml list entertained proposals for the next generation of 3D content, and Silicon Graphic's Moving Worlds proposal based on their Open Inventor toolkit was chosen. Since then, the standard has rapidly grown in use, sophistication, and popularity, and is now an ISO/IEC international standard, 14772.

One of the best things about it is that it is open to Java. The creators of the VRML standard rightly saw Java's utility as a secure full featured Internet based programming language. Early Script node proposals were received from Sony and Dimension X, and Sony's was chosen and expanded to become the scripting language to provide high level power to VRML. A JavaScript Script node appendix that parallels the features of the Java Script node appendix was also created, and JavaScript is another way to bring the power of logic to VRML worlds. An alternate method of using VRML with Java, the External Authoring Interface, was later created by Chris Marrin of SGI, based not on Script nodes but browser plugin architecture.

In terms of using Java and standards, there is a difference between Java used in the script nodes (internally) and Java used through an applet (externally). The internal Java Script nodes, commonly referred to as the JSAI for Java Scripting Authoring Interface, are defined in Annex B of the VRML specification as a normative annex. This means that any VRML 2.0 compliant browser with Java support will comply to Annex B in Script nodes. The External Authoring Interface, on the other hand, is at this point an informative annex, and browsers can choose whether or not to implement this functionality. Currently, it is available in the two major browsers.

The other difference between the two methods of using Java with VRML is the functional difference. VRML Script nodes have a defined set of event and field parameters that are part of the node definition. For the most part, they are limited to using the events and fields that are defined as part of the Script node. The External Authoring Interface offers more flexibility in some areas.

A few words about the terms and fonts used in this article and VRML in general: VRML nodes are concatenated capitalized words (ie TimeSensor). VRML keywords are completely capitalized (DEF, PROTO). VRML Field names are generally concatenated capitalized words with the first letter lower case (diffuseColor), although there are also field names with other naming structure.

Enter 3D

Before we start delving into the guts and prerequisites of using Java in the Script nodes, let's take a quick moment to overview the basics of VRML.

The File

Every VRML file starts with a header to show it's file type upon reading. For VRML 2.0 this is the standard header:
#VRML V2.0 utf8
The header consists of the octothorp, the world "VRML", a space, the letter "V" and the version, a space, and the content encoding type (in this case, utf8, variable one-or-two byte length superset of ASCII, ISO 646). Any other line starting with the octothorp (#) is a comment and that line will not be read as part of the VRML file.

The standard extension for a VRML file is .wrl, the MIME type is model/vrml. Setting up a server to deliver VRML worlds requires setting this MIME type so the client can recognize the content type as a VRML model. For more information about getting started with this, see the comp.lang.vrml Frequently Asked Questions (FAQ):

http://vrmlworks.crispen.org/

This site has much beneficial information to help you get started with VRML. If you are new to VRML, go there now.

Coordinates

The coordinate axes of a VRML universe are traditional right-hand rule coordinates in the X, Y, and Z dimensions. Coordinate units are defined as meters.

Nodes

The procedural elements of this 3D language are called nodes. There are nodes for geometry (Sphere, Box, Cylinder) appearance and material attributes (Appearance, Material, Texture), location and grouping nodes (Transform, Group), sounds and lights (PointLight, DirectionalLight ), interpolators(PositionInterpolator, ColorInterpolator), sensors (TouchSensor, TimeSensor) and more. This is not a complete list of the nodes, but only some examples of each type. For a complete list of the nodes in the VRML language, see the VRML97 specification Part1: 6. Node Reference. http://www.vrml.org/Specification/VRML97/part1/nodesRef.html. Through the use of these nodes, most conventional 3D graphics functions are available.

Fields

The nodes' characteristics are stored in fields. These fields contain a variety of data about rendering an object in 3D and connecting it to other 3D objects. For example, an ImageTexture node has a URL field that points to a 2D image file. A cone has fields for it's height and base circumference. Fields are immutable, that is they can not be changed from their initial definition. Fields that can be changed are termed exposeFields. There is more about exposedFields below.

Field Types

There are a variety of field types to represent different kinds of information. In VRML there are these field types: Each field contains either a single value or multiple values. If the field is a single value, it is an SF type field. If a field has multiple values, it is an MF type field. SFFields contain one instance of the field type's data. MFFields contain zero or more instances of the field type's data. So that leads to these field types in VRML:

SFBool
SFColorMFColor
SFFloatMFFloat
SFImage
SFInt32MFInt32
SFNodeMFNode
SFStringMFString
SFTimeMFTime
SFVec2fMFVec2f
SFVec3fMFVec3f


Notice that there are no MFBool or MFImage events, these field types are only used as single values. MFTime events were added recently.

Events

Nodes transfer information through the use of events. Each node has a different set of these event sender or receptor capabilities. Node event receptors (eventIns) receive field data. Node event senders (eventOuts) transmit field data. Each event has a field type associated with it, and an event will only generate events of this type and accept events of this type. To connect events between nodes, the ROUTE keyword is used to explicitly connect these nodes. When a Sensor node (for example) that is attached to an Interpolator node generates an event the connected Interpolator eventIn will be set to the value of the field data.

ExposedFields

ExposedFields are similar to fields. They can be visualized as a field with associated accessor (changed) and mutator (set) events. Using these associated events, field data can be changed. For example, a Transform node has an exposedField translation, which is the 3D coordinate SFVec3f of the transform's location. As it is an exposedField, a Transform node has these implicit events set_translation and translation_changed, with the same field type as the exposedField, translation.

Setting the Location

To place VRML elements in the world, the most basic node is the Transform. The transform has no geometry or visible appearance itself, but acts as a container to place, orient, and scale child geometry. It has a children exposedField, and an addChildren eventIn, as other grouping nodes.

Things and Things

There are other Grouping nodes which follow parallel semantics to the Transform node. These include Anchor, Group, Transform, and others.

Geometry

There are a variety of predefined Geometry nodes that for the most part encompass any use of geometry that would be needed. These included Geometry nodes include Sphere, Box, Cylinder, Cone, Extrusion, IndexedFaceSet, IndexedLineSet, and others.

Sensors

Sensors are nodes that can respond to user input and location in the VRML world. For example, a TouchSensor node emits events when it's associated geometry is clicked by the user with the mouse.

Interpolators

An Interpolator is node that returns a varying value based on input values. Each interpolator has a key field and a keyValue field. Whenever the interpolator receives an event that matches a key, it's corresponding keyValue is sent as an event. This enables animation in VRML such as keyframe animations. There are interpolators designed for position, orientation, color, and others.

Using DEF

The DEF keyword is used to mark a specific node as reusable in the VRML world. By using the DEF keyword in a node definition, this node is exposed for later use in the VRML file. It's companion keyword, USE, places a reference to this DEF'd node where it is used. For example:
	DEF GROUP1 Group{}
	USE GROUP1
would create two instances of the Group node.

The DEF keyword is also used with Scripts, as it serves as a pointer to a node for a Script's Node fields. Read more about this in the sections about directOutput and Scripts below.

PROTOtyping

Using PROTOs enables you to make new node types for use in the VRML world. Once a PROTO is defined either within the file or with the EXTERNPROTO definition, that node name may be used as the PROTOs base node type would be used throughout the world.


Scripting

Scripting is the process of adding behavioral logic above what is already provided by the event model, interpolators, and sensors.

Script Node

The Script node allows you to create node behaviors through a programming language like Java or Java/ECMAScript. The Java bindings are described in Annex B and the JavaScript bindings in Annex C. They share many of the same functions in relation to VRML use. One way to distinguish between the two is that JavaScript is generally referred to as the lightweight scripting language (no compilation necessary) while Java is the heavyweight scripting language (threading, etcetera).

For more information about Java, see the Documentation at http://java.sun.com/docs. If you are new to Java go to The Java Tutorial at http://java.sun.com/docs/books/tutorial/index.html.

The Script node is like other nodes in it's use in the VRML file. A Script node is not bound to any part of the scene graph, so can be placed at any point within the VRML file. It has three specified fields, url, directOutput, and mustEvaluate. Of these, the only one normally required is the url field, the other fields default to false.

URL

For Java scripts, the url field is the URL of the class file for the Script. Another way to represent Java class in VRML file is with javabc tag and byte code in base 64 although this is rarely used.

	Script{
		url "VRMLScript.class"
	}
The URL can be a relative or an absolute URL.
	Script{
		url "http://www.tiki-lounge.com/vrml/VRMLScript.class"
	}
Some browsers support inline base-64 encoded byte code of a Java class (notably Liquid Reality's Dimension X browser):
	Script{
		url "javabc:AAAAfss4sGNIssgssG4324tGg4VEFgEgK2FDC...=="
	}
Using JavaScript (ECMAScript is now the official VRML lightweight scripting language), a similar URL is used
	Script{
		url "vrmlscript.js"
	}
or the inline code itself:
	Script{
		url "javascript:  function initialize() ...."
	}
Multiple values can be used in the url field. In that way, if the browser can not support one of the field types, it will go to the next until it finds a supported format or all field values are exhausted.
	Script{
		url [ "vrmlscript.js" , "VRMLScript.class" ]
	}
Besides the URL field there can also be a number of fields, eventIns, and eventOuts associated with the Script node. The VRML specification mandates support for up to 25 each of fields, eventIns, and eventOuts. The fields contain VRML field type data that can be used by the Script. The eventIns and eventOuts are also connected to the Script node process.
	Script{
		field SFBool on
		eventIn SFVec3f set_coord
		eventOut SFColor color_changed
		url "ProximityAlert.class"
	}
So when using the Script node in a VRML file, it's events are connected just like any other node, using the ROUTE and TO keywords and the event's DEF identifiers.

The other two fields of a Script node are directOutput and mustEvaluate. Both of these default to false and generally are not needed.

The directOutput Field

The directOutput field of the Script node is used to enhance browser browser performance. Essentially, if the directOutput field is false (the default) then the Script is assumed to have no direct access to other nodes in the scene graph as SFNode fields of the Script node. When this is so, the browser can make some optimization in event structure based on the fact that no events will be be sent "out of the loop", as it were. When an SFNode or MFNode field of a Script node is to be accessed directly, then the directOutput field should be set to true. This degrades browser event model optimization, but opens up the power and capability of applying events directly to the nodes that are fields of the Script node.

The field values that are used are the USE keyword followed by a DEF'd name of another node within the file. For example:
	DEF GROUP1 Group{}
	Script{
		field SFNode USE GROUP1
		url "Script.class"
		directOutput TRUE
	}
The mustEvaluate Field

The mustEvaluate field is generally used when the Script's initialize method might not otherwise be called. There is some flexibility within the specification as to when the initialize method of a Script is called. Generally, the initialize method of a Script node is called automatically when the Script node is loaded into the scene graph, but this is not necessarily the case. Other implementations might only call the initialize method after some event is received by a Script's eventIn. In the case that a Script's initialize method should be called absolutely, set the mustEvaluate field to true.

Script Methods

VRML Scripts (both Java and JavaScript) have these methods in common. For our purposes we will deal specifically with Java classes and methods.

initialize() - This function is called before the Script node does anything else. It is called on each Script node as it is loaded.

processEvent() - This function of the Script is called every time one of the defined eventIns of the Script is received. The arguments of this function is the received event.

getField(String field) - This method return a ConstField containing the value of the Script node's field value.

getEventOut(String eventOut) - This method returns a field representing the eventOut named in the method argument.

getEventIn(String eventIn) - This method returns a field

processEvents(int count, Event[] events)

eventsProcessed()

shutdown() - This method is called when the Script node is no longer in the scene graph. This could be because the browser is closing or the Script node is culled from scene graph.

More detailed information about the context and functionality of these can be found from the specifications conceots guide about scripts.

http://www.vrml.org/Specifications/VRML97/part1/concepts.html#4.12

The next step, after determining what events are necessary for your Script to function, is to write the actual body of the Script class itself. From this point, programming practices are Java-based.

Package Overview

There are these packages defined within Annex B that are used in Script nodes:
	vrml
	vrml.field
	vrml.node
The vrml package has these classes, Field, ConstField, MField, ConstMField, Browser, Event, and BaseNode. The vrml.field package has classes to represent each of the VRML field types. The vrml.node pacakge has the classes of the Script node itself and also the node class, both of which extend BaseNode.

picture of vrml java base classes

The field classes represent each of the data types that VRML uses. There are fields for position and rotation data, time, nodes, and other fields.

For each type of VRML field, there is a Java field class and a Java constant field class.

picture of vrml java field classes

ConstField classes are used as the values of objects whose can only be read, not written, that is, they are read-only. They have get() methods to get the value of the field's data. Regular fields have methods to get and set the values of the field.

picture of vrml java constfield classes

Writing Script Classes

Script nodes inherit from the class vrml.node.Script. Script itself is an abstract class. The most minimal Java Script node class , a blank implementation, would look like this
	import vrml.node.Script;

	public class TextScript1 extends Script{

	}
When compiled, this fragment could be used as the class file for the url field of a Script node, but it would do nothing. So, our next step is to create a Script node class that will do something when the Script node is initialized, that is first loaded by the browser.
	import vrml.node.Script;

	public class TestScript2 extends Script{

		public void initialize(){
			System.out.println("TextScript2 initialized.");
		}

	}
This Script node, when first loaded by the browser, would print the string "TestScript2 initialized." and a line feed to the browser's Java console.

This is all well and fine, but we need more than a Script node simply running in a vacuum. The next step is to add the ability of the script to respond to actions occuring on the VRML side of the equation. To do this, the Script node is defined with an eventIn. When an event is sent to this eventIn, the Script node can then process this. Thus, we can send a variety of user and time based events to the Script node for further processing.

For example, you might have a Script node driving some animation. When the user can see the area of this process, it should be running. To accomplish this, we can use the VisibilitySensor VRML node to register whether or not an area is viewable, and Script node "glue" to activate.

The VRML code would look like this:
	DEF VS VisibilitySensor{
		size 2 2 2
	}

	DEF S Script{
		eventIn SFTime isVisible
		url "TestVisibilityScript1.class"
	}

	ROUTE VS.enterTime TO S.isVisible
The body of the TestVisibilityScript1 class would implement the processEvent() method of the Script class to handle the eventIn. Assume the process to be triggered is the static method of a class Trigger, Trigger.start(). This might bring up an AWT dialog or some other function.
	import vrml.node.Script;
	import vrml.Event;
	import Trigger;

	public class TestVisibilityScript1 extends Script{

		public void processEvent(Event e){
			Trigger.start();
		}

	}
When the user leaves the viewable area, the process will continue and could become a drag on processing power. In this case, we would want the process to be stopped when the area was not in the user's view. We could accomplish that by watching the exitTime of the Visibility Sensor as well, and when an event was sent to the Script to invoke Trigger.stop().
	DEF VS VisibilitySensor{
		size 2 2 2
	}

	DEF S Script{
		eventIn SFTime isVisible
		eventIn SFTIme isNotVisible
		url "TestVisibilityScript2.class"
	}

	ROUTE VS.enterTime TO S.isVisible
	ROUTE VS.exitTime TO S.isNotVisible
Here is the body of the VRML class. The notable part of it is again in the processEvent() method. In this case, the event's name is determined using the Event.getName() method. If the event's name is "isVisible", then the VisibilitySensor's area has entered the viewable area and Trigger.start() will be called. If the event's name is "isNotVisible", then the user's viewing area no longer includes the VisibilitySensor, and Trigger.stop() will be called.
	import vrml.node.Script;
	import vrml.Event;
	import Trigger;

	public class TestVisibilityScript2 extends Script{

		public void processEvent(Event e){
			if (e.getName().equals("isVisible")) {Trigger.start();}
			else {Trigger.stop();}
		}

	}
Thus when compiled and the VRML file is viewed, when the user's viewable area enters the 2 meter by 2 meter by 2 meter cube centered at origin, the Trigger.start() method will be called. When this area can not be seen, the Trigger.stop() method will be called. These events will start and stop other animations or whatever.

To view this in a broader sense, the key concept of this section is the use of the Script class' processEvent(Event event) method. This method is called by the browser whenever an event is received by a defined eventIn of the Script node.

Now that we have a way to send data to the Script node, the next step is to use the Script node to generate events of it's own. This is done by defining eventOuts of the Script within the Script node definition and then using the vrml.field.setValue() methods to generate eventOuts.
	Script{
		eventOut SFTime startTime
		url "TestScript3.class"
	}

	import vrml.node.Script;
	import vrml.field.SFTime;

	public class TestScript3 extends Script{

		SFTime starttime;

		public void initialize(
			starttime=(SFTime)getEventOut("startTime");
			starttime.setValue( (double) (System.currentTimeMillis()/1000) );
		}
	}
TestScript3, when it is initialized, will send the startTime event based on the system time. You might have to set mustEvaluate to TRUE, depending on your browser.

Browser Script Interface

Besides working within the event model, Script nodes can also access certain methods of the VRML browser itself. The vrml.Browser class has these public methods:

String getName() - This method returns vendor specific VRML browser name.

String getVersion() - This method returns vendor specific VRML browser version.

float getCurrentSpeed() - This method returns the navigation speed in meters per second.

float getCurrentFrameRate() - This method returns the current frame rate in frames per second.

String getWorldURL() - This method returns a string representation of the URL that is currently loaded.

void replaceWorld(BaseNode[] nodes) - This method replaces the current scene graph with the BaseNode[] argument.

void loadURL(String[] url, String[] parameter) - This method loads a URL into a given frame target.

void setDescription(String description) -This method sets the description of the VRML browser like the WorldInfo node.

BaseNode[] createVRMLFromString(String vrmlsyntax) - This method creates an array of BaseNodes from a given string that is legal VRML syntax.

void createVrmlFromURL(String[] url, BaseNode node, String event) - This method loads VRML nodes from the given URL series, and sends them as an event to the given BaseNode's eventIn event.

void addRoute(BaseNode fromNode, String fromEventOut, BaseNode toNode, String toEventIn) - This method adds an event route from the given fromNode's fromEventOut to the given toNode's toEventIn.

void deleteRoute(BaseNode fromNode, String fromEventOut, BaseNode toNode,String toEventIn) - This method deletes a given event route between the given fromNode's fromEventOut to the given toNode's toEventIn.

Read more about the Browser script interface from the specification.

http://www.vrml.org/Specification/VRML97/part1/concepts.html#4.12.10

The browser script interface is useful for a variety of things. For example, loading secondary data into another frame opens up many capabilities for multimedia presentations.

AWT Window

The next thing you might want to do is to make an AWT window from Java world. One way to do this is to make a class that extends java.awt.Frame. One thing about this is that to receive events back on the Script from the Frame, the Script must implement a callback interface.
	import java.awt.Frame;
	import FrameCallback;

	public class ScriptFrame extends Frame{

		protected FrameCallback script;

		public ScriptFrame(FrameCallback script, String title){
			super(title);
			script=script;
		}
	}
The FrameScriptCallback interface has a method to callback the Script node that implements it.
	import ScriptFrame;

	public abstract interface FrameCallback{

		public abstract callback(ScriptFrame scriptframe);

		}
	}
Then, the script node can make a copy of the frame for two dimensional user interface.
	import vrml.node.Script;

	public class FrameScript extends Script implements FrameCallback{

		protected ScriptFrame scriptframe;

		public void initialize(){
			scriptframe=new ScriptFrame(this, "Window Title");
			scriptframe.resize(200, 200);
			scriptframe.show();
		}

		public void callback(ScriptFrame scriptframe){

		}
	}
That way, when events are received on the Frame you can notify the Script. By overriding handleEvent of the ScriptFrame object, the ScriptFrame call call this method:
script.callback(this);
Then by overriding the callback method of the Script method of the Script or whatever other object that implements the FrameCallback interface and then the events can be transferred to VRML events through the Script node.

There are other ways to do this. Using the JDK 1.1, you can have the same functionality in one class. You way to do this is to have the Script node implement ActionListner, thus the Frame's actions go directly to the Script.
	import vrml.node.Script;
	import java.awt.Frame;
	import java.awt.Button;
	import java.awt.event.ActionListener;
	import java.awt.event.ActionEvent;

	public class FrameScript2 implements ActionListener{

		Frame frame;
		Button button;

		public FrameScript2(){

			frame=new Frame();
			button=new Button("Click!");
			frame.add("Center", button);
			frame.setSize(200, 200);
			button.addActionListener(this);
		}

		public void initialize(){

			frame.show();

		}

		public void actionPerformed(ActionEvent actionevent){

			// button was clicked

		}
	}
By adding VRML events and AWT events, you can synchronously exchange data between the 2D and 3D environments.
External Authoring Interface

The other way to use Java with VRML is through the use of the External Authoring Interface, the acronym of which is EAI. The EAI connects an applet on the same page to a VRML plugin using browser specific plugin activation framework. The most commonly used implementation of this is with the Netscape browser and it's LiveConnect plugin framework. Essentially, the applet has access to any node in the VRML plugin that has been defined with the keyword DEF. Java from the applet can affect any such node, sending it any event that matches event field type or reading any of it's fields' or eventOuts' values.

Here is the address of the VRML 2.0 External Authoring Interface (EAI) FAQ, which offers some startup information about the EAI:

http://www.tomco.net/~raf/faqs/eaifaq.html

This site leads to more resources about the EAI.

EAI Packages Overview

These packages are used with the EAI:
	vrml.external
	vrml.external.field
	vrml.external.exception
Getting Started

The most basic object used with the EAI is the vrml.external.Browser object. It has many methods that are similar to the Browser script interface. Also, it has the method that is crucial to the EAI, getNode().

public vrml.external.Node getNode(String nodeName) - This method returns a node that has been defined with the given name.

To get an instance of the vrml.external.Browser object, the static method Browser.getBrowser(Applet applet) is used. Once a Browser object has been instanciated, the next step is to use the Browser.getNode() method.


Still to come: Browsers that support Java Script nodes
Cosmo Software Cosmo Player
Intervista WorldView
Newfire Torch
Sony Community Place
Here are some things to do if you get bored reading this.

1: Become familiar with the VRML specification. It's a good idea to read through the entire thing at least once, it doesn't have to all be at one time. By learning how everything fits together from the ground up, you will gain solid knowledge.

2: Find a modeler. There are a multitude of 3D modelers with VRML support on the market. While it is true that any VRML construct can be made with nothing more than a text editor, these visual tools bring much power to create more sophisticated geometries. Some modelers have specific VRML support or VRML animation capabilities. If you are already familiar with the use of a 3D modeler such as Rhino3D or 3DS MAX these tools offer VRML export including animation in some cases. Native VRML tools are available from Paragraph, Cosmo Software, Caligari, Sony, and Platinum. Recommended for Script node development are Sony Conductor and Cosmo Worlds. I use a text editor and JDK 1.1.5.

3: Read some books. There aren't so many about VRML in particular, but there are a lot about 3D graphics. For VRML, a recomended book is Warnecke and Hartman's VRML Handbook. This book has a complete node reference and great optimization tips. Some books I have not read but which are about VRML and Java include Roehl, Couch, et al's Late Night VRML with Java and Lea, Matsuda, et als VRML Java book.

4: Surf and Research. Look for VRML worlds that use Java on the internet. A couple good places to start are vrml.sgi.com and www.sdsc.edu/vrml.

5: Post your questions and ideas to news:comp.lang.vrml.

6: Take a break. Leave your computer. Think of the essence of 3D. Relate your environs to a huge 3D simulation, and imagine what stuff and behavior would be necessary to recreate it. Take a nap.


This is still being worked on when it's done it will be done. Please note that this is not a definitive reference at this time until it is all verified and done. The information in this document is based on VRML97, ISO/IEC 14772, http://www.vrml.org/Specifications/VRML97.

Finlayson Consulting VRML Group / raf@tomco.net


Copyright 1998 Finlayson Consulting All rights reserved.