-->

VRML Beans Proposal

Dmitri Kondratiev
d.i.m.a@bigfoot.com

Created : 2/5/97
Last Modified : 20/3/98
URL of this document : http://www.geocities.com/SiliconValley/Lakes/3767/vrmlbean.htm

Contents

Abstract

In this document I am trying to evaluate the advantages of Java Beans architecture for creating visual 3-D components that can be reused in different applications or applets. The goal is to create reusable VRML components with behavior which properties can be easily manipulated and customized in VRML2.0 builder tools and other applications. VRML Beans is the architecture for these reusable 3D components.

I plan to publish all VRML Beans prototype sources and documentation on the net. Next in this message goes some very preliminary outline of VRML Beans, in fact some ideas I would be very much interested to get any comments from everybody interested in the VRML Beans idea.

Goals that VRML Beans Address

VRML bean is a reusable VRML component that can be used in design/run time Java Beans environments. For the outside world VRML bean looks like a 3-D object with well defined properties and behaviors. VRML bean fully supports all core features that Java Beans architecture provides such as introspection, customization, persistence, event and properties model. Complete support of Java Beans architecture should allow design time environments such as bean builders operate VRML Beans exactly the same way as other beans. This should make construction of different applets and applications with VRML and other beans as building blocks possible in bean builders. In run-time environment VRML bean as a part of some applet or application provides user with visual 3-D interface. When used in bean builders VRML bean may provide its own 3-D GUI to customize its properties visually.

Another approach is to use special VRML bean builder - a sophisticated bean, which allows the user to group some set of "simple" VRML beans together and then connect and customize individual beans in this group. Next VRML Bean builder may generate all necessary "hookup" code and finally save the customized state of the bean group together with bean classes and hookup code in newly-born persistent instance of VRML bean. This new instance can then be used in VRML bean builder tool.

VRML Bean Architecture and Design

VRML bean framework is based on Java Beans - Sun software component model for Java. VRMLBeans framework performs all operations with VRML scene graph using External Authoring Interface, Java Script Authoring Interface and in future by means of new Java/VRML API when it will be available.

VRML bean should be able to provide different levels of VRML scene graph abstraction, from complete scene to separate nodes. From this follows that every VRML node should have corresponding VRMLNodeBean class encapsulating functionality of this node.

VRML Bean Components

VRML bean consists of some or all of the following components :

Packaging

All VRML bean components are contained in JAR file. VRML scene graph is loaded by means of custom URL from JAR file.

Persistent Storage

VRML Bean internal state is defined by property values of VRMLNodeBean classes it includes and by corresponding VRML file if present. To store its state VRML Bean can :

Events and Properties

Events that VRML bean generates will range from very "high level", such as "correct combination to open bank vault is entered", to "low level" as "translation_changed" events. The same consideration is true for VRML Bean properties. No matter what "level of abstraction" property has, it can be accessed with "getter" and/or "setter" methods conforming to Java Beans design patterns for read-only, read/write, or write-only properties :

public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> propertyValue);

So for example, read/write property "dexterity" in Avatar bean encapsulating both 3-D representation of an avatar and its behaviors might be presented by a pair of methods :

public Skill getDexterity();
public void setDexterity(Skill s);

VRMLNodeBean exports its eventIns, eventOuts and fields as distinct Java Beans properties. VRMLNodeBean provides standard Java Beans property accessor methods for the fields of corresponding VRML node

Fields.

In run time.

VRML node fields define the initial values for the node's state. Field values cannot be changed in run time and are considered private. Fields in VRML are designed in such a way to allow browsers perform scene graph optimization in run time. Optimization algorithms may choose to replace some nodes with different geometry where node fields are meaningless and useless, which can be done only when node fields are private and handled internally.

To address this VRML limitation, VRMLNodeBean does not provide any field accessor method in run time.

In design time.

To customize VRMLNodeBean we need full access to fields in design time :

public <FieldType> get<FieldName>();
public void set<FieldName&rt(< FieldType&rt fieldValue); After field values have been changed, new VRML is generated from corresponding VRMLNodeBean object. To see the effect of field customization this new VRML is loaded in the browser bean.

Exposed fields.

public <ExposedFieldType> get<ExposedFieldName>();
public void set<ExposedFieldName>(<ExposedFieldType> fieldValue);

Setting exposed field in VRMLNodeBean automatically sets the value of the corresponding field in VRML node and generates a <ExposedFieldName>_changed eventOut.

