VRML++: Adding Classes to VRML
Stephan Diehl,
Universität des Saarlandes
Download
Examples
see also
Abstract
We present a new object-oriented language called VRML++ which
extends VRML 2.0. The new features of VRML++ are
classes and inheritance, an improved type system
and dynamic routing. As a net result we get
inclusion polymorphism and dynamic binding.
We argue, that these features are essentials of
object-oriented programming languages.
Furthermore using these new features it is possible
to define abstractions of routing structures
which we call connection classes.
VRML++ increases reuseability of specifications
while reducing run-time errors.
Finally we discuss our implementation of VRML++.
Introduction
In VRML 1.0 we can reuse nodes by instantiating them (DEF/USE)
and then applying transformations and changes of field
properties. In VRML 2.0 prototypes provide a more powerful
mechanism to define node types and create instances of
these node types. By introducing scripts, events and
routes VRML 2.0 added programming language concepts
and thus behavior to VRML scenes. We would like to
reuse and parameterize such behavior. This is possible
to some extend in VRML 2.0, but when we look at programming
languages, reuse of code was greatly simplified by the
development of object-oriented programming languages (OOPLs).
Huge libraries of classes are common to object-oriented
languages and make programming an easier task. In
software-engineering object-oriented analysis and
design is propagated as the way to manage the
development of large scale applications.
Cox (90) predicts a revolution in software industry
by reuseable, reliable, abstract software components
which can be plugged together to create new
applications. At any time a component can be
replaced by a more efficient one, which provides
the same interface.
VRML++ and Authoring Virtual Worlds
The object-oriented features of VRML++ and its
improved type system increase reuseability
of specifications and prevent run-time errors
in animated VRML scenes. VRML++ provides a
better way to structure huge libraries of objects and
behaviours.
VRML++ and Shared Virtual Worlds
We expect, that adding object-orientation to
VRML will ease the design of shared virtual worlds
in a similar way. Moreover using class libraries
can help to solve one of the main problems
in shared virtual worlds -- the bandwidth problem.
Using classes we can reduce the amount of data to
be send between browsers. Similar to the Java class library
we would have the same library of objects and behaviours
on each client. Thus messages would contain new
class definitions or instantiations of classes.
Essential Features of Object Orientation
In VRML 2.0 we can use OOPLs within a Script node.
The goal of our research is to lift object-orientiation
into VRML. To achieve this we first have to identify
key ideas of OOPLs.
The concepts of OOPLs we will deal with in this paper
are
- Objects: Objects encapsulate state
and behavior. The state is contained in
variables and the behavior in methods.
To invoke a method or to read or change the value
of a variable a message is send to an object.
Computation of an object-oriented system
consists in sending messages between
objects.
- Classes: Classes are sometimes called
object-factories. A class is a scheme, which
describes the objects of this class and is
used to create such objects. Objects are also called
instances of a class. And the process of
creating such an object is called instantiation.
- Inheritance: A new class is defined
by specifying one (single inheritance)
or more (multiple inheritance) superclasses.
The new class inherits all variables and
methods from its superclasses. In addition
it can add new variables and methods
or even override the definition of an inherited variable
or method.
- Dynamic Binding: Assume, we have
a variable obj, and it can be
inferred statically,i.e. at compile-time,
that the object contained in obj
is a member of class K. Now the
method m of obj is
invoked. Now assume, we run the
program and obj is bound to
an object of class S and
S is a subclass of K.
If static binding is used, then the
method m of K is
invoked. If we use dynamic binding
then the method m of
S is called. Thus dynamic
binding makes sure, that at run-time
the most specific method is used.
- Polymorphism: In a polymorphic
language arguments of operators and functions
can have more than one type. Cardelli and
Wegner (1985) distinguish coercion, overloading, inclusion
and parametric polymorphism. Due to inheritance, inclusion
polymorphism is common to object-oriented languages:
If an argument of a function is required to be an object
of class K, then the function also accepts objects of every
subclass of K as the value of this argument.
How object-oriented is VRML ?
In the paper
"Sony's approach to
behavior and scripting aspects of VRML: an Object-Oriented
perspective" K. Matsuda, Y. Honda and R. Lea
point out, that objects in VRML have
properties, state variables and behaviors. Using
standard terminology of the object-oriented programming
community, this can only be considered object-based.
The extension suggested by Park(97) is to
replace ROUTEs and Scripts by Eventhandlers. He calls the
resulting language OO-VRML. But his extension does
not increase object-orientation.
The work of Curtis(97) shows that there is
a need for object hierarchies or even better
class hierarchies when it comes to implementing
simulations involving behaviours in VRML.
He tries to use VRML 2.0 and Java to this end,
but because of the lack of inheritance in VRML
his implementation becomes complicated.
In our view the concepts present in VRML 2.0
correspond roughly to those of OOPLS as
follows: prototypes are classes without inheritance,
nodes are objects, events and Script nodes,
which process events are methods, fields are variables.
But VRML lacks inheritance, the essential
feature of object-orientation. Furthermore in
VRML there is no elaborate type system
and thus neither dynamic binding nor
inclusion polymorphism.
Inheritance
In this section we explain how inheritance
works in VRML++. If we define a new prototype B
to be a sub-prototype of another prototype A,
then it has all the events and fields of A.
But B can change some of these events or fields
or add new ones. We say, B inherits from A.
The problem is, that if we
program this in VRML 2.0 using prototypes,
we also have to list all fields
and events which remain unchanged.
This makes the specifications hard to
read and maintain.
To solve this problem, we extend the
syntax of VRML 2.0 and use a preprocessor,
which converts the extended syntax
into standard VRML 2.0 syntax.
Classes
The following example shows the basic idea
of how we use classes in VRML++.
Assume, we have a prototype
Robot which provides the input events
walk and jump. Now we want to define
a new prototype MyRobot which
only differs from Robot in that
it provides a different implementation of
the event walk.
CLASS Robot [eventIn SFTime walk
eventIn SFTime jump]
{ ...
Script { field SFNode self USE SELF
eventIn SFTime walker IS walk
url "vrmlscript: function walker(value) ...."
}
Script { field SFNode self USE SELF
eventIn SFTime jumper IS jump
url "vrmlscript: function jumper(value) ...."
}
}
CLASS MyRobot [eventIn SFTime walk]
EXTENDS Robot
{
Script { eventIn SFTime runner IS walk
url "vrmlscript: function runner(value) ...."
}
}
Note, that the jump event, which was not changed
is passed on to Robot.
In a class definition SELF denotes the
instance of the class, when it is instantiated.
More precisely: if the class
inherits only form SFNode, then
SELF denotes the first node in the class definition.
Otherwise, it denotes an instance
of the first superclass. A class can inherit from
other classes, from other prototypes or
from standard VRML nodes like Sphere
or Transform. The top class of the
inheritance hierarchy is always SFNode.
Multiple Inheritance
In VRML++ it is also possible for a class
to have several superclasses. In this case
every field and event is only inherited
from the first class in the list
of superclasses which supports it.
In other words the values of fields
and events are only propagated to
the first superclass providing it.
Improved Type System and Inclusion Polymorphism
The lack of a powerful type system in
VRML 2.0 can lead to many errors at
run-time. Usually these errors are
reported when a node is instantiated
which tries to add a route to an
event not supported by a node.
The situation becomes worse when
we create new nodes at run-time
or allow dynamic routing (see below).
To make sure at compile-time, that a node passed to another
node has a certain event or field, we add user defined
types to VRML. As a result we also get inclusion polymorphism.
CLASS MoveAble [eventIn SFTime move] EXTENDS SFNode
{ ...
}
CLASS MyRobot [eventIn SFTime walk
field MoveAble legs ... ] EXTENDS Robot
{ ...
ROUTE walk TO legs.move
}
In this example we require, that legs
is of type MoveAble and not just
of type SFNode as in VRML. Since
by definition all instances of MoveAble
have an event move there can not
be a run-time error like "Error: Cannot route
to a node, that does not provide EventIn move ".
Dynamic Binding / Dynamic Routing
What we would like to do, is to get a
node at instantiation- (as a value of a field) or
run-time (as a value of an event or exposedField)
and invoke one of its methods. In VRML 2.0 we
can do the following:
PROTO Example []
{
DEF EX1 node{}
DEF EX2 node{}
ROUTE EX1.out TO EX2.in
}
The following three examples show
the dynamic routing features of VRML++:
PROTO Example1 [field SFNode node1 ...
field SFNode node2 ...]
{ ...
ROUTE node1.out TO node2.in
}
PROTO Example2 [field MFNode node1 ...
field MFNode node2 ...]
{ ...
ROUTE node1.out TO node2.in
}
Such dynamic routing can be implemented by using
a Script node and the function
addRoute() of the browser
script interface (see 4.7.10 in the VRML 2.0 specification).
In combination with the type system of VRML++ we
get dynamic binding, i.e. what method (Script
implementing an eventIn) is actually called
depends on the type at run-time. For example
consider the classes Robot
and MyRobot defined above.
If we define a a class WalkRobot
as follows and instantiate it,
it is not clear until run-time whether
the function walker or
jumper is invoked:
CLASS WalkRobot [ field TimeSensor trigger NULL
field Robot robot NULL
] EXTENDS SFNode
{ ROUTE trigger.time TO robot.walk
}
If the value
of robot is an instance of
class Robot then walker
is called, if it is an instance
of MyRobot then jumper
is called.
By using the keyword UNROUTE
instead of ROUTE routes can also be dynamically
deleted. As an example an object can be statically routed
to a touch sensor. If the user clicks at it, the
object is dynamically routed to some other node and
by some other event, e.g. when clicking at another sensor node,
this route is deleted again.
Connection Classes
Using dynamic routing we can define connection classes.
A connection class abstracts a routing structure, i.e.
a connection class is a generic set of routes which
can be instantiated.
For example a class FanOut which given a TimeSensor
will propagate the event fraction_changed
to a set of other nodes, e.g. Interpolators.
CLASS FanOut [field TimeSensor trigger NULL
field MFNode targets []
] EXTENDS SFNode
{ ROUTE trigger.fraction_changed TO targets.set_fraction
}
DEF O1 OrientationInterpolator { ... }
DEF O2 OrientationInterpolator { ... }
DEF TS TimeSensor{ ... }
FanOut { trigger USE TS
targets [ USE O1 USE O2 ]
}
Another typical connection class is Filter which
routes different source nodes like sensors
to a filter, e.g. an Interpolator, and routes
the result of this filter to several target nodes.
CLASS Filter [field MFNode sources []
filter OrientationInterpolator filter NULL
field MFNode targets []
] EXTENDS SFNode
{ ROUTE source.fraction_changed TO filter.set_fraction
ROUTE filter.value_changed TO targets.set_rotation
}
The following example shows that one can use
a connection class (here: MoveBall)
to add an interface to another class.
#VRML++ draft utf8
CLASS Ball [] EXTENDS SFNode
{ Transform { children Shape { geometry Sphere { } } }
}
CLASS MoveBall[ field TimeSensor timer NULL
field PositionInterpolator interpol NULL
] EXTENDS Ball
{
ROUTE timer.fraction_changed TO interpol.set_fraction
ROUTE interpol.value_changed TO SELF.set_translation
}
DEF PI PositionInterpolator
{ key [ 0, 0.5, 1 ]
keyValue [ -1 0 0, 1 0 0, -1 0 0 ]
}
DEF TS TimeSensor { startTime 1 stopTime 0 loop TRUE }
DEF DD MoveBall { timer USE TS
interpol USE PI }
Here SELF is a special node name denoting
the current instance of this class. Instead of wrapping
the connections around the class we could write,
which is almost the expansion of the above definition
of MoveBall:
#VRML++ draft utf8
CLASS MoveBall[ field TimeSensor from NULL
field PositionInterpolator to NULL
] EXTENDS SFNode
{ DEF BALL Transform { children Shape { geometry Sphere { } } }
ROUTE from.fraction_changed TO to.set_fraction
ROUTE to.value_changed TO BALL.set_translation
}
...
Implementation
We are currently implementing a preprocessor which
translates VRML++ files into VRML 2.0 files. By using
such a preprocessor VRML++ becomes very portable and
can be used with every VRML 2.0 browser.
Currently our preprocessor is able to translate
class definitions with multiple superclasses
and dynamic routing based on SFNode/MFNode fields and events.
We are working on the implementation of
the static type checking on the basis of
the improved type system right now.
You can download
of the current version of our preprocessor.
For the executables you will also need
the initialization file
standardClasses.vpp.
The VRML 2.0 code generated by the preprocessor
was tested with the WorldView browser.
The preprocessor was written in C++
using GNUs g++, the standard template library,
bison and flex. The original VRML 2.0
parser underlying our preprocessor was implemented
by Gavin and Woods for Borland's C++ 5.0
and Microsoft's MSDEV C++ 4.0. So it should
be fairly portable. For dynamic routing we
use Script nodes with functions
in VrmlScript code.
As we only use the
browser interface, we could generate
Java source as well.
Usage
To convert a VRML++ file sample.wrl into a
VRML 2.0 file sample20.wrl you can type:
vpp sample.wrl > sample20.wrl
In this case VRMLScript is used in Script nodes. To use
JavaScript instead you have to type:
vpp -js sample.wrl > sample20.wrl
If not specified otherwise the initialization file
standardClasses.vpp is looked up in the local directory.
To use a different initialization file or put it
in a different path type:
vpp -init /usr/lib/vrml++/standardNodes.wrl sample.wrl > sample20.wrl
Future Work
To prove the claims and design goals of VRML++ in practice
we will have to write object and behaviour libraries
and combine these with authoring tools. Another interesting
project would be to integrate VRML++ into an implementation
of a shared virtual world to see, whether it can reduce the
network traffic.
Examples
References
Curtis (97)
Curtis A. Beeson,
"An Object Oriented Approach to VRML Development",
VRML'97
Park (97)
Sungwoo Park
"Object-Oriented VRML for Multi-user Environments",
VRML'97
Cardelli and Wegner (1985),
L. Cardelli and P. Wegner,
"On Understanding Types, Data Abstraction and Polymorphism",
ACM Computing Surveys, volume 17(4), 1985
Cox (90),
Brad J. Cox,
"Planning the Software Industrial Revolution:
The Impact of Object-Oriented Technologies",
IEEE Software, 1990
Sony (96),
K. Matsuda, Y. Honda and R. Lea
"Sony's approach to behavior and scripting aspects of VRML:
an Object-Oriented perspective",
http://www.csl.cony.co.jp/project/vs/proposal/behascri.html
VRML 2.0 Specification
http://vag.vrml.org/VRML2.0/FINAL/
Hartman and Wernecke (1996)
Jed Hartman and Josie Wernecke
"The VRML 2.0 Handbook - Building Moving Worlds on the Web",
Addison-Wesley, 1996