This annex of the specification is under discussion. It is not officially accepted as part of the specification. If consensus can be reached, it may be official by the August 4th deadline. Otherwise, it will be in the next revision of the spec ...
This section needs to be divided into two separate appendices: Java External API and JavaScript External API.
This section describes bindings for the external application programming interface to the VRML browser.
VRML 2.0 has a standard mechanism for interfacing to the outside world. Depending on the external environment a large variety of bindings to the VRML world are possible. This document describes bindings for some popular environments. For a description of the external API general operation see the appendix "External Programming Interface".
While the actual syntax of the API is language dependent, the mapping of nodes, fields within those nodes and methods on the browser itself are described in the appendix "External Programming Interface Reference".
Java provides its own environment. The VRML world appears as an instance of the vrml java class. This instance can be created by the Java applet or, in the case of Java-within-Netscape, it can be accessed through the document interface. In either case a pointer to the world allows access to the "External Programming Interface Reference - Browser API" and to the fields of specified nodes.
The Browser API has parameter and return types shown as VRML field types. In Java these are classes as described in the appendix "Java Scripting Reference".
The JavaScript language allows an author to write small scripts directly on an HTML page. JavaScript can interface to a plug in using a technology known as LiveConnect. The plug-in presents its interface as a Java object, and since JavaScript can interface with Java objects it can interface directly with the plug-in. Therefore the interface is identical to that for Java - the Browser API is used to get a pointer to a node (which is an instance of a Java SFNode class), then the fields of that node can be accessed as described in the External API document.
Here is an example of interfacing to a VRML world from JavaScript. A VRML world is embedded on an HTML page with the EMBED tag. This tag allows a name to be assigned to the VRML world. This name is contained in the document object and allows access to the Browser API of the VRML world. The Browse API can be used to get a pointer to a named node which can be used to send and receive events. For instance, take this VRML world:
#VRML V2.0 ... DEF Camera Viewpoint { } DEF MySphere Transform { children [ Shape { appearance Appearance { material Material { diffuseColor 0 0 1 } } geometry Sphere { } } ] }
If this world is embedded in an HTML page with the name "vrml" a JavaScript author can translate the Sphere by a relative amount by using the following JavaScript:
function myTranslator(x, y, z) { node = document.vrml.getNode("MySphere"); translate = node.get_translation(); translate[0] += x; translate[1] += y; translate[2] += z; node.set_translation(translate); }
To change the authored position of the camera in the above scene use the following JavaScript:
function myCameraMover(x, y, z) { node = document.vrml.getNode("Camera"); translate = node.get_position(); translate[0] += x; translate[1] += y; translate[2] += z; node.set_position(translate); }
Handling an eventOut from JavaScript requires a bit more work. First, a callback and id must be registered with the eventOut. The callback is a language specific value. In JavaScript this is a function pointer which will get called when the eventOut occurs. It will be called with 3 parameters:
ISSUE: In discussions with Paper we discussed having the JavaScript callback be a string of JavaScript to be executed when the event comes in. But there is no way to pass the value or timestamp of the event. So I have shown the callback as a JavaScript function pointer to which 3 parameters are passed. This may not be possible in JavaScript in which case another solution will have to be found.
For instance, given this world:
#VRML V2.0 ... Shape { ... geometry for a cup ... } DEF Cup TouchSensor { }
and this JavaScript:
function handleEventOut(value, timestamp, id) { ... handle value event ... } function mySetup() { node = document.vrml.getNode("Cup"); node = isActive(handleEventOut, 0); }
When the mySetup() function is called the handler is set up. Now when the user presses the mouse button over the cup in the VRML world the function handleEventOut(true, <time>, 0) is called. When the user releases the mouse handleEventOut(false, <time>, 0) is called.
Interacting with the scene using the above interface allows simple manipulation of specific nodes. For more complex interaction JavaScript can send events to a Script node in the VRML scene which performs complex scene control. Here is a simple JavaScript function which starts an interpolated animation in the VRML scene:
function myStartAnimation() { interface = document.vrml.getNode("Interface"); interface.start(true); }
The VRML scene looks like this:
... DEF Interface Script { eventIn SFBool start eventOut SFTime startTime url "vrmlscript: function start(value, timestamp) { startTime = timestamp; }" } DEF PI PositionInterpolator { keys [ ... keys ... ] values [ ... position values ... ] } DEF TS TimeSensor { cycleInterval 5 } # 5 second animation DEF T Transform { children [ ... geometry to animate ... ] } ROUTE Interface.startTime TO TS.startTime ROUTE TS.fraction TO PI.set_fraction ROUTE PI.outValue TO T.translation ...
The Script can then send multiple messages to start combinations of operations.
With this interface a VRML scene can be completely controlled from external Javascript functions or the external controls can simply stimulate the VRML scene to do more complex functions internally.
The Browser API can do many other operations on the VRML world. For example, constructing the first scene above could be done entirely in JavaScript using the Browser API:
function mySceneBuilder { with (document.vrml) { // create the 2 root nodes scene[0] = createVRMLFromString("DEF Camera Viewpoint { }"); scene[1] = createVRMLFromString("DEF MySphere Transform { }"); // create the shape and its children shape = createVRMLFromString("Shape { }"); appearance = createVRMLFromString("Appearance { }"); shape.set_appearance(appearance); geometry = createVRMLFromString("Sphere { }"); shape.set_geometry(geometry); // add a material to the appearance material = createVRMLFromURL("Material { }"); color[0] = 0; color[1] = 0; color[2] = 1; material.set_diffuseColor(color); appearance.set_material(material); // add the shape to the Transform scene[1].add_children(shape); // make this the vrml scene replaceWorld(scene); } }
Now the scene can be controlled as in the early examples.