EventIns.

VRML EventIns are WRITE-ONLY as they doesn’t store any data and reading them is not possible. Browser uses this as another opportunity for scene graph optimization.

When eventIn "setter" method of some VRMLNodeBean class is called, input event is sent to the eventIn of the corresponding node :

public void set<EventInName>(<EventInType> eventInValue);

EventOuts.

To access current value of the eventOut of some node, "getter" method of the corresponding VRMLNodeBean class should be called :

public <EventOutType> get<EventOutName>();

The "setter" access method for eventOuts is not defined. Thus eventOuts are read-only properties.

For each VRML node VRMLBeans framework provides subclass of VRMLNodeBean class encapsulating the functionality of this node. Thus Sphere has SphereBean class, TimeSensor has TimeSensorBean class, PositionInterpolator has PositionInterpolatorBean class, etc.

For example Transform node has the following TransformBean class :

public class TransformBean extends VRMLNodeBean {

  // * Accessing eventIns *

  // eventIn      MFNode      addChildren
  public void setAddChildren(MFNode eventInValue);

  // eventIn      MFNode      removeChildren
  public void setRemoveChildren(MFNode eventInValue);

  // * Accessing exposedFields *

  //   exposedField SFVec3f     center           0 0 0
  public SFVec3f getCenter();
  public void setCenter(SFVec3f fieldValue);

  //   exposedField MFNode      children         []
  public MFNode getChildren();
  public void setChildren(MFNode fieldValue);

  //   exposedField SFRotation  rotation         0 0 1  0
  public SFRotation getRotation();
  public void setRotation(SFRotation fieldValue);

  //   exposedField SFVec3f     scale            1 1 1
  public SFVec3f getScale();
  public void setScale(SFVec3f fieldValue);

  //   exposedField SFRotation  scaleOrientation 0 0 1  0
  public SFRotation getScaleOrientation();
  public void setScaleOrientation(SFRotation fieldValue);

  //   exposedField SFVec3f     translation      0 0 0
  public SFVec3f getTranslation();
  public void setTranslation(SFVec3f fieldValue);

  // * Accessing fields in design time only *

  //   field        SFVec3f     bboxCenter       0 0 0
  public SFVec3f getBboxCenter(); 
  public void setBboxCenter(SFVec3f fieldValue); 

  //   field        SFVec3f     bboxSize         -1 -1 -1
  public SFVec3f getBboxSize(); 
  public void setBboxSize(SFVec3f fieldValue); 

}

VRMLBeans Framework

VRMLBeans framework provides the following base classes and interfaces.

VRMLBean Interface

VRMLBean is an interface that all VRML beans must implement :

public interface VRMLBean extends java.io.Serializable;

VRMLNodeBean Class

VRMLNodeBean is base class that all VRML node beans must extend. This class implements VRMLBean, EventInChangeListener and EventOutChangeListener interfaces.

public class VRMLNodeBean extends vrml.BaseNode
	implements VRMLBean,  EventInChangeListener, EventOutChangeListener {

  // This method gets called when some eventIn of the VRML node has changed. 
  // Node bean classes may overide this. Default operation : nop
  // Parameters: 
  //        evt - A EventInChangeEvent object describing the eventIn that has  changed. 
  public void eventInChanged(EventInChangeEvent evt);

  // This method gets called when some eventOut of the VRML node has changed. 
  // Node bean classes may overide this. Default operation : nop
  // Parameters: 
  //        evt - A EventOutChangeEvent object describing the eventOut that has  changed. 
  public void eventOutChanged(EventOutChangeEvent evt);

  // Multicast event listener registration methods for PropertyChangeListeners
  public void addPropertyChangeListener(PropertyChangeListener x); 
  public void removePropertyChangeListener(PropertyChangeListener x);

  // May be called to report a bound property update to any registered listeners. 
  // No event is fired if old and new are equal and non-null. 
  // Parameters: 
  //        propertyName - The programmatic name of the property that was changed. 
  //        oldValue - The old value of the property. 
  //        newValue - The new value of the property. 

  public void firePropertyChange(String propertyName,
                                 Object oldValue,
                                 Object newValue)

  // Link this VRMLNodeBean to corresponding VRML node with "DEFed" node name. 
  // Parameters :
  //       nodeName - "DEFed" node name
  public void setNode(String nodeName) throws InvalidVRMLNodeException;

  // Link this VRMLNodeBean to corresponding VRML *group* node with "DEFed" node name. 
  // This method allows create "group" VRMLNodeBean object from custom "children" VRMLNodeBean
  // objects.
  // Parameters :
  //       nodeName - "DEFed" group node name
  //	     children - custom instances of VRML node children beans
  public void setNode(String nodeName, VRMLNodeBean[] children) throws InvalidVRMLNodeException;

  // Set name of the VRML node
  public void setNodeName(String nodeName);

  // Get name of the VRML node
  public String getNodeName();
}
EventInChangeListener interface is used by VRMLBrowserBean to notify VRMLNodeBean that one of the eventIns in the corresponding VRML node has received input event. A EventInChangeEvent object gets delivered whenever a VRMLBrowseBean detects input event sent to some eventIn of this VRML node. A EventInChangeEvent object is sent as an argument to the EventInChangeListener "eventInChanged" method. EventInChangeEvent object provides methods to access the name and the old and new value of the changed eventIn. Null value may be provided for the old value if its true value is not known.
public interface EventInChangeListener {

