Note: This proposal deals only with language based interfaces, although a variety of other interfaces such as network based or IPC based are also possible. Furthermore, the only practical example of communication between an external environment and a VRML world shown to date is the interface between a Java applet on an HTML page and an embedded VRML world on that same page. Therefore this proposal deals only with that style of interface.
The External Authoring Interface allows 4 types of access into the VRML scene:
There are 2 conceptual differences between the External Authoring Interface and the Script Authoring Interface. The first has to do with obtaining a node pointer through which its eventIns and eventOuts can be accessed. When creating a VRML file a Script node (and therefore the script it contains) can get a pointer to a node with the USE construct (see Instancing). Since the applet has no implicit access to this instancing mechanism an explicit method is provided to get the node pointer from its DEF name string. The 4th access type (eventOut notification) is also conceptually different since creating a ROUTE is not possible between the VRML scene and the applet. The applet must create a method to be called when the eventOut occurs. This method is registered with an eventOut of a given node. When the eventOut generates an event the registered method is called.
Once the node pointer is obtained, methods for that node are provided to get pointers to the eventIns and eventOuts of that node. Furthermore, once these pointers are obtained interfaces are provided to access them. There is an interface on the eventIn to send it an event, and on the eventOut to get its current value and to register a function to be called when it generates an event.
Note: As of the writing of this proposal the WWW consortium was still in the process of finalizing both embedded objects and applets on an HTML page. Therefore any discussion of the details of the communication between the two (e.g. - how Java gets a pointer to the Browser instance) is subject to change. The concepts outlined here were tested using Netscape's LiveConnect interface so examples will be given using the same assumptions as that mechanism. LiveConnect accesses the Browser instance using a proprietary netscape package and the Browser class must be a subclass of Plugin (also proprietary to Netscape), so that interface may change in the future as the HTML standard progresses. Fortunately, once the Browser instance is gotten the remainder of the interface is independent of the implementation.A Java applet communicates with a VRML world by first obtaining an instance of the Browser class. This class is the Java encapsulation of the VRML world. It contains the entire Browser Script Interface as well as the getNode() method, which returns a Node when given a DEF name string. Only DEF names in the base file are accessible. Names in Inline files and those created with createVRMLFromString() or createVRMLFromURL() are not accessible. Since VRML files can have multiple occurances of the same DEF name only the node with the last occurance of a given name is accessible.
Here is an example of sending an eventIn to a VRML scene containing this node:
DEF Mover Transform { ... }Here is the Java code to send it an event to change its translation field (assume browser is the instance of a Browser class gotten from a previous call):
Node mover = browser.getNode("Mover"); EventInSFVec3f translation = (EventInSFVec3f) mover.getEventIn("set_translation"); float value[3] = new float[3]; value[0] = 5; value[1] = 0; value[2] = 1; translation.setValue(value);In the above example, the translation value (5, 0, 1) is sent to the translation field of the Transform node.
Using the eventIn example above, the current value of the translation field can be read like this:
float current[] = ((EventOutSFVec3f) (mover.getEventOut("translation_changed"))).getValue();The array current now contains 3 floats with the current translation value.
Using the above example again, the applet can get notified when the translation field of the Transform is changed like this:
public class MyObserver implements EventOutObserver { public void callback(EventOut value, double timeStamp, Object data) { // cast value into an EventOutSFVec3f and use it } } ... MyObserver observer = new MyObserver; mover.getEventOut("translation_changed").advise(observer, null);When the eventOut from translation occurs, observer.callback() is executed.
vrml.external | +- vrml.external.Browser +- vrml.external.Node +- vrml.external.field | +- vrml.external.field.EventIn | | +- vrml.external.field.EventInMFColor | | +- vrml.external.field.EventInMFFloat | | +- vrml.external.field.EventInMFInt32 | | +- vrml.external.field.EventInMFNode | | +- vrml.external.field.EventInMFRotation | | +- vrml.external.field.EventInMFString | | +- vrml.external.field.EventInMFVec2f | | +- vrml.external.field.EventInMFVec3f | | +- vrml.external.field.EventInSFBool | | +- vrml.external.field.EventInSFColor | | +- vrml.external.field.EventInSFFloat | | +- vrml.external.field.EventInSFImage | | +- vrml.external.field.EventInSFInt32 | | +- vrml.external.field.EventInSFNode | | +- vrml.external.field.EventInSFRotation | | +- vrml.external.field.EventInSFString | | +- vrml.external.field.EventInSFTime | | +- vrml.external.field.EventInSFVec2f | | +- vrml.external.field.EventInSFVec3f | | | +- vrml.external.field.EventOut | | +- vrml.external.field.EventOutMField | | | +- vrml.external.field.EventOutMFColor | | | +- vrml.external.field.EventOutMFFloat | | | +- vrml.external.field.EventOutMFInt32 | | | +- vrml.external.field.EventOutMFNode | | | +- vrml.external.field.EventOutMFRotation | | | +- vrml.external.field.EventOutMFString | | | +- vrml.external.field.EventOutMFVec2f | | | +- vrml.external.field.EventOutMFVec3f | | | | | +- vrml.external.field.EventOutSFBool | | +- vrml.external.field.EventOutSFColor | | +- vrml.external.field.EventOutSFFloat | | +- vrml.external.field.EventOutSFImage | | +- vrml.external.field.EventOutSFInt32 | | +- vrml.external.field.EventOutSFNode | | +- vrml.external.field.EventOutSFRotation | | +- vrml.external.field.EventOutSFString | | +- vrml.external.field.EventOutSFTime | | +- vrml.external.field.EventOutSFVec2f | | +- vrml.external.field.EventOutSFVec3f | | | +- vrml.external.field.EventOutObserver | +- vrml.external.field.FieldTypes | +- vrml.external.exception +- vrml.external.exception.InvalidEventInException +- vrml.external.exception.InvalidEventOutException +- vrml.external.exception.InvalidNodeException +- vrml.external.exception.InvalidRouteException +- vrml.external.exception.InvalidVrmlException
// Specification of the External Interface for a VRML browser. public class Browser { // Get the "name" and "version" of the VRML browser (browser-specific) public String getName(); public String getVersion(); // Get the current velocity of the bound viewpoint in meters/sec, // if available, or 0.0 if not public float getCurrentSpeed(); // Get the current frame rate of the browser, or 0.0 if not available public float getCurrentFrameRate(); // Get the URL for the root of the current world, or an empty string // if not available public String getWorldURL(); // Replace the current world with the passed array of nodes public void replaceWorld(Node[] nodes) throws IllegalArgumentException; // Load the given URL with the passed parameters (as described // in the Anchor node) public void loadURL(String[] url, String[] parameter); // Set the description of the current world in a browser-specific // manner. To clear the description, pass an empty string as argument public void setDescription(String description); // Parse STRING into a VRML scene and return the list of root // nodes for the resulting scene public Node[] createVrmlFromString(String vrmlSyntax) throws InvalidVrmlException; // Tells the browser to load a VRML scene from the passed URL or // URLs. After the scene is loaded, an event is sent to the MFNode // eventIn in node NODE named by the EVENT argument public void createVrmlFromURL(String[] url, Node node, String event); // Get a DEFed node by name. Nodes given names in the root scene // graph must be made available to this method. DEFed nodes in inlines, // as well as DEFed nodes returned from createVrmlFromString/URL, may // or may not be made available to this method, depending on the // browser's implementation public Node getNode(String name) throws InvalidNodeException; // Add and delete, respectively, a route between the specified eventOut // and eventIn of the given nodes public void addRoute(Node fromNode, String fromEventOut, Node toNode, String toEventIn) throws IllegalArgumentException; public void deleteRoute(Node fromNode, String fromEventOut, Node toNode, String toEventIn) throws IllegalArgumentException; // return an instance of the Browser class // This returns the first embedded plugin in the current frame. static public Browser getBrowser(Applet pApplet); // return an instance of the Browser class // If frameName is NULL, current frame is assumed. static public Browser getBrowser(Applet pApplet, String frameName, int index); }
// Specification of the Java interface to a VRML node. package vrml.external; import vrml.external.field.EventIn; import vrml.external.field.EventOut; import vrml.external.exception.InvalidEventInException; import vrml.external.exception.InvalidEventOutException; public class Node { // Get a string specifying the type of this node. May return the // name of a PROTO, or the class name public String getType(); // Means of getting a handle to an EventIn of this node public EventIn getEventIn(String name) throws InvalidEventInException; // Means of getting a handle to an EventOut of this node public EventOut getEventOut(String name) throws InvalidEventOutException; }
// Specification of the base interface for all eventIn types. public class EventIn { // Get the type of this EventIn (specified in FieldTypes.java) public int getType(); }
public class EventInMFColor extends EventIn { public void setValue(float[][] value) throws IllegalArgumentException; public void set1Value(int index, float[] value) throws IllegalArgumentException; }
public class EventInMFFloat extends EventIn { public void setValue(float[] value) throws IllegalArgumentException; public void set1Value(int index, float value) throws IllegalArgumentException; }
public class EventInMFInt32 extends EventIn { public void setValue(int[] value) throws IllegalArgumentException; public void set1Value(int index, int value) throws IllegalArgumentException; }
public class EventInMFNode extends EventIn { public void setValue(Node[] node) throws IllegalArgumentException; public void set1Value(int index, Node node) throws IllegalArgumentException; }
public class EventInMFRotation extends EventIn { public void setValue(float[][] value) throws IllegalArgumentException; public void set1Value(int index, float[] value) throws IllegalArgumentException; }
public class EventInMFString extends EventIn { public void setValue(String[] value) throws IllegalArgumentException; public void set1Value(int index, String value) throws IllegalArgumentException; }
public class EventInMFVec2f extends EventIn { public void setValue(float[][] value) throws IllegalArgumentException; public void set1Value(int index, float[] value) throws IllegalArgumentException; }
public class EventInMFVec3f extends EventIn { public void setValue(float[][] value) throws IllegalArgumentException; public void set1Value(int index, float[] value) throws IllegalArgumentException; }
public class EventInSFBool extends EventIn { public void setValue(boolean value); }
public class EventInSFColor extends EventIn { public void setValue(float[] value) throws IllegalArgumentException; }
public class EventInSFFloat extends EventIn { public void setValue(float value); }
public class EventInSFImage extends EventIn { public void setValue(int width, int height, int numComponents, byte[] pixels) throws IllegalArgumentException; }
public class EventInSFInt32 extends EventIn { public void setValue(int value); }
public class EventInSFNode extends EventIn { public void setValue(Node value) throws IllegalArgumentException; }
public class EventInSFRotation extends EventIn { public void setValue(float[] value) throws IllegalArgumentException; }
public class EventInSFString extends EventIn { public void setValue(String value); }
public class EventInSFTime extends EventIn { public void setValue(double value); }
public class EventInSFVec2f extends EventIn { public void setValue(float[] value) throws IllegalArgumentException; }
public class EventInSFVec3f extends EventIn { public void setValue(float[] value) throws IllegalArgumentException; }
// Specification of the base interface for all eventOut types. public class EventOut { // Get the type of this EventOut (specified in FieldTypes.java) public int getType(); // Mechanism for setting up an observer for this field. // The EventOutObserver's callback gets called when the // EventOut's value changes. public void advise(EventOutObserver f, Object userData); }
// Interface which all observer classes must implement. public interface EventOutObserver { void callback(EventOut value, double timeStamp, Object userData); }
public class EventOutMField extends EventOut { public int getSize(); }
public class EventOutMFColor extends EventOutMField { public float[][] getValue(); public float[] get1Value(int index); }
public class EventOutMFFloat extends EventOutMField { public float[] getValue(); public float get1Value(int index); }
public class EventOutMFInt32 extends EventOutMField { public int[] getValue(); public int get1Value(int index); }
public class EventOutMFNode extends EventOutMField { public Node[] getValue(); public Node get1Value(int index); }
public class EventOutMFRotation extends EventOutMField { public float[][] getValue(); public float[] get1Value(int index); }
public class EventOutMFString extends EventOutMField { public String[] getValue(); public String get1Value(int index); }
public class EventOutMFVec2f extends EventOutMField { public float[][] getValue(); public float[] get1Value(int index); }
public class EventOutMFVec3f extends EventOutMField { public float[][] getValue(); public float[] get1Value(int index); }
public class EventOutSFBool extends EventOut { public boolean getValue(); }
public class EventOutSFColor extends EventOut { public float[] getValue(); }
public class EventOutSFFloat extends EventOut { public float getValue(); }
public class EventOutSFImage extends EventOut { public int getWidth(); public int getHeight(); public int getNumComponents(); public byte[] getPixels(); }
public class EventOutSFInt32 extends EventOut { public int getValue(); }
public class EventOutSFNode extends EventOut { public Node getValue(); }
public class EventOutSFRotation extends EventOut { public float[] getValue(); }
public class EventOutSFString extends EventOut { public String getValue(); }
public class EventOutSFTime extends EventOut { // Note that this returns a VRML "Time" - the number of seconds since // Jan 1, 1970 GMT, rather than a Java time which is a long, the number // of milliseconds since Jan 1, 1970 GMT public double getValue(); }
public class EventOutSFVec2f extends EventOut { public float[] getValue(); }
public class EventOutSFVec3f extends EventOut { public float[] getValue(); }
// Wrapper class specifying the types of all VRML eventIns/eventOuts. public final class FieldTypes { public final static int UnknownType = 0; public final static int SFBOOL = 1; public final static int SFIMAGE = 2; public final static int SFTIME = 3; public final static int SFCOLOR = 4; public final static int MFCOLOR = 5; public final static int SFFLOAT = 6; public final static int MFFLOAT = 7; public final static int SFINT32 = 8; public final static int MFINT32 = 9; public final static int SFNODE = 10; public final static int MFNODE = 11; public final static int SFROTATION = 12; public final static int MFROTATION = 13; public final static int SFSTRING = 14; public final static int MFSTRING = 15; public final static int SFVEC2F = 16; public final static int MFVEC2F = 17; public final static int SFVEC3F = 18; public final static int MFVEC3F = 19; // This class should never need to be instantiated private FieldTypes() {} }
InvalidEventInException.java public class InvalidEventInException extends RuntimeException { /** * Constructs an InvalidEventInException with no detail message. */ public InvalidEventInException() { super(); } /** * Constructs an InvalidEventInException with the specified detail message. * A detail message is a String that describes this particular exception. * @param s the detail message */ public InvalidEventInException(String s) { super(s); } }
public class InvalidEventOutException extends RuntimeException { public InvalidEventOutException() { super(); } public InvalidEventOutException(String s) { super(s); } }
public class InvalidNodeException extends RuntimeException { public InvalidNodeException() { super(); } public InvalidNodeException(String s) { super(s); } }
public class InvalidRouteException extends RuntimeException { public InvalidRouteException() { super(); } public InvalidRouteException(String s) { super(s); } }
public class InvalidVrmlException extends RuntimeException { public InvalidVrmlException() { super(); } public InvalidVrmlException(String s) { super(s); } }
Test | Description | |
---|---|---|
RGBTest | Illustrates sending/receiving of events, and registering of callbacks. | |
CreateTest | tests createVrmlFromString and replaceWorld methods of the Browser API. | |
AddRemoveTest | verifies operation of addChildren and removeChildren fields. | |
Tiny3D | simple example of a tiny VRML "authoring tool" written in Java. |
This proposal and all examples and source code contained herein are hereby placed in the public domain. No rights are claimed or inferred by the author or Silicon Graphics, Inc.