  // This method gets called when some eventIn of the VRML node has changed. 
  // Parameters: 
  //        evt - A EventInChangeEvent object describing the eventIn that has  changed. 
  public abstract void eventInChanged(EventInChangeEvent evt);
     
}

public class EventInChangeEvent extends java.util.EventObject {

  /**
     Constructor parameters: 
          source - The VRMLBrowserBean that fired the event. 
          eventInName - name of the eventIn in VRML node that has changed. 
          oldValue - The old value of the property. 
          newValue - The new value of the property. 
   */

  public EventInChangeEvent(Object source,
                             String eventInName,
                             Object oldValue,
                             Object newValue);

  public String getEventInName(); // returns the eventIn name of the eventIn that has changed. 
  public Object getNewValue(); // returns the new value for the eventIn
  public Object getOldValue(); // returns the old value for the eventIn

}
Respectively EventOutChangeListener interface is used by VRMLBrowserBean to notify VRMLNodeBean that one of the eventOuts in corresponding VRML node has sent an output event. A EventOutChangeEvent object gets delivered whenever a VRMLBrowseBean detects that output event was sent by some eventOut of this VRML node. A EventOutChangeEvent object is sent as an argument to the EventOutChangeListener "eventOutChanged" method. EventOutChangeEvent object provides methods to access the name and the old and new value of the changed eventOut. Null value may be provided for the old value if its true value is not known.
public interface EventOutChangeListener {

  // This method gets called when some eventOut of the VRML node has changed. 
  // Parameters: 
  //        evt - A EventOutChangeEvent object describing the eventOut that has  changed. 
  public abstract void eventOutChanged(EventOutChangeEvent evt);
     
}

public class EventOutChangeEvent extends java.util.EventObject {

  /**
     Constructor parameters: 
          source - The VRMLBrowserBean that fired the event. 
          eventOutName - name of the eventOut in VRML node that has changed. 
          oldValue - The old value of the property. 
          newValue - The new value of the property. 
   */

  public EventOutChangeEvent(Object source,
                             String eventOutName,
                             Object oldValue,
                             Object newValue);

  public String getEventOutName(); // returns the eventOut name of the eventOut that has changed. 
  public Object getNewValue(); // returns the new value for the eventOut
  public Object getOldValue(); // returns the old value for the eventOut

}
VRMLNodeBean provides a change notification service for some or all of its properties : eventIns, eventOuts and fields. Such properties are known as "bound properties" and allow other components to bind special behavior to property changes. In case when node bean classes want to report a bound property update to any registered listeners they should call VRMLNodeBean "firePropertyChange" method.

The PropertyChangeListener event listener interface is used to report updates to VRMLNodeBean bound properties. VRMLNodeBean supports bound properties by a pair of multicast event listener registration methods for PropertyChangeListeners:

public void addPropertyChangeListener(PropertyChangeListener x);
public void removePropertyChangeListener(PropertyChangeListener x);

VRMLNodeBean fires "PropertyChangeEvent" event to registered listeners, whenever any of its fields, eventIns or eventOuts get changed.

VRMLNodeBean fires "PropertyChangeEvent" event AFTER updating its internal state. Any bean, which may be also "not-VRML bean", can register itself as a PropertyChangeListener with a source VRMLNodeBean to be notified of its property updates. When a property change occurs on a bound property the VRMLNodeBean calls the "PropertyChange-Listener. propertyChange(PropertyChangeEvent)" method on any registered listeners, passing a PropertyChangeEvent that encapsulates the eventIn/eventOut/field name of the property and its old and new values.

Method "setNode(String nodeName)" links this VRMLNodeBean object to corresponding VRML node with the "DEFed" name. There are two different cases when this method is called :

In both cases this method is called only once for each VRMLNodeBean object.

Method "setNode(String nodeName, VRMLNodeBean[] children)" links this VRMLNodeBean object to corresponding VRML group node with the "DEFed" name. This method allows create "group" VRMLNodeBean objects from custom "children" VRMLNodeBean objects. Custom node beans extened their base node bean classes, such as MySensor class extends TouchSensorBean base class. The advantage of creating group beans from customized node beans is that you can override eventIn/eventOut handling methods in your custom bean :

public class MySensor extends TouchSensorBean {

  public MySensor() {
    try {
      setNode("MySensor");
    } catch (InvalidVRMLNodeException e) {}
  }

  public void eventInChanged(EventInChangeEvent evt) {
    System.out.println("MySensor node just received event : "+evt.toString());
  }

  public void eventOutChanged(EventOutChangeEvent evt) {
    System.out.println("MySensor node just sent event : "+evt.toString());
  }
}
Having this custom MySensor node bean defined, you can create, say some group node bean like this :
public class MyGroup extends GroupBean {

  private  transient VRMLNodeBean[] children;

  public MyGroup() {
    children[0] = new MySensor();
    try {
      setNode("MyGroup", children);
    } catch (InvalidVRMLNodeException e) {}
  }

}
MyGroup node bean will be linked with the following scene graph :
#VRML V2.0 utf8

DEF MyGroup Group { 
  children [
     DEF MySensor TouchSensor {}
  ]
}

VRMLBrowserBean Interface

public interface VRMLBrowserBean implements VRMLBean {

  // Creates VRML node in the root scene graph from VRML bean
  public abstaract void createVrmlFromBean(VRMLBean bean) throws InvalidVRMLBeanException;
}
VRMLBrowserBean is the interface that any browser bean must implement. VRMLBrowserBean performs all operations with scene graph using Java External Authoring Interface, Java Script Authoring Interface and in future by means of new Java/VRML API when it will be available.

VRMLBrowserBean "createVrmlFromBean(VRMLBean bean)" method allows create scene graph using introspection of parameter bean. VRMLNodeBean methods are implemented by means of VRMLBrowserBean class.

VRMLBeans Class

This class provides some general purpose VRML beans control methods.
public class VRMLBeans {
  
  public static VRMLBrowserBean getBrowserBean();
}

Example

To understand how VRMLBeans framework may be used let's create node beans for the given below scene graph and play with them.
#VRML V2.0 utf8

DEF XForm Transform { children [

  DEF BoxShape Shape { 

    appearance DEF BoxAppearance Appearance {
        material DEF BoxMaterial Material {
            diffuseColor 1.0 0.0 0.0
        }
    }

    geometry Box {} 
  }

  DEF Clicker TouchSensor {}
  DEF TimeSource TimeSensor { cycleInterval 2.0 } # Run once for 2 sec.
  # Animate one full turn about Y axis:
  DEF Animation OrientationInterpolator {
       key      [ 0,      .33,       .66,        1.0 ]
       keyValue [ 0 1 0 0, 0 1 0 2.1, 0 1 0 4.2, 0 1 0 0 ]
  }
]}
ROUTE Clicker.touchTime TO TimeSource.startTime
ROUTE TimeSource.fraction_changed TO Animation.set_fraction
ROUTE Animation.value_changed TO XForm.rotation
For this scene graph we can create the following below node beans.

Clicker bean just informs about TouchSensor events. TimeSource bean reports time when TimeSensor was started.

AnimationMonitor class is a "not-VRML" bean that listens for any "bound" property change. Animation bean reports changes of its bound property rotation to all registered listeners calling "firePropertyChange" method defined in its VRMLNodeBean base class. New rotation value is the value of "value_changed" eventOut that OrientationInterpolator generates. AnimationMonitor receives new rotation value passed in PropertyChangeEvent event to its "propertyChange" method.

XForm node bean first creates custom Clicker, TimeSource and Animation node beans. After that it links itself to XForm Transform node together with its customized children node beans. Next it creates AnimationMonitor bean and adds it as a bound change listener to animation bean. Finally XForm bean starts animation by setting exposed field "startTime" in TimeSensor node.

public class Clicker extends TouchSensorBean  {

  public Clicker() {
    try {
      setNode("Clicker");
    } catch (InvalidVRMLNodeException e) {}
  }

  public void eventInChanged(EventInChangeEvent evt) {
    System.out.println("Clicker : 'enabled' changed !"+evt.toString());
  }

  public void eventOutChanged(EventOutChangeEvent evt) {
    System.out.println("Clicker : just sent event : "+evt.toString());
  }

}

public class TimeSource extends TimeSensorBean {

  public TimeSource() {
    try {
      setNode("TimeSource");
    } catch (InvalidVRMLNodeException e) {}
  }

  public void eventInChanged(EventInChangeEvent evt) {

    if(evt.getEventInName().equals("set_startTime"))
      System.out.println("TimeSource started at : "+ evt.getNewValue());
  }

}

public class Animation extends OrientationInterpolatorBean {

  private transient SFRotation oldRotation;

  public void eventOutChanged(EventOutChangeEvent evt) {

    if(evt.getEventOutName().equals("value_changed")) {
      SFRotation newRotation = (SFRotation)evt.getNewValue();
      firePropertyChange("value_changed", oldRotation, newRotation);
    }
  }

}

public class AnimationMonitor implements PropertyChangeListener {

  public void propertyChange(PropertyChangeEvent evt) {

    System.out.println("Received changed property from :"+ evt.getSource());
    if(evt.getPropertyName().equals("value_changed")) {
      System.out.println("Rotation changed from : "+ evt.getOldValue()+" to : "+evt.getNewValue());
    }
  }

}

public class XForm extends TransformBean {

  // Create custom node beans

  private  transient VRMLNodeBean[] children;
  private Clicker clicker = new Clicker();
  private TimeSource timeSource = new TimeSource();
  private Animation animation = new Animation();
  private AnimationMonitor monitor = new AnimationMonitor();

  public XForm() {

    children[0] = clicker;
    children[1] = timeSource;
    children[2] = animation;

   // link XForm bean to Transform node with DEFed name XForm
    try {
      setNode("XForm", children);
    } catch (InvalidVRMLNodeException e) {}

    // add bound property change monitor to animation bean
    animation.addPropertyChangeListener(monitor);

    // get current time
    SFTime startNow = new SFTime(System.currentTimeMillis());

    // set exposed field "startTime" in TimeSensor node and start animation now
    timeSource.setStartTime(startNow);
  }

}

Transitional VRML Beans

VRMLBeans architecture rely on communication model with VRML Browser that provides event notification mechanism for user-defined classes observing VRML node and scene events. At the time of this writing, standard defining such communication model was not yet defined.

However in order to understand the applicability of VRMLBeans architecture it is possible to begin initial development of VRMLBeans components with Transitional VRML beans.

Transitional VRML bean implementation is based on VRML scripting with Java defined in Java Script Authoring Interface. General mechanism is provided that makes Script class a VRML Event Source for other Java beans. Any bean can register as a listener for VRML events with Transitional VRML Bean.

Transitional VRML bean identifies itself as VRML event source by defining registration methods that conform to a Java Bean specific design patterns. These registration methods accept references to instances of particular VRMLEventListener interfaces.

To register for VRML Events, listener should implement distinct interfaces extending VRMLEventListener interface for each VRMLEvent type. VRMLEventListener in turn inherits from java.util.EventListener.

Beans that implement VRMLEventListener interfaces receive VRML event objects defined as follows :

public class VrmlEventObject extends java.util.EventObject;

VRML event object extends java.util.EventObject and encapsulates some scene state. VRML event object is passed as the sole argument to the event method defined in VRMLEventListener class :

  public interface VrmlListener extends java.util.EventListener {

    public void handleVrmlEvent(VrmlEventObject veo);
  }
VRMLEvent class provides "setter" and "getter" methods to access vrml.Event object which it contains as a member :

public void setVrmlEvent(vrml.Event ve)
public vrml.Event getVrmlEvent()

Class vrml.Event is defined in VRML2.0 Java Scripting Reference and in its turn provides access methods for its members :

public String getName() - returns event name string;
public double getTimeStamp() - returns the timestamp when the event has occurred;
public ConstField getValue() - returns event value;

--- 1